From ddfbb88cde1a3bb50647c0865f1a93fb32aa8a5c Mon Sep 17 00:00:00 2001
From: Lee Jong Mun <43285072+crwusiz@users.noreply.github.com>
Date: Mon, 10 Mar 2025 01:49:47 +0900
Subject: [PATCH 01/68] kor translation update (#34822)
---
selfdrive/ui/translations/main_ko.ts | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index 6c6c73da16..e90e5d44ed 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -315,27 +315,29 @@
openpilot learns to drive by watching humans, like you, drive.
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.
-
+ 오픈파일럿은 여러분과 같은 사람이 운전하는 모습을 보면서 운전하는 법을 배웁니다.
+
+파이어호스 모드를 사용하면 훈련 데이터 업로드를 최대화하여 오픈파일럿의 주행 모델을 개선할 수 있습니다. 더 많은 데이터는 더 큰 모델을 의미하며, 이는 더 나은 실험 모드를 의미합니다.Firehose Mode: ACTIVE
-
+ 파이어호스 모드: 활성화For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
+ 효과를 극대화하려면 기기를 차 안으로 가져가서 매주 USB-C 어댑터와 Wi-Fi에 연결하세요.<br><br>“파이어호스 모드”는 핫스팟이나 무제한 SIM 카드에 연결된 경우 운전 중에도 작동할 수 있습니다.<br><br><br><b>자주 묻는 질문</b><br><br><i>"운전 방법이나 장소가 중요한가요?</i> 아니요, 평소와 같이 운전하시면 됩니다.<br><br><i> 좋은 USB-C 어댑터는 무엇인가요?</i> 휴대폰이나 노트북 고속 충전기가 있으면 됩니다.<br><br><i>어떤 소프트웨어를 실행해도 상관없나요?</i> 예, 업스트림 Openpilot(및 특정 포크)만 트레이닝에 사용할 수 있습니다.<b>%1 %2</b> of your driving are in the training dataset so far.
-
+ <b>%1 %2</b> 의 운전이 지금까지의 훈련 데이터 세트에 포함되어 있습니다.ACTIVE
-
+ 활성화<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
-
+ <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>비활성</span>: 무제한 네트워크에 연결
@@ -993,11 +995,11 @@ This may take up to a minute.
Welcome to openpilot
-
+ 오픈 파일럿에 오신 것을 환영합니다.You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.
-
+ 오픈파일럿을 사용하려면 이용약관에 동의해야 합니다. 최신 약관은 <span style='color: #465BEA;'>https://comma.ai/terms</span> 에서 최신 약관을 읽은 후 계속하세요.
From 08e70f19ed351b74d4b5665089111e3999f1b533 Mon Sep 17 00:00:00 2001
From: Alexandre Nobuharu Sato
<66435071+AlexandreSato@users.noreply.github.com>
Date: Sun, 9 Mar 2025 13:50:22 -0300
Subject: [PATCH 02/68] Multilang: update pt-BR translations (#34821)
update pt-BR translations
---
selfdrive/ui/translations/main_pt-BR.ts | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index 16edf605ad..b48d065f11 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -315,27 +315,29 @@
openpilot learns to drive by watching humans, like you, drive.
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.
-
+ O openpilot aprende a dirigir observando humanos, como você, dirigirem.
+
+O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar os modelos de direção do openpilot. Mais dados significam modelos maiores, o que resulta em um Modo Experimental melhor.Firehose Mode: ACTIVE
-
+ Mode Firehose: ATIVOFor maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
+ Para maior eficácia, leve seu dispositivo para dentro de casa e conecte-o a um bom adaptador USB-C e Wi-Fi semanalmente.<br><br>O Modo Firehose também pode funcionar enquanto você dirige, se estiver conectado a um hotspot ou a um chip SIM com dados ilimitados.<br><br><br><b>Perguntas Frequentes</b><br><br><i>Importa como ou onde eu dirijo?</i> Não, basta dirigir normalmente<br><br><i>Qual é um bom adaptador USB-C?</i> Qualquer carregador rápido de telefone ou laptop deve ser suficiente.<br><br><i>Importa qual software eu uso?</i> Sim, apenas o openpilot oficial (e alguns forks específicos) podem ser usados para treinamento.<b>%1 %2</b> of your driving are in the training dataset so far.
-
+ <b>%1 %2</b> da sua direção já está no conjunto de dados de treinamento até agora.ACTIVE
-
+ ATIVO<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
-
+ <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INATIVO</span>: conecte-se a uma rede sem restrição de dados
@@ -997,11 +999,11 @@ Isso pode levar até um minuto.
Welcome to openpilot
-
+ Bem vindo ao openpilotYou must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.
-
+ Você deve aceitar os Termos e Condições para usar o openpilot. Leia os termos mais recentes em <span style='color: #465BEA;'>https://comma.ai/terms</span> antes de continuar.
From 8edaaeae507dc29c0218ee50b9ee0dc288332d44 Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Sun, 9 Mar 2025 10:07:14 -0700
Subject: [PATCH 03/68] one more firehose faq
---
selfdrive/ui/qt/offroad/firehose.cc | 1 +
1 file changed, 1 insertion(+)
diff --git a/selfdrive/ui/qt/offroad/firehose.cc b/selfdrive/ui/qt/offroad/firehose.cc
index ab1d0198c7..fe42178a4c 100644
--- a/selfdrive/ui/qt/offroad/firehose.cc
+++ b/selfdrive/ui/qt/offroad/firehose.cc
@@ -69,6 +69,7 @@ FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent)
"
"
"Frequently Asked Questions
"
"Does it matter how or where I drive? Nope, just drive as you normally would.
"
+ "Do all of my segments get pulled in Firehose Mode? No, we selectively pull a subset of your segments.
"
"What's a good USB-C adapter? Any fast phone or laptop charger should be fine.
"
"Does it matter which software I run? Yes, only upstream openpilot (and particular forks) are able to be used for training."
));
From 06d6f254df1dd75463d97ff04489032568c5397b Mon Sep 17 00:00:00 2001
From: Dean Lee
Date: Mon, 10 Mar 2025 09:10:30 +0800
Subject: [PATCH 04/68] radard: use dict comprehension for ar_pts construction
(#34824)
use dict comprehension
---
selfdrive/controls/radard.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py
index 80ce10380a..0267166795 100755
--- a/selfdrive/controls/radard.py
+++ b/selfdrive/controls/radard.py
@@ -211,9 +211,7 @@ class RadarD:
self.v_ego_hist.append(self.v_ego)
self.last_v_ego_frame = sm.recv_frame['carState']
- ar_pts = {}
- for pt in rr.points:
- ar_pts[pt.trackId] = [pt.dRel, pt.yRel, pt.vRel, pt.measured]
+ ar_pts = {pt.trackId: [pt.dRel, pt.yRel, pt.vRel, pt.measured] for pt in rr.points}
# *** remove missing points from meta data ***
for ids in list(self.tracks.keys()):
From 2bfbb181e58690b807468ae259fb511f94e0d1e4 Mon Sep 17 00:00:00 2001
From: Dean Lee
Date: Mon, 10 Mar 2025 09:48:01 +0800
Subject: [PATCH 05/68] python ui: add Keyboard Implementation (#34827)
implement keyboard
---
system/ui/widgets/keyboard.py | 104 ++++++++++++++++++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 system/ui/widgets/keyboard.py
diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py
new file mode 100644
index 0000000000..4d1ad1b2cd
--- /dev/null
+++ b/system/ui/widgets/keyboard.py
@@ -0,0 +1,104 @@
+import pyray as rl
+from openpilot.system.ui.lib.button import gui_button
+from openpilot.system.ui.lib.label import gui_label
+
+# Constants for special keys
+BACKSPACE_KEY = "<-"
+ENTER_KEY = "Enter"
+SPACE_KEY = " "
+SHIFT_KEY = "↑"
+SHIFT_DOWN_KEY = "↓"
+NUMERIC_KEY = "123"
+SYMBOL_KEY = "#+="
+ABC_KEY = "ABC"
+
+# Define keyboard layouts as a dictionary for easier access
+keyboard_layouts = {
+ "lowercase": [
+ ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
+ ["a", "s", "d", "f", "g", "h", "j", "k", "l"],
+ [SHIFT_KEY, "z", "x", "c", "v", "b", "n", "m", BACKSPACE_KEY],
+ [NUMERIC_KEY, "/", "-", SPACE_KEY, ".", ENTER_KEY],
+ ],
+ "uppercase": [
+ ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
+ ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
+ [SHIFT_DOWN_KEY, "Z", "X", "C", "V", "B", "N", "M", BACKSPACE_KEY],
+ [NUMERIC_KEY, "/", "-", SPACE_KEY, ".", ENTER_KEY],
+ ],
+ "numbers": [
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
+ ["-", "/", ":", ";", "(", ")", "$", "&", "@", "\""],
+ [SYMBOL_KEY, ".", ",", "?", "!", "`", BACKSPACE_KEY],
+ [ABC_KEY, SPACE_KEY, ".", ENTER_KEY],
+ ],
+ "specials": [
+ ["[", "]", "{", "}", "#", "%", "^", "*", "+", "="],
+ ["_", "\\", "|", "~", "<", ">", "€", "£", "¥", "•"],
+ [NUMERIC_KEY, ".", ",", "?", "!", "'", BACKSPACE_KEY],
+ [ABC_KEY, SPACE_KEY, ".", ENTER_KEY],
+ ],
+}
+
+
+class Keyboard:
+ def __init__(self, max_text_size: int = 255):
+ self._layout = keyboard_layouts["lowercase"]
+ self._input_text = ""
+ self._max_text_size = max_text_size
+
+ @property
+ def text(self) -> str:
+ return self._input_text
+
+ def clear(self):
+ self._input_text = ""
+
+ def render(self, rect, title, sub_title):
+ gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90)
+ gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, rl.GRAY)
+ if gui_button(rl.Rectangle(rect.x + rect.width - 300, rect.y, 300, 100), "Cancel"):
+ return -1
+
+ # Text box for input
+ rl.gui_text_box(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100), self._input_text, self._max_text_size, True)
+
+ h_space, v_space = 15, 15
+ row_y_start = rect.y + 300 # Starting Y position for the first row
+ key_height = (rect.height - 300 - 3 * v_space) / 4
+ key_max_width = (rect.width - (len(self._layout[2]) - 1) * h_space) / len(self._layout[2])
+
+ # Iterate over the rows of keys in the current layout
+ for row, keys in enumerate(self._layout):
+ key_width = min((rect.width - (180 if row == 1 else 0) - h_space * (len(keys) - 1)) / len(keys), key_max_width)
+ start_x = rect.x + (90 if row == 1 else 0)
+
+ for i, key in enumerate(keys):
+ if i > 0:
+ start_x += h_space
+
+ new_width = (key_width * 3 + h_space * 2) if key == SPACE_KEY else (key_width * 2 + h_space if key == ENTER_KEY else key_width)
+ key_rect = rl.Rectangle(start_x, row_y_start + row * (key_height + v_space), new_width, key_height)
+ start_x += new_width
+
+ if gui_button(key_rect, key):
+ if key == ENTER_KEY:
+ return 1
+ else:
+ self.handle_key_press(key)
+
+ return 0
+
+ def handle_key_press(self, key):
+ if key in (SHIFT_DOWN_KEY, ABC_KEY):
+ self._layout = keyboard_layouts["lowercase"]
+ elif key == SHIFT_KEY:
+ self._layout = keyboard_layouts["uppercase"]
+ elif key == NUMERIC_KEY:
+ self._layout = keyboard_layouts["numbers"]
+ elif key == SYMBOL_KEY:
+ self._layout = keyboard_layouts["specials"]
+ elif key == BACKSPACE_KEY and len(self._input_text) > 0:
+ self._input_text = self._input_text[:-1]
+ elif key != BACKSPACE_KEY and len(self._input_text) < self._max_text_size:
+ self._input_text += key
From 5574b91d17df1cb667534405c1baf2bcd335b997 Mon Sep 17 00:00:00 2001
From: Dean Lee
Date: Mon, 10 Mar 2025 23:28:32 +0800
Subject: [PATCH 06/68] python ui: add confirm dialog implementation (#34828)
add confirm dialog implementation
---
system/ui/widgets/confirm_dialog.py | 62 +++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 system/ui/widgets/confirm_dialog.py
diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py
new file mode 100644
index 0000000000..e5ca002ecf
--- /dev/null
+++ b/system/ui/widgets/confirm_dialog.py
@@ -0,0 +1,62 @@
+import pyray as rl
+from openpilot.system.ui.lib.button import gui_button, ButtonStyle
+from openpilot.system.ui.lib.label import gui_text_box
+
+# Constants for dialog dimensions and styling
+DIALOG_WIDTH = 1520
+DIALOG_HEIGHT = 600
+BUTTON_HEIGHT = 160
+MARGIN = 50
+TEXT_AREA_HEIGHT_REDUCTION = 200
+BACKGROUND_COLOR = rl.Color(27, 27, 27, 255)
+
+
+def confirm_dialog(rect: rl.Rectangle, message: str, confirm_text: str, cancel_text: str = "Cancel") -> int:
+ # Calculate dialog position and size, centered within the parent rectangle
+ dialog_x = rect.x + (rect.width - DIALOG_WIDTH) / 2
+ dialog_y = rect.y + (rect.height - DIALOG_HEIGHT) / 2
+ dialog_rect = rl.Rectangle(dialog_x, dialog_y, DIALOG_WIDTH, DIALOG_HEIGHT)
+
+ # Calculate button positions at the bottom of the dialog
+ bottom = dialog_rect.y + dialog_rect.height
+ button_width = (dialog_rect.width - 3 * MARGIN) // 2
+ no_button_x = dialog_rect.x + MARGIN
+ yes_button_x = dialog_rect.x + dialog_rect.width - button_width - MARGIN
+ button_y = bottom - BUTTON_HEIGHT - MARGIN
+ no_button = rl.Rectangle(no_button_x, button_y, button_width, BUTTON_HEIGHT)
+ yes_button = rl.Rectangle(yes_button_x, button_y, button_width, BUTTON_HEIGHT)
+
+ # Draw the dialog background
+ rl.draw_rectangle(
+ int(dialog_rect.x),
+ int(dialog_rect.y),
+ int(dialog_rect.width),
+ int(dialog_rect.height),
+ BACKGROUND_COLOR,
+ )
+
+ # Draw the message in the dialog, centered
+ text_rect = rl.Rectangle(dialog_rect.x, dialog_rect.y, dialog_rect.width, dialog_rect.height - TEXT_AREA_HEIGHT_REDUCTION)
+ gui_text_box(
+ text_rect,
+ message,
+ alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
+ alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE,
+ )
+
+ # Initialize result; -1 means no action taken yet
+ result = -1
+
+ # Check for keyboard input for accessibility
+ if rl.is_key_pressed(rl.KeyboardKey.KEY_ENTER):
+ result = 1 # Confirm
+ elif rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
+ result = 0 # Cancel
+
+ # Check for button clicks
+ if gui_button(yes_button, confirm_text, button_style=ButtonStyle.PRIMARY):
+ result = 1 # Confirm
+ if gui_button(no_button, cancel_text):
+ result = 0 # Cancel
+
+ return result
From f870e48b3a13e72b5da20beec56c6be171f1618b Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Mon, 10 Mar 2025 09:43:46 -0700
Subject: [PATCH 07/68] BASEDIR -> DIR
---
launch_chffrplus.sh | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh
index 66f57c2cd1..d4689aae53 100755
--- a/launch_chffrplus.sh
+++ b/launch_chffrplus.sh
@@ -34,25 +34,25 @@ function launch {
# Check to see if there's a valid overlay-based update available. Conditions
# are as follows:
#
- # 1. The BASEDIR init file has to exist, with a newer modtime than anything in
- # the BASEDIR Git repo. This checks for local development work or the user
+ # 1. The DIR init file has to exist, with a newer modtime than anything in
+ # the DIR Git repo. This checks for local development work or the user
# switching branches/forks, which should not be overwritten.
# 2. The FINALIZED consistent file has to exist, indicating there's an update
# that completed successfully and synced to disk.
- if [ -f "${BASEDIR}/.overlay_init" ]; then
- find ${BASEDIR}/.git -newer ${BASEDIR}/.overlay_init | grep -q '.' 2> /dev/null
+ if [ -f "${DIR}/.overlay_init" ]; then
+ find ${DIR}/.git -newer ${DIR}/.overlay_init | grep -q '.' 2> /dev/null
if [ $? -eq 0 ]; then
- echo "${BASEDIR} has been modified, skipping overlay update installation"
+ echo "${DIR} has been modified, skipping overlay update installation"
else
if [ -f "${STAGING_ROOT}/finalized/.overlay_consistent" ]; then
if [ ! -d /data/safe_staging/old_openpilot ]; then
echo "Valid overlay update found, installing"
LAUNCHER_LOCATION="${BASH_SOURCE[0]}"
- mv $BASEDIR /data/safe_staging/old_openpilot
- mv "${STAGING_ROOT}/finalized" $BASEDIR
- cd $BASEDIR
+ mv $DIR /data/safe_staging/old_openpilot
+ mv "${STAGING_ROOT}/finalized" $DIR
+ cd $DIR
echo "Restarting launch script ${LAUNCHER_LOCATION}"
unset AGNOS_VERSION
From 5b6c7ddc9370c22a02bdfd67a622f7e637bf5030 Mon Sep 17 00:00:00 2001
From: commaci-public <60409688+commaci-public@users.noreply.github.com>
Date: Mon, 10 Mar 2025 10:57:06 -0700
Subject: [PATCH 08/68] [bot] Update Python packages (#34830)
Update Python packages
Co-authored-by: Vehicle Researcher
---
opendbc_repo | 2 +-
uv.lock | 20 ++++++++++----------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/opendbc_repo b/opendbc_repo
index 0efdb71b8f..75d4e0eb5c 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit 0efdb71b8f2440ee51a806d057f20a553b849a3a
+Subproject commit 75d4e0eb5c8fa961a665ec020eb816a50eed231c
diff --git a/uv.lock b/uv.lock
index 8da47bf004..8b0c751e17 100644
--- a/uv.lock
+++ b/uv.lock
@@ -4455,15 +4455,15 @@ sdist = { url = "https://files.pythonhosted.org/packages/79/0c/c16bc93ac2755bac0
[[package]]
name = "pywin32"
-version = "308"
+version = "309"
source = { registry = "https://pypi.org/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/eb/e2/02652007469263fe1466e98439831d65d4ca80ea1a2df29abecedf7e47b7/pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a", size = 5928156 },
- { url = "https://files.pythonhosted.org/packages/48/ef/f4fb45e2196bc7ffe09cad0542d9aff66b0e33f6c0954b43e49c33cad7bd/pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b", size = 6559559 },
- { url = "https://files.pythonhosted.org/packages/79/ef/68bb6aa865c5c9b11a35771329e95917b5559845bd75b65549407f9fc6b4/pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6", size = 7972495 },
- { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 },
- { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 },
- { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 },
+ { url = "https://files.pythonhosted.org/packages/05/54/6409b1d98f2b8fed3bc2cc854859e48ae4a2dd956176664e38ee49c50a4c/pywin32-309-cp311-cp311-win32.whl", hash = "sha256:d5df6faa32b868baf9ade7c9b25337fa5eced28eb1ab89082c8dae9c48e4cd51", size = 8779225 },
+ { url = "https://files.pythonhosted.org/packages/6a/f0/ae8ddb56771093dd2905baa852958fd65d42a8972aeefcf13578dfae69f4/pywin32-309-cp311-cp311-win_amd64.whl", hash = "sha256:e7ec2cef6df0926f8a89fd64959eba591a1eeaf0258082065f7bdbe2121228db", size = 9514129 },
+ { url = "https://files.pythonhosted.org/packages/7a/4b/1f5e377a04448cf410e13040bc0e4c408bfa0a65705cabf96904178f18df/pywin32-309-cp311-cp311-win_arm64.whl", hash = "sha256:54ee296f6d11db1627216e9b4d4c3231856ed2d9f194c82f26c6cb5650163f4c", size = 8450450 },
+ { url = "https://files.pythonhosted.org/packages/20/2c/b0240b14ff3dba7a8a7122dc9bbf7fbd21ed0e8b57c109633675b5d1761f/pywin32-309-cp312-cp312-win32.whl", hash = "sha256:de9acacced5fa82f557298b1fed5fef7bd49beee04190f68e1e4783fbdc19926", size = 8790648 },
+ { url = "https://files.pythonhosted.org/packages/dd/11/c36884c732e2b3397deee808b5dac1abbb170ec37f94c6606fcb04d1e9d7/pywin32-309-cp312-cp312-win_amd64.whl", hash = "sha256:6ff9eebb77ffc3d59812c68db33c0a7817e1337e3537859499bd27586330fc9e", size = 9497399 },
+ { url = "https://files.pythonhosted.org/packages/18/9f/79703972958f8ba3fd38bc9bf1165810bd75124982419b0cc433a2894d46/pywin32-309-cp312-cp312-win_arm64.whl", hash = "sha256:619f3e0a327b5418d833f44dc87859523635cf339f86071cc65a13c07be3110f", size = 8454122 },
]
[[package]]
@@ -4757,11 +4757,11 @@ wheels = [
[[package]]
name = "setuptools"
-version = "75.8.2"
+version = "76.0.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d1/53/43d99d7687e8cdef5ab5f9ec5eaf2c0423c2b35133a2b7e7bc276fc32b21/setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2", size = 1344083 }
+sdist = { url = "https://files.pythonhosted.org/packages/32/d2/7b171caf085ba0d40d8391f54e1c75a1cda9255f542becf84575cfd8a732/setuptools-76.0.0.tar.gz", hash = "sha256:43b4ee60e10b0d0ee98ad11918e114c70701bc6051662a9a675a0496c1a158f4", size = 1349387 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a9/38/7d7362e031bd6dc121e5081d8cb6aa6f6fedf2b67bf889962134c6da4705/setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f", size = 1229385 },
+ { url = "https://files.pythonhosted.org/packages/37/66/d2d7e6ad554f3a7c7297c3f8ef6e22643ad3d35ef5c63bf488bc89f32f31/setuptools-76.0.0-py3-none-any.whl", hash = "sha256:199466a166ff664970d0ee145839f5582cb9bca7a0a3a2e795b6a9cb2308e9c6", size = 1236106 },
]
[[package]]
From c749e90846d40cbe2a3316586ba95fe2322a6a14 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 10 Mar 2025 10:57:25 -0700
Subject: [PATCH 09/68] [bot] Update translations (#34829)
Update translations
Co-authored-by: Vehicle Researcher
---
selfdrive/ui/translations/main_ar.ts | 8 ++++----
selfdrive/ui/translations/main_de.ts | 8 ++++----
selfdrive/ui/translations/main_es.ts | 8 ++++----
selfdrive/ui/translations/main_fr.ts | 8 ++++----
selfdrive/ui/translations/main_ja.ts | 8 ++++----
selfdrive/ui/translations/main_ko.ts | 8 ++++----
selfdrive/ui/translations/main_pt-BR.ts | 8 ++++----
selfdrive/ui/translations/main_th.ts | 8 ++++----
selfdrive/ui/translations/main_tr.ts | 8 ++++----
selfdrive/ui/translations/main_zh-CHS.ts | 8 ++++----
selfdrive/ui/translations/main_zh-CHT.ts | 8 ++++----
11 files changed, 44 insertions(+), 44 deletions(-)
diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts
index 6471159616..bb5cec335c 100644
--- a/selfdrive/ui/translations/main_ar.ts
+++ b/selfdrive/ui/translations/main_ar.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts
index bdda0484d4..34aadcc733 100644
--- a/selfdrive/ui/translations/main_de.ts
+++ b/selfdrive/ui/translations/main_de.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts
index 8168e126e3..9c7510856f 100644
--- a/selfdrive/ui/translations/main_es.ts
+++ b/selfdrive/ui/translations/main_es.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts
index 3fe002e03d..08b310dcd9 100644
--- a/selfdrive/ui/translations/main_fr.ts
+++ b/selfdrive/ui/translations/main_fr.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts
index d242875937..591594edb4 100644
--- a/selfdrive/ui/translations/main_ja.ts
+++ b/selfdrive/ui/translations/main_ja.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index e90e5d44ed..079c5849c1 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -323,10 +323,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE파이어호스 모드: 활성화
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
- 효과를 극대화하려면 기기를 차 안으로 가져가서 매주 USB-C 어댑터와 Wi-Fi에 연결하세요.<br><br>“파이어호스 모드”는 핫스팟이나 무제한 SIM 카드에 연결된 경우 운전 중에도 작동할 수 있습니다.<br><br><br><b>자주 묻는 질문</b><br><br><i>"운전 방법이나 장소가 중요한가요?</i> 아니요, 평소와 같이 운전하시면 됩니다.<br><br><i> 좋은 USB-C 어댑터는 무엇인가요?</i> 휴대폰이나 노트북 고속 충전기가 있으면 됩니다.<br><br><i>어떤 소프트웨어를 실행해도 상관없나요?</i> 예, 업스트림 Openpilot(및 특정 포크)만 트레이닝에 사용할 수 있습니다.
- <b>%1 %2</b> of your driving are in the training dataset so far.<b>%1 %2</b> 의 운전이 지금까지의 훈련 데이터 세트에 포함되어 있습니다.
@@ -339,6 +335,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>비활성</span>: 무제한 네트워크에 연결
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index b48d065f11..7ddd433bf4 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -323,10 +323,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar
Firehose Mode: ACTIVEMode Firehose: ATIVO
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
- Para maior eficácia, leve seu dispositivo para dentro de casa e conecte-o a um bom adaptador USB-C e Wi-Fi semanalmente.<br><br>O Modo Firehose também pode funcionar enquanto você dirige, se estiver conectado a um hotspot ou a um chip SIM com dados ilimitados.<br><br><br><b>Perguntas Frequentes</b><br><br><i>Importa como ou onde eu dirijo?</i> Não, basta dirigir normalmente<br><br><i>Qual é um bom adaptador USB-C?</i> Qualquer carregador rápido de telefone ou laptop deve ser suficiente.<br><br><i>Importa qual software eu uso?</i> Sim, apenas o openpilot oficial (e alguns forks específicos) podem ser usados para treinamento.
- <b>%1 %2</b> of your driving are in the training dataset so far.<b>%1 %2</b> da sua direção já está no conjunto de dados de treinamento até agora.
@@ -339,6 +335,10 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INATIVO</span>: conecte-se a uma rede sem restrição de dados
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts
index fc5e690ce0..e041d799ef 100644
--- a/selfdrive/ui/translations/main_th.ts
+++ b/selfdrive/ui/translations/main_th.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts
index 8484f23160..1147ff0b94 100644
--- a/selfdrive/ui/translations/main_tr.ts
+++ b/selfdrive/ui/translations/main_tr.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index 69a2c0c594..91839fd68a 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index 6d50bf92b8..29cc095698 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
@@ -337,6 +333,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
+
+ For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ HudRenderer
From 58e5b31ccae4659bd3d2162b1fb1063046617c67 Mon Sep 17 00:00:00 2001
From: Alexandre Nobuharu Sato
<66435071+AlexandreSato@users.noreply.github.com>
Date: Mon, 10 Mar 2025 17:56:21 -0300
Subject: [PATCH 10/68] Multilang: update pt-BR translations (#34832)
---
selfdrive/ui/translations/main_pt-BR.ts | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index 7ddd433bf4..fb67a5d9e1 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -309,7 +309,7 @@
FirehosePanel🔥 Firehose Mode 🔥
- 🔥 Mode Firehose 🔥
+ 🔥 Modo Firehose 🔥openpilot learns to drive by watching humans, like you, drive.
@@ -321,7 +321,7 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar
Firehose Mode: ACTIVE
- Mode Firehose: ATIVO
+ Modo Firehose: ATIVO<b>%1 %2</b> of your driving are in the training dataset so far.
@@ -333,11 +333,11 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar
<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
- <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INATIVO</span>: conecte-se a uma rede sem restrição de dados
+ <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INATIVO</span>: conecte-se a uma rede sem restrição <br> de dadosFor maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
+ Para maior eficácia, leve seu dispositivo para dentro de casa e conecte-o a um bom adaptador USB-C e Wi-Fi semanalmente.<br><br>O Modo Firehose também pode funcionar enquanto você dirige, se estiver conectado a um hotspot ou a um chip SIM com dados ilimitados.<br><br><br><b>Perguntas Frequentes</b><br><br><i>Importa como ou onde eu dirijo?</i> Não, basta dirigir normalmente.<br><br><i>Todos os meus segmentos são enviados no Modo Firehose?</i> Não, selecionamos apenas um subconjunto dos seus segmentos.<br><br><i>Qual é um bom adaptador USB-C?</i> Qualquer carregador rápido de telefone ou laptop deve ser suficiente.<br><br><i>Importa qual software eu uso?</i> Sim, apenas o openpilot oficial (e alguns forks específicos) podem ser usados para treinamento.
@@ -1164,7 +1164,7 @@ Isso pode levar até um minuto.
<span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span>
- <span style='font-family: "Noto Color Emoji";'>🔥</span> Mode Firehose <span style='font-family: Noto Color Emoji;'>🔥</span>
+ <span style='font-family: "Noto Color Emoji";'>🔥</span> Modo Firehose <span style='font-family: Noto Color Emoji;'>🔥</span>
From 1c678c910e5c25f55cecadcf0991b242a7e32358 Mon Sep 17 00:00:00 2001
From: ZwX1616
Date: Mon, 10 Mar 2025 16:01:03 -0700
Subject: [PATCH 11/68] camerad: fix AE oscillation (#34833)
* flooring is fine
* .
* better solution
---------
Co-authored-by: Comma Device
---
system/camerad/cameras/camera_qcom2.cc | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc
index f050070d63..18a42f05d8 100644
--- a/system/camerad/cameras/camera_qcom2.cc
+++ b/system/camerad/cameras/camera_qcom2.cc
@@ -141,7 +141,8 @@ void CameraState::set_camera_exposure(float grey_frac) {
// TODO: Lower latency to 2 frames, by using the histogram outputted by the sensor we can do AE before the debayering is complete
const auto &sensor = camera.sensor;
- const float cur_ev_ = cur_ev[camera.buf.cur_frame_data.frame_id % 3] * sensor->ev_scale;
+ // Offset idx by one to not get stuck in self loop
+ const float cur_ev_ = cur_ev[(camera.buf.cur_frame_data.frame_id - 1) % 3] * sensor->ev_scale;
// Scale target grey between min and 0.4 depending on lighting conditions
float new_target_grey = std::clamp(0.4 - 0.3 * log2(1.0 + sensor->target_grey_factor*cur_ev_) / log2(6000.0), target_grey_minimums[camera.cc.camera_num], 0.4);
From dc9b5b7c25f67257b6c2826316aaea075d654985 Mon Sep 17 00:00:00 2001
From: Lee Jong Mun <43285072+crwusiz@users.noreply.github.com>
Date: Tue, 11 Mar 2025 09:29:55 +0900
Subject: [PATCH 12/68] kor translation update (#34838)
---
selfdrive/ui/translations/main_ko.ts | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index 079c5849c1..5b9024bfd3 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -129,11 +129,11 @@
WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).
- 경고: openpilot 가감속 제어 알파 기능으로 차량의 자동긴급제동(AEB)을 비활성화합니다.
+ 경고: openpilot 가감속 제어는 알파 기능으로 차량의 자동긴급제동(AEB)기능이 작동하지 않습니다.On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.
- 이 차량은 openpilot 가감속 제어 대신 기본적으로 차량의 ACC로 가감속을 제어합니다. openpilot의 가감속 제어로 전환하려면 이 기능을 활성화하세요. openpilot 가감속 제어 알파를 활성화하는 경우 실험 모드 활성화를 권장합니다.
+ 이 차량에서 openpilot은 openpilot 가감속 제어 대신 기본적으로 차량의 ACC로 가감속을 제어합니다. openpilot 가감속 제어로 전환하려면 이 기능을 활성화하세요. openpilot 가감속 제어 알파 기능을 활성화하는 경우 실험 모드 활성화를 권장합니다.Enable ADB
@@ -325,7 +325,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<b>%1 %2</b> of your driving are in the training dataset so far.
- <b>%1 %2</b> 의 운전이 지금까지의 훈련 데이터 세트에 포함되어 있습니다.
+ <b>%1 %2</b> 의 운전이 지금까지의 학습 데이터셋에 포함되어 있습니다.ACTIVE
@@ -337,7 +337,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
+ 최대한의 효과를 얻으려면 매주 장치를 실내로 가져와 좋은 USB-C 케이블에 연결하고 Wi-Fi에 연결하세요.<br><br>파이어호스 모드는 핫스팟 또는 무제한 SIM 카드에 연결된 경우에는 운전 중에도 작동할 수 있습니다.<br><br><br><b>자주 묻는 질문</b><br><br><i>운전 방법이나 장소가 중요한가요?</i> 아니요, 평소처럼 운전하시면 됩니다.<br><br><i>파이어호스 모드에서 제 모든 구간을 가져오나요?.<br><br><i> 아니요, 저희는 여러분의 구간 중 일부를 선별적으로 가져옵니다.<br><br><i>좋은 USB-C 케이블은 무엇인가요?</i> 휴대폰이나 노트북 고속 충전기가 있으면 됩니다.<br><br><i>어떤 소프트웨어를 실행하는지가 중요한가요?</i> 예, 오직 공식 openpilot의 특정 포크만 트레이닝에 사용할 수 있습니다.
@@ -443,7 +443,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai.
- openpilot이 차량을 식별할 수 없었습니다. 지원되지 않는 차량이거나 ECU가 인식되지 않습니다. 해당 차량에 맞는 펌웨어 버전을 추가하려면 PR을 제출하세요. 도움이 필요하시면 discord.comma.ai에 가입하세요.
+ openpilot이 차량을 식별할 수 없습니다. 지원되지 않는 차량이거나 ECU가 인식되지 않습니다. 해당 차량에 맞는 펌웨어 버전을 추가하려면 PR을 제출하세요. 도움이 필요하시면 discord.comma.ai에 가입하세요.openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield.
@@ -451,7 +451,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Device temperature too high. System cooling down before starting. Current internal component temperature: %1
- 장치 온도가 너무 높습니다. 시작하기 전에 온도를 낮춰주세요. 현재 내부 부품 온도: %1
+ 장치의 온도가 너무 높습니다. 시작 전에 온도를 낮춰주세요. 현재 내부 구성 요소 온도: %1
@@ -1102,7 +1102,7 @@ This may take up to a minute.
The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner.
- 운전 시각화는 일부 회전을 더 잘 보여주기 위해 저속에서 도로를 향한 광각 카메라로 전환됩니다. 실험 모드 로고도 우측 상단에 표시됩니다.
+ 운전 시각화는 일부 회전을 더 잘 보여주기 위해 저속에서 도로를 향한 광각 카메라로 전환됩니다. 우측 상단에 실험 모드 로고가 표시됩니다.Always-On Driver Monitoring
@@ -1110,7 +1110,7 @@ This may take up to a minute.
Enable driver monitoring even when openpilot is not engaged.
- Openpilot이 활성화되지 않은 경우에도 드라이버 모니터링을 활성화합니다.
+ openpilot이 활성화되지 않은 경우에도 드라이버 모니터링을 활성화합니다.
From b33342c18fcff8a82b6032b3ab3b3b31a2b99e5f Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Tue, 11 Mar 2025 10:22:49 -0700
Subject: [PATCH 13/68] loggerd: limit encoded queue size (#34837)
* loggerd: limit encoded queue size
* less spammy
---
system/loggerd/loggerd.cc | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/system/loggerd/loggerd.cc b/system/loggerd/loggerd.cc
index ea0178fe80..953ae1df32 100644
--- a/system/loggerd/loggerd.cc
+++ b/system/loggerd/loggerd.cc
@@ -165,8 +165,15 @@ int handle_encoder_msg(LoggerdState *s, Message *msg, std::string &name, struct
s->logger.segment(), offset_segment_num,
s->ready_to_rotate.load(), s->max_waiting, name.c_str());
}
- // queue up all the new segment messages, they go in after the rotate
- re.q.push_back(msg);
+
+ // TODO: define this behavior, but for now don't leak
+ if (re.q.size() > MAIN_FPS*10) {
+ LOGE_100("%s: dropping frame, queue is too large", name.c_str());
+ delete msg;
+ } else {
+ // queue up all the new segment messages, they go in after the rotate
+ re.q.push_back(msg);
+ }
} else {
LOGE("%s: encoderd packet has a older segment!!! idx.getSegmentNum():%d s->logger.segment():%d re.encoderd_segment_offset:%d",
name.c_str(), idx.getSegmentNum(), s->logger.segment(), re.encoderd_segment_offset);
From 8c359892be0638db26d9de732ce79b5f58fcb0d8 Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Tue, 11 Mar 2025 11:02:02 -0700
Subject: [PATCH 14/68] camerad: clear request queue on exit (#34842)
* camerad: doesn't need rt
* clear q
---
system/camerad/cameras/spectra.cc | 2 ++
system/camerad/main.cc | 5 ++---
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc
index 713d5ba1cc..1bd6f00352 100644
--- a/system/camerad/cameras/spectra.cc
+++ b/system/camerad/cameras/spectra.cc
@@ -1258,6 +1258,8 @@ void SpectraCamera::camera_close() {
LOG("-- Stop devices %d", cc.camera_num);
if (enabled) {
+ clear_req_queue();
+
// ret = device_control(sensor_fd, CAM_STOP_DEV, session_handle, sensor_dev_handle);
// LOGD("stop sensor: %d", ret);
int ret = device_control(m->isp_fd, CAM_STOP_DEV, session_handle, isp_dev_handle);
diff --git a/system/camerad/main.cc b/system/camerad/main.cc
index d55bd495ad..05fdef1a65 100644
--- a/system/camerad/main.cc
+++ b/system/camerad/main.cc
@@ -6,9 +6,8 @@
#include "common/util.h"
int main(int argc, char *argv[]) {
- int ret = util::set_realtime_priority(53);
- assert(ret == 0);
- ret = util::set_core_affinity({6});
+ // doesn't need RT priority since we're using isolcpus
+ int ret = util::set_core_affinity({6});
assert(ret == 0 || Params().getBool("IsOffroad")); // failure ok while offroad due to offlining cores
camerad_thread();
From 4d86e0b039a0c9b53445ae982ce4825f5feb267b Mon Sep 17 00:00:00 2001
From: Dean Lee
Date: Wed, 12 Mar 2025 04:57:20 +0800
Subject: [PATCH 15/68] pyproject.toml: add pygame and dbus-next dependencies
(#34826)
add pygame and dbus-next dependencies to pyproject.toml
---
pyproject.toml | 2 +
uv.lock | 1135 +++++++++++++++++++++++++-----------------------
2 files changed, 587 insertions(+), 550 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 58e714e3a6..1fb751e2d5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -99,11 +99,13 @@ dev = [
"av",
"azure-identity",
"azure-storage-blob",
+ "dbus-next",
"dictdiffer",
"lru-dict",
"matplotlib",
"parameterized >=0.8, <0.9",
"pyautogui",
+ "pygame",
"pyopencl; platform_machine != 'aarch64'", # broken on arm64
"pytools < 2024.1.11; platform_machine != 'aarch64'", # pyopencl use a broken version
"pywinctl",
diff --git a/uv.lock b/uv.lock
index 8b0c751e17..fe95157eb2 100644
--- a/uv.lock
+++ b/uv.lock
@@ -466,6 +466,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/27/6b/7c87867d255cbce8167ed99fc65635e9395d2af0f0c915428f5b17ec412d/Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806", size = 1171640 },
]
+[[package]]
+name = "dbus-next"
+version = "0.2.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885 },
+]
+
[[package]]
name = "dictdiffer"
version = "0.9.0"
@@ -490,7 +499,7 @@ version = "0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "python-xlib", marker = "sys_platform == 'linux'" },
- { name = "typing-extensions" },
+ { name = "typing-extensions", marker = "sys_platform != 'darwin'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/2f/3a/46ca34abf0725a754bc44ef474ad34aedcc3ea23b052d97b18b76715a6a9/EWMHlib-0.2-py3-none-any.whl", hash = "sha256:f5b07d8cfd4c7734462ee744c32d490f2f3233fa7ab354240069344208d2f6f5", size = 46657 },
@@ -631,10 +640,10 @@ name = "gymnasium"
version = "1.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "cloudpickle" },
- { name = "farama-notifications" },
- { name = "numpy" },
- { name = "typing-extensions" },
+ { name = "cloudpickle", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "farama-notifications", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/90/69/70cd29e9fc4953d013b15981ee71d4c9ef4d8b2183e6ef2fe89756746dce/gymnasium-1.1.1.tar.gz", hash = "sha256:8bd9ea9bdef32c950a444ff36afc785e1d81051ec32d30435058953c20d2456d", size = 829326 }
wheels = [
@@ -945,22 +954,22 @@ name = "metadrive-simulator"
version = "0.4.2.4"
source = { url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl" }
dependencies = [
- { name = "filelock" },
- { name = "gymnasium" },
- { name = "lxml" },
- { name = "matplotlib" },
- { name = "numpy" },
- { name = "opencv-python-headless" },
- { name = "panda3d" },
- { name = "panda3d-gltf" },
- { name = "pillow" },
- { name = "progressbar" },
- { name = "psutil" },
- { name = "pygments" },
- { name = "requests" },
- { name = "shapely" },
- { name = "tqdm" },
- { name = "yapf" },
+ { name = "filelock", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "gymnasium", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "lxml", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "matplotlib", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "opencv-python-headless", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "panda3d-gltf", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "pillow", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "progressbar", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "psutil", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "pygments", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "requests", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "shapely", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "tqdm", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "yapf", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
wheels = [
{ url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl", hash = "sha256:fbf0ea9be67e65cd45d38ff930e3d49f705dd76c9ddbd1e1482e3f87b61efcef" },
@@ -1212,7 +1221,7 @@ name = "opencv-python-headless"
version = "4.11.0.86"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "numpy" },
+ { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 }
wheels = [
@@ -1269,11 +1278,13 @@ dev = [
{ name = "av" },
{ name = "azure-identity" },
{ name = "azure-storage-blob" },
+ { name = "dbus-next" },
{ name = "dictdiffer" },
{ name = "lru-dict" },
{ name = "matplotlib" },
{ name = "parameterized" },
{ name = "pyautogui" },
+ { name = "pygame" },
{ name = "pyopencl", marker = "platform_machine != 'aarch64'" },
{ name = "pyprof2calltree" },
{ name = "pytools", marker = "platform_machine != 'aarch64'" },
@@ -1324,6 +1335,7 @@ requires-dist = [
{ name = "coverage", marker = "extra == 'testing'" },
{ name = "crcmod" },
{ name = "cython" },
+ { name = "dbus-next", marker = "extra == 'dev'" },
{ name = "dictdiffer", marker = "extra == 'dev'" },
{ name = "future-fstrings" },
{ name = "hypothesis", marker = "extra == 'testing'", specifier = "==6.47.*" },
@@ -1347,6 +1359,7 @@ requires-dist = [
{ name = "pyautogui", marker = "extra == 'dev'" },
{ name = "pycapnp" },
{ name = "pycryptodome" },
+ { name = "pygame", marker = "extra == 'dev'" },
{ name = "pyjwt" },
{ name = "pyopencl", marker = "platform_machine != 'aarch64' and extra == 'dev'" },
{ name = "pyopenssl", specifier = "<24.3.0" },
@@ -1418,8 +1431,8 @@ name = "panda3d-gltf"
version = "0.13"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "panda3d" },
- { name = "panda3d-simplepbr" },
+ { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "panda3d-simplepbr", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573 }
wheels = [
@@ -1431,8 +1444,8 @@ name = "panda3d-simplepbr"
version = "0.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "panda3d" },
- { name = "typing-extensions" },
+ { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/af/505608eef09d7f9b822e69dc7631cd14102650b8fe1b6f60d9562d2788d9/panda3d-simplepbr-0.12.0.tar.gz", hash = "sha256:c71d490afeeb3a90455dcfde1d30c41f321a38742a97d18834e5c31016331ed5", size = 1929980 }
wheels = [
@@ -1730,6 +1743,28 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/25/68/7e150cba9eeffdeb3c5cecdb6896d70c8edd46ce41c0491e12fb2b2256ff/pyee-12.1.1-py3-none-any.whl", hash = "sha256:18a19c650556bb6b32b406d7f017c8f513aceed1ef7ca618fb65de7bd2d347ef", size = 15527 },
]
+[[package]]
+name = "pygame"
+version = "2.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/49/cc/08bba60f00541f62aaa252ce0cfbd60aebd04616c0b9574f755b583e45ae/pygame-2.6.1.tar.gz", hash = "sha256:56fb02ead529cee00d415c3e007f75e0780c655909aaa8e8bf616ee09c9feb1f", size = 14808125 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c4/ca/8f367cb9fe734c4f6f6400e045593beea2635cd736158f9fabf58ee14e3c/pygame-2.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:20349195326a5e82a16e351ed93465a7845a7e2a9af55b7bc1b2110ea3e344e1", size = 13113753 },
+ { url = "https://files.pythonhosted.org/packages/83/47/6edf2f890139616b3219be9cfcc8f0cb8f42eb15efd59597927e390538cb/pygame-2.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3935459109da4bb0b3901da9904f0a3e52028a3332a355d298b1673a334cf21", size = 12378146 },
+ { url = "https://files.pythonhosted.org/packages/00/9e/0d8aa8cf93db2d2ee38ebaf1c7b61d0df36ded27eb726221719c150c673d/pygame-2.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c31dbdb5d0217f32764797d21c2752e258e5fb7e895326538d82b5f75a0cd856", size = 13611760 },
+ { url = "https://files.pythonhosted.org/packages/d7/9e/d06adaa5cc65876bcd7a24f59f67e07f7e4194e6298130024ed3fb22c456/pygame-2.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:173badf82fa198e6888017bea40f511cb28e69ecdd5a72b214e81e4dcd66c3b1", size = 14298054 },
+ { url = "https://files.pythonhosted.org/packages/7a/a1/9ae2852ebd3a7cc7d9ae7ff7919ab983e4a5c1b7a14e840732f23b2b48f6/pygame-2.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce8cc108b92de9b149b344ad2e25eedbe773af0dc41dfb24d1f07f679b558c60", size = 13977107 },
+ { url = "https://files.pythonhosted.org/packages/31/df/6788fd2e9a864d0496a77670e44a7c012184b7a5382866ab0e60c55c0f28/pygame-2.6.1-cp311-cp311-win32.whl", hash = "sha256:811e7b925146d8149d79193652cbb83e0eca0aae66476b1cb310f0f4226b8b5c", size = 10250863 },
+ { url = "https://files.pythonhosted.org/packages/d2/55/ca3eb851aeef4f6f2e98a360c201f0d00bd1ba2eb98e2c7850d80aabc526/pygame-2.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:91476902426facd4bb0dad4dc3b2573bc82c95c71b135e0daaea072ed528d299", size = 10622016 },
+ { url = "https://files.pythonhosted.org/packages/92/16/2c602c332f45ff9526d61f6bd764db5096ff9035433e2172e2d2cadae8db/pygame-2.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4ee7f2771f588c966fa2fa8b829be26698c9b4836f82ede5e4edc1a68594942e", size = 13118279 },
+ { url = "https://files.pythonhosted.org/packages/cd/53/77ccbc384b251c6e34bfd2e734c638233922449a7844e3c7a11ef91cee39/pygame-2.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8040ea2ab18c6b255af706ec01355c8a6b08dc48d77fd4ee783f8fc46a843bf", size = 12384524 },
+ { url = "https://files.pythonhosted.org/packages/06/be/3ed337583f010696c3b3435e89a74fb29d0c74d0931e8f33c0a4246307a9/pygame-2.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47a6938de93fa610accd4969e638c2aebcb29b2fca518a84c3a39d91ab47116", size = 13587123 },
+ { url = "https://files.pythonhosted.org/packages/fd/ca/b015586a450db59313535662991b34d24c1f0c0dc149cc5f496573900f4e/pygame-2.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33006f784e1c7d7e466fcb61d5489da59cc5f7eb098712f792a225df1d4e229d", size = 14275532 },
+ { url = "https://files.pythonhosted.org/packages/b9/f2/d31e6ad42d657af07be2ffd779190353f759a07b51232b9e1d724f2cda46/pygame-2.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1206125f14cae22c44565c9d333607f1d9f59487b1f1432945dfc809aeaa3e88", size = 13952653 },
+ { url = "https://files.pythonhosted.org/packages/f3/42/8ea2a6979e6fa971702fece1747e862e2256d4a8558fe0da6364dd946c53/pygame-2.6.1-cp312-cp312-win32.whl", hash = "sha256:84fc4054e25262140d09d39e094f6880d730199710829902f0d8ceae0213379e", size = 10252421 },
+ { url = "https://files.pythonhosted.org/packages/5f/90/7d766d54bb95939725e9a9361f9c06b0cfbe3fe100aa35400f0a461a278a/pygame-2.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9e7396be0d9633831c3f8d5d82dd63ba373ad65599628294b7a4f8a5a01a65", size = 10624591 },
+]
+
[[package]]
name = "pygetwindow"
version = "0.0.9"
@@ -1806,162 +1841,162 @@ name = "pyobjc"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-accessibility", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-accounts", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-addressbook" },
- { name = "pyobjc-framework-adservices", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-adsupport", marker = "platform_release >= '18.0'" },
- { name = "pyobjc-framework-applescriptkit" },
- { name = "pyobjc-framework-applescriptobjc", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-applicationservices" },
- { name = "pyobjc-framework-apptrackingtransparency", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-audiovideobridging", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-authenticationservices", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-automaticassessmentconfiguration", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-automator" },
- { name = "pyobjc-framework-avfoundation", marker = "platform_release >= '11.0'" },
- { name = "pyobjc-framework-avkit", marker = "platform_release >= '13.0'" },
- { name = "pyobjc-framework-avrouting", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-backgroundassets", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-browserenginekit", marker = "platform_release >= '23.4'" },
- { name = "pyobjc-framework-businesschat", marker = "platform_release >= '18.0'" },
- { name = "pyobjc-framework-calendarstore", marker = "platform_release >= '9.0'" },
- { name = "pyobjc-framework-callkit", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-carbon" },
- { name = "pyobjc-framework-cfnetwork" },
- { name = "pyobjc-framework-cinematic", marker = "platform_release >= '23.0'" },
- { name = "pyobjc-framework-classkit", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-cloudkit", marker = "platform_release >= '14.0'" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-collaboration", marker = "platform_release >= '9.0'" },
- { name = "pyobjc-framework-colorsync", marker = "platform_release >= '17.0'" },
- { name = "pyobjc-framework-contacts", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-contactsui", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-coreaudio" },
- { name = "pyobjc-framework-coreaudiokit" },
- { name = "pyobjc-framework-corebluetooth", marker = "platform_release >= '14.0'" },
- { name = "pyobjc-framework-coredata" },
- { name = "pyobjc-framework-corehaptics", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-corelocation", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-coremedia", marker = "platform_release >= '11.0'" },
- { name = "pyobjc-framework-coremediaio", marker = "platform_release >= '11.0'" },
- { name = "pyobjc-framework-coremidi" },
- { name = "pyobjc-framework-coreml", marker = "platform_release >= '17.0'" },
- { name = "pyobjc-framework-coremotion", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-coreservices" },
- { name = "pyobjc-framework-corespotlight", marker = "platform_release >= '17.0'" },
- { name = "pyobjc-framework-coretext" },
- { name = "pyobjc-framework-corewlan", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-cryptotokenkit", marker = "platform_release >= '14.0'" },
- { name = "pyobjc-framework-datadetection", marker = "platform_release >= '21.0'" },
- { name = "pyobjc-framework-devicecheck", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-devicediscoveryextension", marker = "platform_release >= '24.0'" },
- { name = "pyobjc-framework-dictionaryservices", marker = "platform_release >= '9.0'" },
- { name = "pyobjc-framework-discrecording" },
- { name = "pyobjc-framework-discrecordingui" },
- { name = "pyobjc-framework-diskarbitration" },
- { name = "pyobjc-framework-dvdplayback" },
- { name = "pyobjc-framework-eventkit", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-exceptionhandling" },
- { name = "pyobjc-framework-executionpolicy", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-extensionkit", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-externalaccessory", marker = "platform_release >= '17.0'" },
- { name = "pyobjc-framework-fileprovider", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-fileproviderui", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-findersync", marker = "platform_release >= '14.0'" },
- { name = "pyobjc-framework-fsevents", marker = "platform_release >= '9.0'" },
- { name = "pyobjc-framework-gamecenter", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-gamecontroller", marker = "platform_release >= '13.0'" },
- { name = "pyobjc-framework-gamekit", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-gameplaykit", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-healthkit", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-imagecapturecore", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-inputmethodkit", marker = "platform_release >= '9.0'" },
- { name = "pyobjc-framework-installerplugins" },
- { name = "pyobjc-framework-instantmessage", marker = "platform_release >= '9.0'" },
- { name = "pyobjc-framework-intents", marker = "platform_release >= '16.0'" },
- { name = "pyobjc-framework-intentsui", marker = "platform_release >= '21.0'" },
- { name = "pyobjc-framework-iobluetooth" },
- { name = "pyobjc-framework-iobluetoothui" },
- { name = "pyobjc-framework-iosurface", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-ituneslibrary", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-kernelmanagement", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-latentsemanticmapping" },
- { name = "pyobjc-framework-launchservices" },
- { name = "pyobjc-framework-libdispatch", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-libxpc", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-linkpresentation", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-localauthentication", marker = "platform_release >= '14.0'" },
- { name = "pyobjc-framework-localauthenticationembeddedui", marker = "platform_release >= '21.0'" },
- { name = "pyobjc-framework-mailkit", marker = "platform_release >= '21.0'" },
- { name = "pyobjc-framework-mapkit", marker = "platform_release >= '13.0'" },
- { name = "pyobjc-framework-mediaaccessibility", marker = "platform_release >= '13.0'" },
- { name = "pyobjc-framework-mediaextension", marker = "platform_release >= '24.0'" },
- { name = "pyobjc-framework-medialibrary", marker = "platform_release >= '13.0'" },
- { name = "pyobjc-framework-mediaplayer", marker = "platform_release >= '16.0'" },
- { name = "pyobjc-framework-mediatoolbox", marker = "platform_release >= '13.0'" },
- { name = "pyobjc-framework-metal", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-metalfx", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-metalkit", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-metalperformanceshaders", marker = "platform_release >= '17.0'" },
- { name = "pyobjc-framework-metalperformanceshadersgraph", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-metrickit", marker = "platform_release >= '21.0'" },
- { name = "pyobjc-framework-mlcompute", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-modelio", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-multipeerconnectivity", marker = "platform_release >= '14.0'" },
- { name = "pyobjc-framework-naturallanguage", marker = "platform_release >= '18.0'" },
- { name = "pyobjc-framework-netfs", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-network", marker = "platform_release >= '18.0'" },
- { name = "pyobjc-framework-networkextension", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-notificationcenter", marker = "platform_release >= '14.0'" },
- { name = "pyobjc-framework-opendirectory", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-osakit" },
- { name = "pyobjc-framework-oslog", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-passkit", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-pencilkit", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-phase", marker = "platform_release >= '21.0'" },
- { name = "pyobjc-framework-photos", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-photosui", marker = "platform_release >= '15.0'" },
- { name = "pyobjc-framework-preferencepanes" },
- { name = "pyobjc-framework-pushkit", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-quartz" },
- { name = "pyobjc-framework-quicklookthumbnailing", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-replaykit", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-safariservices", marker = "platform_release >= '16.0'" },
- { name = "pyobjc-framework-safetykit", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-scenekit", marker = "platform_release >= '11.0'" },
- { name = "pyobjc-framework-screencapturekit", marker = "platform_release >= '21.4'" },
- { name = "pyobjc-framework-screensaver" },
- { name = "pyobjc-framework-screentime", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-scriptingbridge", marker = "platform_release >= '9.0'" },
- { name = "pyobjc-framework-searchkit" },
- { name = "pyobjc-framework-security" },
- { name = "pyobjc-framework-securityfoundation" },
- { name = "pyobjc-framework-securityinterface" },
- { name = "pyobjc-framework-sensitivecontentanalysis", marker = "platform_release >= '23.0'" },
- { name = "pyobjc-framework-servicemanagement", marker = "platform_release >= '10.0'" },
- { name = "pyobjc-framework-sharedwithyou", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-sharedwithyoucore", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-shazamkit", marker = "platform_release >= '21.0'" },
- { name = "pyobjc-framework-social", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-soundanalysis", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-speech", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-spritekit", marker = "platform_release >= '13.0'" },
- { name = "pyobjc-framework-storekit", marker = "platform_release >= '11.0'" },
- { name = "pyobjc-framework-symbols", marker = "platform_release >= '23.0'" },
- { name = "pyobjc-framework-syncservices" },
- { name = "pyobjc-framework-systemconfiguration" },
- { name = "pyobjc-framework-systemextensions", marker = "platform_release >= '19.0'" },
- { name = "pyobjc-framework-threadnetwork", marker = "platform_release >= '22.0'" },
- { name = "pyobjc-framework-uniformtypeidentifiers", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-usernotifications", marker = "platform_release >= '18.0'" },
- { name = "pyobjc-framework-usernotificationsui", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-videosubscriberaccount", marker = "platform_release >= '18.0'" },
- { name = "pyobjc-framework-videotoolbox", marker = "platform_release >= '12.0'" },
- { name = "pyobjc-framework-virtualization", marker = "platform_release >= '20.0'" },
- { name = "pyobjc-framework-vision", marker = "platform_release >= '17.0'" },
- { name = "pyobjc-framework-webkit" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-accessibility", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-accounts", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-addressbook", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-adservices", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-adsupport", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-applescriptkit", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-applescriptobjc", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-applicationservices", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-apptrackingtransparency", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-audiovideobridging", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-authenticationservices", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-automaticassessmentconfiguration", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-automator", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-avfoundation", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-avkit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-avrouting", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-backgroundassets", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-browserenginekit", marker = "platform_release >= '23.4' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-businesschat", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-calendarstore", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-callkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-carbon", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cfnetwork", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cinematic", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-classkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cloudkit", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-collaboration", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-colorsync", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-contacts", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-contactsui", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreaudiokit", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-corebluetooth", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-corehaptics", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-corelocation", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremediaio", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremidi", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreml", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremotion", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-corespotlight", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coretext", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-corewlan", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cryptotokenkit", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-datadetection", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-devicecheck", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-devicediscoveryextension", marker = "platform_release >= '24.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-dictionaryservices", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-discrecording", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-discrecordingui", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-diskarbitration", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-dvdplayback", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-eventkit", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-exceptionhandling", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-executionpolicy", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-extensionkit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-externalaccessory", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-fileprovider", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-fileproviderui", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-findersync", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-fsevents", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-gamecenter", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-gamecontroller", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-gamekit", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-gameplaykit", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-healthkit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-imagecapturecore", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-inputmethodkit", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-installerplugins", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-instantmessage", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-intents", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-intentsui", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-iobluetooth", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-iobluetoothui", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-iosurface", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-ituneslibrary", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-kernelmanagement", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-latentsemanticmapping", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-launchservices", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-libdispatch", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-libxpc", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-linkpresentation", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-localauthentication", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-localauthenticationembeddedui", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-mailkit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-mapkit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-mediaaccessibility", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-mediaextension", marker = "platform_release >= '24.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-medialibrary", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-mediaplayer", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-mediatoolbox", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metal", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metalfx", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metalkit", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metalperformanceshaders", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metalperformanceshadersgraph", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metrickit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-mlcompute", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-modelio", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-multipeerconnectivity", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-naturallanguage", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-netfs", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-network", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-networkextension", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-notificationcenter", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-opendirectory", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-osakit", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-oslog", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-passkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-pencilkit", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-phase", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-photos", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-photosui", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-preferencepanes", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-pushkit", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quicklookthumbnailing", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-replaykit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-safariservices", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-safetykit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-scenekit", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-screencapturekit", marker = "platform_release >= '21.4' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-screensaver", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-screentime", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-scriptingbridge", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-searchkit", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-securityfoundation", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-securityinterface", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-sensitivecontentanalysis", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-servicemanagement", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-sharedwithyou", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-sharedwithyoucore", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-shazamkit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-social", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-soundanalysis", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-speech", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-spritekit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-storekit", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-symbols", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-syncservices", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-systemconfiguration", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-systemextensions", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-threadnetwork", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-uniformtypeidentifiers", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-usernotifications", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-usernotificationsui", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-videosubscriberaccount", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-videotoolbox", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-virtualization", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-vision", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-webkit", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e1/d6/27b1c9a02f6cb4954984ce1a0239618e52f78c329c7e7450bf1f219b0f0a/pyobjc-11.0.tar.gz", hash = "sha256:a8f7baed65797f67afd46290b02f652c23f4b158ddf960bce0441b78f6803418", size = 11044 }
wheels = [
@@ -1983,9 +2018,9 @@ name = "pyobjc-framework-accessibility"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b5/61/7484cc4ad3aa7854cd4c969379a5f044261259d08f7c20b6718493b484f9/pyobjc_framework_accessibility-11.0.tar.gz", hash = "sha256:097450c641fa9ac665199762e77867f2a82775be2f749b8fa69223b828f60656", size = 44597 }
wheels = [
@@ -1998,8 +2033,8 @@ name = "pyobjc-framework-accounts"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c2/fa/b64f3f02e0a8b189dc07c391546e2dbe30ef1b3515d1427cdab743545b90/pyobjc_framework_accounts-11.0.tar.gz", hash = "sha256:afc4ae277be1e3e1f90269001c2fd886093a5465e365d7f9a3a0af3e17f06210", size = 17340 }
wheels = [
@@ -2012,8 +2047,8 @@ name = "pyobjc-framework-addressbook"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/ef/5b5f6b61907ae43509fbf1654e043115d9a64d97efdc28fbb90d06c199f6/pyobjc_framework_addressbook-11.0.tar.gz", hash = "sha256:87073c85bb342eb27faa6eceb7a0e8a4c1e32ad1f2b62bb12dafb5e7b9f15837", size = 97116 }
wheels = [
@@ -2026,8 +2061,8 @@ name = "pyobjc-framework-adservices"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/51/7c/0c6e01f83b0c5c7968564a40146f4d07080df278457bdb5a982c8f26a74d/pyobjc_framework_adservices-11.0.tar.gz", hash = "sha256:d2e1a2f395e93e1bbe754ab0d76ce1d64c0d3928472634437e0382eafc6765cd", size = 12732 }
wheels = [
@@ -2040,8 +2075,8 @@ name = "pyobjc-framework-adsupport"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0c/07/b8b5f741d1e2cad97100444b255e6ecaca3668e7414039981799aa330035/pyobjc_framework_adsupport-11.0.tar.gz", hash = "sha256:20eb8a683d34fb7a6efeceaf964a24b88c3434875c44f66db5e1b609e678043a", size = 12819 }
wheels = [
@@ -2054,8 +2089,8 @@ name = "pyobjc-framework-applescriptkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/14/c3/d7f9a33de7ab8e3950350e0862214e66f27ed6bff1a491bc391c377ab83e/pyobjc_framework_applescriptkit-11.0.tar.gz", hash = "sha256:4bafac4a036f0fb8ba01488b8e91d3ac861ce6e61154ffbd0b26f82b99779b50", size = 12638 }
wheels = [
@@ -2068,8 +2103,8 @@ name = "pyobjc-framework-applescriptobjc"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fb/9f/bb4fdbcea418f8472d7a67d4d2e4a15fca11fed04648db5208b0fce84807/pyobjc_framework_applescriptobjc-11.0.tar.gz", hash = "sha256:baff9988b6e886aed0e76441358417707de9088be5733f22055fed7904ca1001", size = 12675 }
wheels = [
@@ -2082,10 +2117,10 @@ name = "pyobjc-framework-applicationservices"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coretext" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coretext", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ba/fb/4e42573b0d3baa3fa18ec53614cf979f951313f1451e8f2e17df9429da1f/pyobjc_framework_applicationservices-11.0.tar.gz", hash = "sha256:d6ea18dfc7d5626a3ecf4ac72d510405c0d3a648ca38cae8db841acdebecf4d2", size = 224334 }
wheels = [
@@ -2098,8 +2133,8 @@ name = "pyobjc-framework-apptrackingtransparency"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/36/40/c1c48ed49b5e55c7a635aa1e7ca41ffa1c5547e26243f26489c4768cd730/pyobjc_framework_apptrackingtransparency-11.0.tar.gz", hash = "sha256:cd5c834b5b19c21ad6c317ba5d29f30a8d0ae5d14e7cf557da22abc0850f1e91", size = 13385 }
wheels = [
@@ -2112,8 +2147,8 @@ name = "pyobjc-framework-audiovideobridging"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/89/5f/0bd5beded0415b53f443da804410eda6a53e1bc64f8779ed9a592719da8c/pyobjc_framework_audiovideobridging-11.0.tar.gz", hash = "sha256:dbc45b06418dd780c365956fdfd69d007436b5ee54c51e671196562eb8290ba6", size = 72418 }
wheels = [
@@ -2126,8 +2161,8 @@ name = "pyobjc-framework-authenticationservices"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/31/0f/2de0d941e9c9b2eb1ce8b22eb31adc7227badfe1e53f615431d3a7fdcd48/pyobjc_framework_authenticationservices-11.0.tar.gz", hash = "sha256:6a060ce651df142e8923d1383449bc6f2c7f5eb0b517152dac609bde3901064e", size = 140036 }
wheels = [
@@ -2140,8 +2175,8 @@ name = "pyobjc-framework-automaticassessmentconfiguration"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/09/d5/5febfee260b88e426c7e799cc95990818feeaa9f740fb9dd516559c96520/pyobjc_framework_automaticassessmentconfiguration-11.0.tar.gz", hash = "sha256:5d3691af2b94e44ca594b6791556e15a9f0a3f9432df51cb891f5f859a65e467", size = 24420 }
wheels = [
@@ -2154,8 +2189,8 @@ name = "pyobjc-framework-automator"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/25/1b/1ba4eb296c3915f2e367e45470cb310a9c78b4dd65a37bd522f458f245aa/pyobjc_framework_automator-11.0.tar.gz", hash = "sha256:412d330f8c6f30066cad15e1bdecdc865510bbce469cc7d9477384c4e9f2550f", size = 200905 }
wheels = [
@@ -2168,11 +2203,11 @@ name = "pyobjc-framework-avfoundation"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coreaudio" },
- { name = "pyobjc-framework-coremedia" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/06/018ad0e2a38dbdbc5c126d7ce37488c4d581d4e2a2b9ef678162bb36d5f6/pyobjc_framework_avfoundation-11.0.tar.gz", hash = "sha256:269a592bdaf8a16948d8935f0cf7c8cb9a53e7ea609a963ada0e55f749ddb530", size = 871064 }
wheels = [
@@ -2185,9 +2220,9 @@ name = "pyobjc-framework-avkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/79/5b2fcb94b051da32a24b54bb0d90b1d01b190e1402b6303747de47fb17ac/pyobjc_framework_avkit-11.0.tar.gz", hash = "sha256:5fa40919320277b820df3e4c6e84cba91ef7221a28f4eb5374e3dbd80d1e521a", size = 46311 }
wheels = [
@@ -2200,8 +2235,8 @@ name = "pyobjc-framework-avrouting"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d5/80/63680dc7788bc3573a20fc5421dfcf606970a0cd3b2457829d9b66603ae0/pyobjc_framework_avrouting-11.0.tar.gz", hash = "sha256:54ec9ea0b5adb5149b554e23c07c6b4f4bdb2892ca2ed7b3e88a5de936313025", size = 20561 }
wheels = [
@@ -2214,8 +2249,8 @@ name = "pyobjc-framework-backgroundassets"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/17/83b873069b0c0763365de88648ad4a2472e9e96fcac39fa534f3633552e8/pyobjc_framework_backgroundassets-11.0.tar.gz", hash = "sha256:9488c3f86bf427898a88b7100e77200c08a487a35c75c1b5735bd69c57ba38cb", size = 23658 }
wheels = [
@@ -2228,11 +2263,11 @@ name = "pyobjc-framework-browserenginekit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coreaudio" },
- { name = "pyobjc-framework-coremedia" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/2e/df3d2f7e53132d398c2922d331dd1d2aa352997a1a4a1390e59db51c1d13/pyobjc_framework_browserenginekit-11.0.tar.gz", hash = "sha256:51971527f5103c0e09a4ef438c352ebb037fcad8971f8420a781c72ee421f758", size = 31352 }
wheels = [
@@ -2245,8 +2280,8 @@ name = "pyobjc-framework-businesschat"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5a/f2/4541989f2c9c5fc3cdfc94ebf31fc6619554b6c22dafdbb57f866a392bc1/pyobjc_framework_businesschat-11.0.tar.gz", hash = "sha256:20fe1c8c848ef3c2e132172d9a007a8aa65b08875a9ca5c27afbfc4396b16dbb", size = 12953 }
wheels = [
@@ -2259,8 +2294,8 @@ name = "pyobjc-framework-calendarstore"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/d3/722c1b16c7d9bdd5c408735c15193e8396f2d22ab6410b0af4569f39c46e/pyobjc_framework_calendarstore-11.0.tar.gz", hash = "sha256:40173f729df56b70ec14f9680962a248c3ce7b4babb46e8b0d760a13975ef174", size = 68475 }
wheels = [
@@ -2273,8 +2308,8 @@ name = "pyobjc-framework-callkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/0a/9d39ebac92006960b8059f664d8eb7b9cdb8763fe4e8102b2d24b853004f/pyobjc_framework_callkit-11.0.tar.gz", hash = "sha256:52e44a05d0357558e1479977ed2bcb325fabc8d337f641f0249178b5b491fc59", size = 39720 }
wheels = [
@@ -2287,8 +2322,8 @@ name = "pyobjc-framework-carbon"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/22/15/51964f36a8ae1002b16d213d2e5ba11cc861bdd9369f1e3f116350d788c5/pyobjc_framework_carbon-11.0.tar.gz", hash = "sha256:476f690f0b34aa9e4cb3923e61481aefdcf33e38ec6087b530a94871eee2b914", size = 37538 }
wheels = [
@@ -2301,8 +2336,8 @@ name = "pyobjc-framework-cfnetwork"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4f/36/7cebdfb621c7d46eeab3173256bc2e1cba1bbbbe6c0ac8aeb9a4fe2a4627/pyobjc_framework_cfnetwork-11.0.tar.gz", hash = "sha256:eb742fc6a42b248886ff09c3cf247d56e65236864bbea4264e70af8377948d96", size = 78532 }
wheels = [
@@ -2315,11 +2350,11 @@ name = "pyobjc-framework-cinematic"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-avfoundation" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coremedia" },
- { name = "pyobjc-framework-metal" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/33/ef/b5857d567cd6e0366f61c381ebea52383b98d1ac03341f39e779a085812a/pyobjc_framework_cinematic-11.0.tar.gz", hash = "sha256:94a2de8bf3f38bd190311b6bf98d1e2cea7888840b3ce3aa92e464c0216a5cdb", size = 25740 }
wheels = [
@@ -2332,8 +2367,8 @@ name = "pyobjc-framework-classkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f5/81/126075eaf5ccf254ddb4cfd99d92a266c30803c5b4572ea3a920fd85e850/pyobjc_framework_classkit-11.0.tar.gz", hash = "sha256:dc5b3856612cafdc7071fbebc252b8908dbf2433e0e5ddb15a0bcd1ee282d27c", size = 39301 }
wheels = [
@@ -2346,11 +2381,11 @@ name = "pyobjc-framework-cloudkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-accounts" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coredata" },
- { name = "pyobjc-framework-corelocation" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-accounts", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-corelocation", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/89/6c/b0709fed7fc5a1e81de311b9273bb7ba3820a636f8ba880e90510bb6d460/pyobjc_framework_cloudkit-11.0.tar.gz", hash = "sha256:e3f6bf2c3358dd394174b1e69fcec6859951fcd15f6433c6fa3082e3b7e2656d", size = 123034 }
wheels = [
@@ -2363,7 +2398,7 @@ name = "pyobjc-framework-cocoa"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c5/32/53809096ad5fc3e7a2c5ddea642590a5f2cb5b81d0ad6ea67fdb2263d9f9/pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5", size = 6173848 }
wheels = [
@@ -2376,8 +2411,8 @@ name = "pyobjc-framework-collaboration"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6b/ee/1f6893eb882af5ecc6a6f4182b2ec85df777c4bc6b9a20a6b42c23abff3f/pyobjc_framework_collaboration-11.0.tar.gz", hash = "sha256:9f53929dd6d5b1a5511494432bf83807041c6f8b9ab6cf6ff184eee0b6f8226f", size = 17084 }
wheels = [
@@ -2390,8 +2425,8 @@ name = "pyobjc-framework-colorsync"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9a/24/397a80cd2313cc9e1b73b9acb1de66b740bbece4fe87ed4ea158de8fcef8/pyobjc_framework_colorsync-11.0.tar.gz", hash = "sha256:4f531f6075d9cc4b9d426620a1b04d3aaeb56b5ff178d0a6b0e93d068a5db0d2", size = 39249 }
wheels = [
@@ -2404,8 +2439,8 @@ name = "pyobjc-framework-contacts"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f5/a2/89053853b28c1f2f2e69092d3e81b7c26073bc8396fc87772b3b1bfb9d57/pyobjc_framework_contacts-11.0.tar.gz", hash = "sha256:fc215baa9f66dbf9ffa1cb8170d102a3546cfd708b2b42de4e9d43645aec03d9", size = 84253 }
wheels = [
@@ -2418,9 +2453,9 @@ name = "pyobjc-framework-contactsui"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-contacts" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-contacts", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3f/67/122b16fd7f2da7f0f48c1d7fcaf0f1951253ddd5489d909a1b5fb80f3925/pyobjc_framework_contactsui-11.0.tar.gz", hash = "sha256:d0f2a4afea807fbe4db1518c4f81f0dc9aa1817fe7cb16115308fc00375a70db", size = 19486 }
wheels = [
@@ -2433,8 +2468,8 @@ name = "pyobjc-framework-coreaudio"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/31/e6/3b7a8af3defec012d6cacf277fd8d5c3e254ceace63a05447dc1119f3a7e/pyobjc_framework_coreaudio-11.0.tar.gz", hash = "sha256:38b6b531381119be6998cf704d04c9ea475aaa33f6dd460e0584351475acd0ae", size = 140507 }
wheels = [
@@ -2447,9 +2482,9 @@ name = "pyobjc-framework-coreaudiokit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coreaudio" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ef/1a/604cac8d992b6e66adbb98edb1f65820116f5d74d8decd6d43898ae2929d/pyobjc_framework_coreaudiokit-11.0.tar.gz", hash = "sha256:1a4c3de4a02b0dfa7410c012c7f0939edd2e127d439fb934aeafc68450615f1d", size = 21450 }
wheels = [
@@ -2462,8 +2497,8 @@ name = "pyobjc-framework-corebluetooth"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/93/74/66a62a36da9db5924ee15de6fe1eb544930609b307b3bfbc021b5cf43781/pyobjc_framework_corebluetooth-11.0.tar.gz", hash = "sha256:1dcb7c039c2efa7c72dc14cdda80e677240b49fa38999941a77ee02ca142998d", size = 59797 }
wheels = [
@@ -2476,8 +2511,8 @@ name = "pyobjc-framework-coredata"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/84/22/6787205b91cb6d526b6b472ebaa5baff275200774050a55b4b25d2bd957a/pyobjc_framework_coredata-11.0.tar.gz", hash = "sha256:b11acb51ff31cfb69a53f4e127996bf194bcac770e8fa67cb5ba3fb16a496058", size = 260029 }
wheels = [
@@ -2490,8 +2525,8 @@ name = "pyobjc-framework-corehaptics"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2a/b8/66481497362171e7ad42fc8fcc0272c04b95a707c5c1e7e8f8a8bfe58917/pyobjc_framework_corehaptics-11.0.tar.gz", hash = "sha256:1949b56ac0bd4219eb04c466cdd0f7f93d6826ed92ee61f01a4b5e98139ee039", size = 42956 }
wheels = [
@@ -2504,8 +2539,8 @@ name = "pyobjc-framework-corelocation"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0a/2d/b21ca49a34db49390420a9d7d05fd9eb89850dbec0a555c9ee408f52609c/pyobjc_framework_corelocation-11.0.tar.gz", hash = "sha256:05055c3b567f7f8f796845da43fb755d84d630909b927a39f25cf706ef52687d", size = 103955 }
wheels = [
@@ -2518,8 +2553,8 @@ name = "pyobjc-framework-coremedia"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/02/60/7c7b9f13c94910882de6cc08f48a52cce9739e75cc3b3b6de5c857e6536a/pyobjc_framework_coremedia-11.0.tar.gz", hash = "sha256:a414db97ba30b43c9dd96213459d6efb169f9e92ce1ad7a75516a679b181ddfb", size = 249161 }
wheels = [
@@ -2532,8 +2567,8 @@ name = "pyobjc-framework-coremediaio"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a1/59/904af57d302caa4c20d3bfebb9fb9300ccc3c396134460821c9f1e8ab65b/pyobjc_framework_coremediaio-11.0.tar.gz", hash = "sha256:7d652cf1a2a75c78ea6e8dbc7fc8b782bfc0f07eafc84b700598172c82f373d8", size = 107856 }
wheels = [
@@ -2546,8 +2581,8 @@ name = "pyobjc-framework-coremidi"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/90/d004cdf4c52b8b16842e15135495de882d743b4f0217946bd8ae1a920173/pyobjc_framework_coremidi-11.0.tar.gz", hash = "sha256:acace4448b3e4802ab5dd75bbf875aae5e1f6c8cab2b2f1d58af20fc8b2a5a7f", size = 107342 }
wheels = [
@@ -2560,8 +2595,8 @@ name = "pyobjc-framework-coreml"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2e/64/4f0a990ec0955fe9b88f1fa58303c8471c551996670216527b4ac559ed8f/pyobjc_framework_coreml-11.0.tar.gz", hash = "sha256:143a1f73a0ea0a0ea103f3175cb87a61bbcb98f70f85320ed4c61302b9156d58", size = 81452 }
wheels = [
@@ -2574,8 +2609,8 @@ name = "pyobjc-framework-coremotion"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/be/79/5c4ff39a48f0dc0f764d1330b2360e9f31e3a32414e8690e7f20e4574e93/pyobjc_framework_coremotion-11.0.tar.gz", hash = "sha256:d1e7ca418897e35365d07c6fd5b5d625a3c44261b6ce46dcf80787f634ad6fa5", size = 66508 }
wheels = [
@@ -2588,9 +2623,9 @@ name = "pyobjc-framework-coreservices"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-fsevents" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-fsevents", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ca/b5/19c096b9938d6e2fdb1b436f21ad989b77dbeb4e59b3db4bd344800fa1e8/pyobjc_framework_coreservices-11.0.tar.gz", hash = "sha256:ac96954f1945a1153bdfef685611665749eaa8016b5af6f34bd56a274952b03a", size = 1244406 }
wheels = [
@@ -2603,8 +2638,8 @@ name = "pyobjc-framework-corespotlight"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/6a/6707d7ef339b9ad2dd0994d1df42969ee3b231f2d098f3377d40aed60b4f/pyobjc_framework_corespotlight-11.0.tar.gz", hash = "sha256:a96c9b4ba473bc3ee19afa01a9af989458e6a56e9656c2cdea1850d2b13720e6", size = 86130 }
wheels = [
@@ -2617,9 +2652,9 @@ name = "pyobjc-framework-coretext"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/e8/9b68dc788828e38143a3e834e66346713751cb83d7f0955016323005c1a2/pyobjc_framework_coretext-11.0.tar.gz", hash = "sha256:a68437153e627847e3898754dd3f13ae0cb852246b016a91f9c9cbccb9f91a43", size = 274222 }
wheels = [
@@ -2632,8 +2667,8 @@ name = "pyobjc-framework-corewlan"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2e/a9/cda522b270adb75d62bae447b2131da62912b5eda058a07e3a433689116f/pyobjc_framework_corewlan-11.0.tar.gz", hash = "sha256:8803981d64e3eb4fa0ea56657a9b98e4004de5a84d56e32e5444815d8ed6fa6f", size = 65254 }
wheels = [
@@ -2646,8 +2681,8 @@ name = "pyobjc-framework-cryptotokenkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b8/72/b871fa5476479e4a22a4a0e971fb4724b0eb94c721365539ad55f4dc3135/pyobjc_framework_cryptotokenkit-11.0.tar.gz", hash = "sha256:a1bbfe9170c35cb427d39167af55aefea651c5c8a45c0de60226dae04b61a6b1", size = 58734 }
wheels = [
@@ -2660,8 +2695,8 @@ name = "pyobjc-framework-datadetection"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/33/6b/b896feb16e914dc81b6ed6cdbd0b6e6390eaafc80fff5297ec17eb0bd716/pyobjc_framework_datadetection-11.0.tar.gz", hash = "sha256:9967555151892f8400cffac86e8656f2cb8d7866963fdee255e0747fa1386533", size = 13738 }
wheels = [
@@ -2674,8 +2709,8 @@ name = "pyobjc-framework-devicecheck"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/f8/237a92dd9ba8a88b7027f78cba83e61b0011bfc2a49351ecaa177233f639/pyobjc_framework_devicecheck-11.0.tar.gz", hash = "sha256:66cff0323dc8eef1b76d60f9c9752684f11e534ebda60ecbf6858a9c73553f64", size = 14198 }
wheels = [
@@ -2688,8 +2723,8 @@ name = "pyobjc-framework-devicediscoveryextension"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e1/48/178a1879109128f34334fdae2fe4463c7620f169593bea96704f347d945e/pyobjc_framework_devicediscoveryextension-11.0.tar.gz", hash = "sha256:576dac3f418cfc4f71020a45f06231d14e4b2a8e182ef0020dd9da3cf238d02f", size = 14511 }
wheels = [
@@ -2702,8 +2737,8 @@ name = "pyobjc-framework-dictionaryservices"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-coreservices" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/cf/2913c7df737eb8519acb7ef6429127e40d6c334415e38cfa18d6481150eb/pyobjc_framework_dictionaryservices-11.0.tar.gz", hash = "sha256:6b5f27c75424860f169e7c7e182fabffdba22854fedb8023de180e8770661dce", size = 10823 }
wheels = [
@@ -2716,8 +2751,8 @@ name = "pyobjc-framework-discrecording"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/cc/f36612b67ca1fff7659d7933b563dce61f8c84dad0bf79fab08bb34949ad/pyobjc_framework_discrecording-11.0.tar.gz", hash = "sha256:6bdc533f067d049ea5032f65af70b5cdab68673574ac32dacb46509a9411d256", size = 122426 }
wheels = [
@@ -2730,9 +2765,9 @@ name = "pyobjc-framework-discrecordingui"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-discrecording" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-discrecording", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d4/6b/3c120c59a939854dd4b7a162fad47011375c5ba00a12940f7217aea90eeb/pyobjc_framework_discrecordingui-11.0.tar.gz", hash = "sha256:bec8a252fd2022dce6c58b1f3366a7295efb0c7c77817f11f9efcce70527d7a2", size = 19614 }
wheels = [
@@ -2745,8 +2780,8 @@ name = "pyobjc-framework-diskarbitration"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/fb/5d3ff093144f499904b1e1bce18d010fe2171b9be62b4679d3dda8b3ad19/pyobjc_framework_diskarbitration-11.0.tar.gz", hash = "sha256:1c3e21398b366a1ce96cf68501a2e415f5ccad4b43a3e7cc901e09e896dfb545", size = 20096 }
wheels = [
@@ -2759,8 +2794,8 @@ name = "pyobjc-framework-dvdplayback"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c0/89/89ebee4863fd6f173bff9373b5bda4ffa87eba6197337617ab086e23c7d5/pyobjc_framework_dvdplayback-11.0.tar.gz", hash = "sha256:9a005f441afbc34aea301857e166fd650d82762a75d024253e18d1102b21b2f8", size = 64798 }
wheels = [
@@ -2773,8 +2808,8 @@ name = "pyobjc-framework-eventkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/54/13/38a98e5cee62e1655d84cfb88cad54fdec4ec272b5fd0c5ac3fc21e33e49/pyobjc_framework_eventkit-11.0.tar.gz", hash = "sha256:3d412203a510b3d62a5eb0987406e0951b13ed39c3351c0ec874afd72496627c", size = 75399 }
wheels = [
@@ -2787,8 +2822,8 @@ name = "pyobjc-framework-exceptionhandling"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cc/46/6c2c4805697a0cfb8413eb7bc6901298e7a1febd49bb1ea960274fc33af3/pyobjc_framework_exceptionhandling-11.0.tar.gz", hash = "sha256:b11562c6eeaef5d8d43e9d817cf50feceb02396e5eb6a7f61df2c0cec93d912b", size = 18157 }
wheels = [
@@ -2801,8 +2836,8 @@ name = "pyobjc-framework-executionpolicy"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ab/91/2e4cacbdabf01bc1207817edacc814b6bc486df12e857a8d86964d98fef4/pyobjc_framework_executionpolicy-11.0.tar.gz", hash = "sha256:de953a8acae98079015b19e75ec8154a311ac1a70fb6d885e17fab09464c98a9", size = 13753 }
wheels = [
@@ -2815,8 +2850,8 @@ name = "pyobjc-framework-extensionkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/22/98/803e3cb000dac227eb0d223802a0aeb052d34a741e572d9584e7d83afca7/pyobjc_framework_extensionkit-11.0.tar.gz", hash = "sha256:82d9e79532e5a0ff0eadf1ccac236c5d3dca344e1090a0f3e88519faa24143c7", size = 19200 }
wheels = [
@@ -2829,8 +2864,8 @@ name = "pyobjc-framework-externalaccessory"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/67/b0/ac0a02fe26e66c33fee751a65c1ed06bbd2934db8636e08bb491e8334bad/pyobjc_framework_externalaccessory-11.0.tar.gz", hash = "sha256:39e59331ced75cdcccf23bb5ffe0fa9d67e0c190c1da8887a0e4349b7e27584f", size = 22577 }
wheels = [
@@ -2843,8 +2878,8 @@ name = "pyobjc-framework-fileprovider"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/44/fc/b8593d8645b9933e60a885f451d0c12d9c0e1b00e62121d8660d95852dff/pyobjc_framework_fileprovider-11.0.tar.gz", hash = "sha256:dcc3ac3c90117c1b8027ea5f26dad6fe5045f688ce3e60d07ece12ec56e17ab3", size = 78701 }
wheels = [
@@ -2857,8 +2892,8 @@ name = "pyobjc-framework-fileproviderui"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-fileprovider" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-fileprovider", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/9d/ca4aed36e6188623e9da633634af772f239bee74934322e1c19ae7b79a53/pyobjc_framework_fileproviderui-11.0.tar.gz", hash = "sha256:cf5c7d32b29d344b65217397eea7b1a2913ce52ce923c9e04135a7a298848d04", size = 13419 }
wheels = [
@@ -2871,8 +2906,8 @@ name = "pyobjc-framework-findersync"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f6/e3/24df6e24b589073815be13f2943b93feb12afbf558f6e54c4033b57c29ee/pyobjc_framework_findersync-11.0.tar.gz", hash = "sha256:8dab3feff5debd6bc3746a21ded991716723d98713d1ba37cec1c5e2ad78ee63", size = 15295 }
wheels = [
@@ -2885,8 +2920,8 @@ name = "pyobjc-framework-fsevents"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/82/37/4c09cc7b8678e2bb5b68ebc62e817eb88c409b1c41bdc1510d7d24a0372d/pyobjc_framework_fsevents-11.0.tar.gz", hash = "sha256:e01dab04704a518e4c3e1f7d8722819a4f228d5082978e11618aa7abba3883fe", size = 29078 }
wheels = [
@@ -2899,8 +2934,8 @@ name = "pyobjc-framework-gamecenter"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7f/3b/e66caebc948d9fe3b2671659caab220aff6d5e80ac25442d83331b523d23/pyobjc_framework_gamecenter-11.0.tar.gz", hash = "sha256:18a05500dbcf2cca4a0f05839ec010c76ee08ab65b65020c9538a31feb274483", size = 31459 }
wheels = [
@@ -2913,8 +2948,8 @@ name = "pyobjc-framework-gamecontroller"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fa/30/02ca5a4fb911acf3e8018abcbd29631a842aeac02958ae91fab1acb13ad1/pyobjc_framework_gamecontroller-11.0.tar.gz", hash = "sha256:6d62f4493d634eba03a43a14c4d1e4511e1e3a2ca2e9cbefa6ae9278a272c1d0", size = 115318 }
wheels = [
@@ -2927,9 +2962,9 @@ name = "pyobjc-framework-gamekit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3f/df/c161460e5736a34f9b59aa0a3f2d6ad1d1cd9a913aa63c89c41a6ba3b6ae/pyobjc_framework_gamekit-11.0.tar.gz", hash = "sha256:29b5464ca78f0de62e6b6d56e80bbeccb96dc13820b6d5b4e835ab1cc127e5b9", size = 164394 }
wheels = [
@@ -2942,9 +2977,9 @@ name = "pyobjc-framework-gameplaykit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-spritekit" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-spritekit", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/41/f0/980c4fc3c594d9726b7eb6ae83f73127b22560e1541c7d272d23d17fdf0d/pyobjc_framework_gameplaykit-11.0.tar.gz", hash = "sha256:90eeec464fba992d75a406ccbddb35ed7420a4f5226f19c018982fa3ba7bf431", size = 72837 }
wheels = [
@@ -2957,8 +2992,8 @@ name = "pyobjc-framework-healthkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7b/2f/d79d2ec7c23bfc94bfaa7b7c6f6487a8bffdb73263eea6900aab56135889/pyobjc_framework_healthkit-11.0.tar.gz", hash = "sha256:e78ccb05f747ae3e70b5d73522030b7ba01ef2d390155fba7d50c1c614ae241f", size = 201558 }
wheels = [
@@ -2971,8 +3006,8 @@ name = "pyobjc-framework-imagecapturecore"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/fe/db1fc3ffd784a9010070cd87a05d7fd2542c400395589341fab5970a01e1/pyobjc_framework_imagecapturecore-11.0.tar.gz", hash = "sha256:f5d185d8c8b564f8b4a815381bcdb424b10d203ba5bdf0fc887085e007df6f7a", size = 99935 }
wheels = [
@@ -2985,8 +3020,8 @@ name = "pyobjc-framework-inputmethodkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e7/e9/13d007285582e598903264a7d25cc6771a2a52d6c2a96a68fe91db0844fb/pyobjc_framework_inputmethodkit-11.0.tar.gz", hash = "sha256:86cd648bf98c4e777c884b7f69ebcafba84866740430d297645bf388eee6ce52", size = 26684 }
wheels = [
@@ -2999,8 +3034,8 @@ name = "pyobjc-framework-installerplugins"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f2/f3/0379655e8ea3566002768d5e7b3ccd72ca845390632a8dabf801348af3a7/pyobjc_framework_installerplugins-11.0.tar.gz", hash = "sha256:88ec84e6999e8b2df874758b09878504a4fbfc8471cf3cd589d57e556f5b916e", size = 27687 }
wheels = [
@@ -3013,9 +3048,9 @@ name = "pyobjc-framework-instantmessage"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/08/4d/6810a1f2039ff24d9498858b3ebb46357d4091aa5cec9ff4e41bbcdb25de/pyobjc_framework_instantmessage-11.0.tar.gz", hash = "sha256:ec5c4c70c9b0e61ae82888067246e4f931e700d625b3c42604e54759d4fbf65c", size = 34027 }
wheels = [
@@ -3028,8 +3063,8 @@ name = "pyobjc-framework-intents"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/56/88/07e47b0c5c46fe97c23c883ae7a053c2ca6f6fd6afe851d1c2c784644f0f/pyobjc_framework_intents-11.0.tar.gz", hash = "sha256:6405c816dfed8ffa8b3f8b0fae75f61d64787dbae8db1c475bb4450cf8fdf6b5", size = 447921 }
wheels = [
@@ -3042,8 +3077,8 @@ name = "pyobjc-framework-intentsui"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-intents" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-intents", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ee/96/3b3b367f70a4d0a60d2c6251e4a1f4bf470945ae939e0ba20e6d56d10c7a/pyobjc_framework_intentsui-11.0.tar.gz", hash = "sha256:4ce04f926c823fbc1fba7d9c5b33d512b514396719e6bc50ef65b82774e42bc5", size = 20774 }
wheels = [
@@ -3056,8 +3091,8 @@ name = "pyobjc-framework-iobluetooth"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1e/46/62913f8e5ac307b154b3dd50a7a0b167c9d7ac2a579223e33208c141c387/pyobjc_framework_iobluetooth-11.0.tar.gz", hash = "sha256:869f01f573482da92674abbae4a154143e993b1fe4b2c3523f9e0f9c48b798d4", size = 300463 }
wheels = [
@@ -3070,8 +3105,8 @@ name = "pyobjc-framework-iobluetoothui"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-iobluetooth" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-iobluetooth", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/55/d194de8cfa63c96970e6c90c35e80ce3fceb42934a85d3728736a0e416ff/pyobjc_framework_iobluetoothui-11.0.tar.gz", hash = "sha256:a583758d3e54149ee2dcf00374685aa99e8ae407e044f7c378acc002f9f27e63", size = 23091 }
wheels = [
@@ -3084,8 +3119,8 @@ name = "pyobjc-framework-iosurface"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fb/91/ae9ca9e1a777eb786d9d43649437d01d24386736cffe9bb2f504b57e8db6/pyobjc_framework_iosurface-11.0.tar.gz", hash = "sha256:24da8d1cf9356717b1c7e75a1c61e9a9417b62f051d13423a4a7b0978d3dcda5", size = 20555 }
wheels = [
@@ -3098,8 +3133,8 @@ name = "pyobjc-framework-ituneslibrary"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/41/fe/881ab1058d795fe68ccc1e14df0d5e161601dced15d3be84105ecc44bae6/pyobjc_framework_ituneslibrary-11.0.tar.gz", hash = "sha256:2e15dcfbb9d5e95634ddff153de159a28f5879f1a13fdf95504e011773056c6e", size = 47647 }
wheels = [
@@ -3112,8 +3147,8 @@ name = "pyobjc-framework-kernelmanagement"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4a/ea/8ef534fce78817fc577f18de2b34e363873f785894f2bbbfc694823f5088/pyobjc_framework_kernelmanagement-11.0.tar.gz", hash = "sha256:812479d5f85eae27aeeaa22f64c20b926b28b5b9b2bf31c8eab9496d3e038028", size = 12794 }
wheels = [
@@ -3126,8 +3161,8 @@ name = "pyobjc-framework-latentsemanticmapping"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/42/29/8838eefeb82da95931134b06624364812dedf7e9cc905f36d95d497f2904/pyobjc_framework_latentsemanticmapping-11.0.tar.gz", hash = "sha256:6f578c3e0a171706bdbfcfc2c572a8059bf8039d22c1475df13583749a35cec1", size = 17704 }
wheels = [
@@ -3140,8 +3175,8 @@ name = "pyobjc-framework-launchservices"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-coreservices" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/da/59/eb847389224c670c885ae3d008b1ffe3b996bbe094b43e49dfa84f3947a9/pyobjc_framework_launchservices-11.0.tar.gz", hash = "sha256:7c5c8a8cec013e2cb3fa82a167ca2d61505c36a79f75c718f3f913e597f9ffee", size = 20691 }
wheels = [
@@ -3154,8 +3189,8 @@ name = "pyobjc-framework-libdispatch"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ab/33/4ec96a9edd37948f09e94635852c2db695141430cc1adc7b25968e1f3a95/pyobjc_framework_libdispatch-11.0.tar.gz", hash = "sha256:d22df11b07b1c3c8e7cfc4ba9e876a95c19f44acd36cf13d40c5cccc1ffda04b", size = 53496 }
wheels = [
@@ -3168,8 +3203,8 @@ name = "pyobjc-framework-libxpc"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/7e/9fa73ce6925db9cfd8a6b45d97943af8fe59f92251e7fd201b6e4608c172/pyobjc_framework_libxpc-11.0.tar.gz", hash = "sha256:e0c336913ab6a526b036915aa9038de2a5281e696ac2d3db3347b3040519c11d", size = 48627 }
wheels = [
@@ -3182,9 +3217,9 @@ name = "pyobjc-framework-linkpresentation"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/5c/dac9fe4ad0a4076c863b5ac9925e751fc18c637ae411e4891c4b7558a5b3/pyobjc_framework_linkpresentation-11.0.tar.gz", hash = "sha256:bc4ace4aab4da4a4e4df10517bd478b6d51ebf00b423268ee8d9f356f9e87be9", size = 15231 }
wheels = [
@@ -3197,9 +3232,9 @@ name = "pyobjc-framework-localauthentication"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-security" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ec/b1/bea4b5f8adbb69c0b34eddee63e052f35271cc630db43fbef6873352e21f/pyobjc_framework_localauthentication-11.0.tar.gz", hash = "sha256:eb55a3de647894092d6ed3f8f13fdc38e5dbf4850be320ea14dd2ac83176b298", size = 40020 }
wheels = [
@@ -3212,9 +3247,9 @@ name = "pyobjc-framework-localauthenticationembeddedui"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-localauthentication" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-localauthentication", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e1/ee/821f2d2e9da4cba3dc47e50c8367c6405e91551fb7d8ec842858d5b1d45d/pyobjc_framework_localauthenticationembeddedui-11.0.tar.gz", hash = "sha256:7e9bf6df77ff12a4e827988d8578c15b4431694b2fcfd5b0dad5d7738757ee6a", size = 14204 }
wheels = [
@@ -3227,8 +3262,8 @@ name = "pyobjc-framework-mailkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/79/9c9140f726ba14898762ddc19e7142724e0ce5930f08eb20f33f78b05be8/pyobjc_framework_mailkit-11.0.tar.gz", hash = "sha256:d08a2dcc95b5e7955c7c385fe6e018325113d02c007c4178d3fb3c9ab326c163", size = 32274 }
wheels = [
@@ -3241,10 +3276,10 @@ name = "pyobjc-framework-mapkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-corelocation" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-corelocation", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/7e/ef86c6e218a58bb9497ce9754a77f12ffe01c4b3609279727b7d7e44655a/pyobjc_framework_mapkit-11.0.tar.gz", hash = "sha256:cd8a91df4c0b442fcf1b14d735e566a06b21b3f48a2a4afe269fca45bfa49117", size = 165080 }
wheels = [
@@ -3257,8 +3292,8 @@ name = "pyobjc-framework-mediaaccessibility"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/81/8e/9fe2cb251ff6107a03bafa07f63b6593df145a2579fffb096023fb21b167/pyobjc_framework_mediaaccessibility-11.0.tar.gz", hash = "sha256:1298cc0128e1c0724e8f8e63a6167ea6809a985922c67399b997f8243de59ab4", size = 18671 }
wheels = [
@@ -3271,10 +3306,10 @@ name = "pyobjc-framework-mediaextension"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-avfoundation" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coremedia" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/18/1f/e31d9431bc71077b09583ea863b3c91b7de9371d0cc17a8be99be8119daa/pyobjc_framework_mediaextension-11.0.tar.gz", hash = "sha256:ecd8a64939e1c16be005690117c21fd406fc04d3036e2adea7600d2a0c53f4ea", size = 57931 }
wheels = [
@@ -3287,9 +3322,9 @@ name = "pyobjc-framework-medialibrary"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/a4/8c7d1635994800dc412a5db2c4b43ed499184651efcec0c8da3cf8e2bcc7/pyobjc_framework_medialibrary-11.0.tar.gz", hash = "sha256:692889fab1e479a9c207f0ff23c900dad5f47caf47c05cc995d9bb7c1e56e8b9", size = 18975 }
wheels = [
@@ -3302,8 +3337,8 @@ name = "pyobjc-framework-mediaplayer"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-avfoundation" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/3d2783f2f96ddf51bebcf6537a4a0f2a8a1fe4e520de218fc1b7c5b219ed/pyobjc_framework_mediaplayer-11.0.tar.gz", hash = "sha256:c61be0ba6c648db6b1d013a52f9afb8901a8d7fbabd983df2175c1b1fbff81e5", size = 94020 }
wheels = [
@@ -3316,8 +3351,8 @@ name = "pyobjc-framework-mediatoolbox"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/da/46/cf5f3bde6cad32f10095850ca44f24ba241d18b26379187c412be1260f39/pyobjc_framework_mediatoolbox-11.0.tar.gz", hash = "sha256:de949a44f10b5a15e5a7131ee53b2806b8cb753fd01a955970ec0f475952ba24", size = 23067 }
wheels = [
@@ -3330,8 +3365,8 @@ name = "pyobjc-framework-metal"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/77/e0/a6d18a1183410a5d8610ca1ae6c065b8944586441f8669faee7509817246/pyobjc_framework_metal-11.0.tar.gz", hash = "sha256:cad390150aa63502d5cfe242026b55ed39ffaf816342ddf51e44a9aead6c24be", size = 446102 }
wheels = [
@@ -3344,8 +3379,8 @@ name = "pyobjc-framework-metalfx"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-metal" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/cf/ff9367e4737a12ebd12a17e693ec247028cf065761acc073ebefb2b2393a/pyobjc_framework_metalfx-11.0.tar.gz", hash = "sha256:2ae41991bf7a733c44fcd5b6550cedea3accaaf0f529643975d3da113c9f0caa", size = 26436 }
wheels = [
@@ -3358,9 +3393,9 @@ name = "pyobjc-framework-metalkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-metal" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/92/27/fb3c1b10914abf2ae6682837abf76bcd8cb7af2ba613fbc55fb9d055bb95/pyobjc_framework_metalkit-11.0.tar.gz", hash = "sha256:1bbbe35c7c6a481383d32f6eaae59a1cd8084319a65c1aa343d63a257d8b4ddb", size = 44628 }
wheels = [
@@ -3373,8 +3408,8 @@ name = "pyobjc-framework-metalperformanceshaders"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-metal" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/14/c2/c08996a8c6cfef09fb9e726cc99b0bf3ad0ffcef66d5c2543e6b35dd4e2e/pyobjc_framework_metalperformanceshaders-11.0.tar.gz", hash = "sha256:41179e3a11e55325153fffd84f48946d47c1dc1944677febd871a127021e056d", size = 301444 }
wheels = [
@@ -3387,8 +3422,8 @@ name = "pyobjc-framework-metalperformanceshadersgraph"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-metalperformanceshaders" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-metalperformanceshaders", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b5/b8/353852c76eb437e907ca0acf8a5b5f9255e9b9ee8c0706b69b0c17498f97/pyobjc_framework_metalperformanceshadersgraph-11.0.tar.gz", hash = "sha256:33077ebbbe1aa7787de2552a83534be6c439d7f4272de17915a85fda8fd3b72d", size = 105381 }
wheels = [
@@ -3401,8 +3436,8 @@ name = "pyobjc-framework-metrickit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/28/82/605ad654f40ff4480ba9366ad3726da80c98e33b73f122fb91259be1ce81/pyobjc_framework_metrickit-11.0.tar.gz", hash = "sha256:ee3da403863beec181a2d6dc7b7eeb4d07e954b88bbabac58a82523b2f83fdc7", size = 40414 }
wheels = [
@@ -3415,8 +3450,8 @@ name = "pyobjc-framework-mlcompute"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c5/c9/22fe4720685724ec1444c8e5cdb41d360b1434d0971fb3e43cf3e9bf51fd/pyobjc_framework_mlcompute-11.0.tar.gz", hash = "sha256:1a1ee9ab43d1824300055ff94b042a26f38f1d18f6f0aa08be1c88278e7284d9", size = 89265 }
wheels = [
@@ -3429,9 +3464,9 @@ name = "pyobjc-framework-modelio"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ca/7c/b75b84d41e7854ffe9c9a42846f8105227a5fd0b02b690b4a75018b2caa3/pyobjc_framework_modelio-11.0.tar.gz", hash = "sha256:c875eb6ff7f94d18362a00faaa3016ae0c28140326338d18aa03c0b62f1c6b9d", size = 122652 }
wheels = [
@@ -3444,8 +3479,8 @@ name = "pyobjc-framework-multipeerconnectivity"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/14/80/4137cb9751aa3846c4954b3e61f948aae17afeb6851e01194aa50683caef/pyobjc_framework_multipeerconnectivity-11.0.tar.gz", hash = "sha256:8278a3483c0b6b88a8888ca76c46fd85808f9df56d45708cbc4e4182a5565cd3", size = 25534 }
wheels = [
@@ -3458,8 +3493,8 @@ name = "pyobjc-framework-naturallanguage"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/62/64/63e97635fa637384bc8c980796573dc7a9e7074a6866aef073b1faf3e11d/pyobjc_framework_naturallanguage-11.0.tar.gz", hash = "sha256:4c9471fa2c48a8fd4899de4406823e66cb0292dbba7b471622017f3647d53fa4", size = 46385 }
wheels = [
@@ -3472,8 +3507,8 @@ name = "pyobjc-framework-netfs"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c7/29/eb569870b52c7581104ed2806cae2d425d60b5ab304128cd58155d5b567f/pyobjc_framework_netfs-11.0.tar.gz", hash = "sha256:3de5f627a62addf4aab8a4d2d07213e9b2b6c8adbe6cc4c332ee868075785a6a", size = 16173 }
wheels = [
@@ -3486,8 +3521,8 @@ name = "pyobjc-framework-network"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/78/8e/18e55aff83549e041484d2ee94dd91b29cec9de40508e7fe9c4afec110a7/pyobjc_framework_network-11.0.tar.gz", hash = "sha256:d4dcc02773d7d642a385c7f0d951aeb7361277446c912a49230cddab60a65ab8", size = 124160 }
wheels = [
@@ -3500,8 +3535,8 @@ name = "pyobjc-framework-networkextension"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/59/90/97dcfac5895b07e891adf634c3a074b68992d132ccfab386c186ac1a598c/pyobjc_framework_networkextension-11.0.tar.gz", hash = "sha256:5ba2254e2c13010b6c4f1e2948047d95eff86bfddfc77716747718fa3a8cb1af", size = 188551 }
wheels = [
@@ -3514,8 +3549,8 @@ name = "pyobjc-framework-notificationcenter"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d7/d0/f0a602e01173531a2b639e283a092cf1f307fd873abd2ed590b9c4122337/pyobjc_framework_notificationcenter-11.0.tar.gz", hash = "sha256:f878b318c693d63d6b8bd1c3e2ad4f8097b22872f18f40142e394d84f1ead9f6", size = 22844 }
wheels = [
@@ -3528,8 +3563,8 @@ name = "pyobjc-framework-opendirectory"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/cf/ba0cf807758acdc6a19e4787fdcda2eb59034aa22c4203d04fd49b276981/pyobjc_framework_opendirectory-11.0.tar.gz", hash = "sha256:0c82594f4f0bcf2318c4641527f9243962d7b03e67d4f3fb111b899a299fc7eb", size = 189165 }
wheels = [
@@ -3542,8 +3577,8 @@ name = "pyobjc-framework-osakit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d3/4a/e49680f7f3ab9c0632ed9be76a0a59299e7fd797335690b3da4d117f2d7b/pyobjc_framework_osakit-11.0.tar.gz", hash = "sha256:77ac18e2660133a9eeb01c76ad3df3b4b36fd29005fc36bca00f57cca121aac3", size = 22535 }
wheels = [
@@ -3556,10 +3591,10 @@ name = "pyobjc-framework-oslog"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coremedia" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b0/93/0a72353d0212a815bd5e43aec528ce7b28b71d461d26e5fa3882ff96ffa3/pyobjc_framework_oslog-11.0.tar.gz", hash = "sha256:9d29eb7c89a41d7c702dffb6e2e338a2d5219387c8dae22b67754ddf9e2fcb3f", size = 24151 }
wheels = [
@@ -3572,8 +3607,8 @@ name = "pyobjc-framework-passkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cb/f8/ebb2bc840f87292a4f60080463ee698ca08516cc958364741dfff2858b33/pyobjc_framework_passkit-11.0.tar.gz", hash = "sha256:2044d9d634dd98b7b624ee09487b27e5f26a7729f6689abba23a4a011febe19c", size = 120495 }
wheels = [
@@ -3586,8 +3621,8 @@ name = "pyobjc-framework-pencilkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f4/8d/1e97cd72b776e5e1294cbda84325b364702617dd435d32448dcc0a80bd93/pyobjc_framework_pencilkit-11.0.tar.gz", hash = "sha256:9598c28e83f5b7f091592cc1af2b16f7ae94cf00045d8d14ed2c17cb9e4ffd50", size = 22812 }
wheels = [
@@ -3600,8 +3635,8 @@ name = "pyobjc-framework-phase"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-avfoundation" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d2/a2/65182dcb44fceb2173f4134d6cd4325dfd0731225b621aa2027d2a03d043/pyobjc_framework_phase-11.0.tar.gz", hash = "sha256:e06a0f8308ae4f3731f88b3e1239b7bdfdda3eef97023e3ce972e2f386451d80", size = 59214 }
wheels = [
@@ -3614,8 +3649,8 @@ name = "pyobjc-framework-photos"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f7/c3/fc755c1f8f411433d7ba2e92f3fe3e7b417e9629675ad6baf94ac8b01e64/pyobjc_framework_photos-11.0.tar.gz", hash = "sha256:cfdfdefb0d560b091425227d5c0e24a40b445b5251ff4d37bd326cd8626b80cd", size = 92122 }
wheels = [
@@ -3628,8 +3663,8 @@ name = "pyobjc-framework-photosui"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/2c/70ac99fb2b7ba14d220c78cf6401c0c7a47992269f85f699220a6a2cff09/pyobjc_framework_photosui-11.0.tar.gz", hash = "sha256:3c65342e31f6109d8229992b2712b29cab1021475969b55f4f215dd97e2a99db", size = 47898 }
wheels = [
@@ -3642,8 +3677,8 @@ name = "pyobjc-framework-preferencepanes"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/35/01/81cc46e0a92d15f2b664b2efdcc8fd310acac570c9f63a99d446e0489784/pyobjc_framework_preferencepanes-11.0.tar.gz", hash = "sha256:ee000c351befeb81f4fa678ada85695ca4af07933b6bd9b1947164e16dd0b3e5", size = 26419 }
wheels = [
@@ -3656,8 +3691,8 @@ name = "pyobjc-framework-pushkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/17/ab/7fe55ce5b32c434142be026ec27b1801a2d4694b159b502f9ecd568eebf2/pyobjc_framework_pushkit-11.0.tar.gz", hash = "sha256:df9854ed4065c50022863b3c11c2a21c4279b36c2b5c8f08b834174aacb44e81", size = 20816 }
wheels = [
@@ -3670,8 +3705,8 @@ name = "pyobjc-framework-quartz"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a5/ad/f00f3f53387c23bbf4e0bb1410e11978cbf87c82fa6baff0ee86f74c5fb6/pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619", size = 3952463 }
wheels = [
@@ -3684,9 +3719,9 @@ name = "pyobjc-framework-quicklookthumbnailing"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/a1/35ca40d2d4ab05acbc9766986d482482d466529003711c7b4e52a8df4935/pyobjc_framework_quicklookthumbnailing-11.0.tar.gz", hash = "sha256:40763284bd0f71e6a55803f5234ad9cd8e8dd3aaaf5e1fd204e6c952b3f3530d", size = 16784 }
wheels = [
@@ -3699,8 +3734,8 @@ name = "pyobjc-framework-replaykit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/aa/43/c751c517dbb8ee599a31e59832c01080473c7964b6996ca29906f46c0967/pyobjc_framework_replaykit-11.0.tar.gz", hash = "sha256:e5693589423eb9ad99d63a7395169f97b484a58108321877b0fc27c748344593", size = 25589 }
wheels = [
@@ -3713,8 +3748,8 @@ name = "pyobjc-framework-safariservices"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/40/ec/c9a97b1aa713145cc8c522c4146af06b293cfe1a959a03ee91007949533b/pyobjc_framework_safariservices-11.0.tar.gz", hash = "sha256:dba416bd0ed5f4481bc400bf56ce57e982c19feaae94bc4eb75d8bda9af15b7e", size = 34367 }
wheels = [
@@ -3727,9 +3762,9 @@ name = "pyobjc-framework-safetykit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4e/30/89bfdbdca93e57b19891ddeff1742b20a2019cdeb2e44902027dce2642e1/pyobjc_framework_safetykit-11.0.tar.gz", hash = "sha256:9ec996a6a8eecada4b9fd1138244bcffea96a37722531f0ec16566049dfd4cdb", size = 20745 }
wheels = [
@@ -3742,9 +3777,9 @@ name = "pyobjc-framework-scenekit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/26/3f/a2761585399e752bce8275c9d56990d4b83e57b13d06dd98335891176a89/pyobjc_framework_scenekit-11.0.tar.gz", hash = "sha256:c0f37019f8de2a583f66e6d14dfd4ae23c8d8703e93f61c1c91728a21f62cd26", size = 213647 }
wheels = [
@@ -3757,9 +3792,9 @@ name = "pyobjc-framework-screencapturekit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coremedia" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/77/90/71f10db2f52ea324f82eaccc959442c43d21778cc5b1294c29e1942e635c/pyobjc_framework_screencapturekit-11.0.tar.gz", hash = "sha256:ca2c960e28216e56f33e4ca9b9b1eda12d9c17b719bae727181e8b96f0314c4b", size = 53046 }
wheels = [
@@ -3772,8 +3807,8 @@ name = "pyobjc-framework-screensaver"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f6/b6/71c20259a1bfffcb5103be62564006b1bbc21f80180658101e2370683bcb/pyobjc_framework_screensaver-11.0.tar.gz", hash = "sha256:2e4c643624cc0cffeafc535c43faf5f8de8be030307fa8a5bea257845e8af474", size = 23774 }
wheels = [
@@ -3786,8 +3821,8 @@ name = "pyobjc-framework-screentime"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/42/a7/ee60ee5b0471a4367eaa1c8a243418874fd48fac5dbdfdd318a653d94aaa/pyobjc_framework_screentime-11.0.tar.gz", hash = "sha256:6dd74dc64be1865346fcff63b8849253697f7ac68d83ee2708019cf3852c1cd7", size = 14398 }
wheels = [
@@ -3800,8 +3835,8 @@ name = "pyobjc-framework-scriptingbridge"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/592af19047935e44c07ddd1eba4f05aa8eb460ee842f7d5d48501231cd69/pyobjc_framework_scriptingbridge-11.0.tar.gz", hash = "sha256:65e5edd0ea608ae7f01808b963dfa25743315f563705d75c493c2fa7032f88cc", size = 22626 }
wheels = [
@@ -3814,8 +3849,8 @@ name = "pyobjc-framework-searchkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-coreservices" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/15/27/9676327cf7d13346d546325b411a5deaa072bd0fbe733c8aae8a9a00c0e0/pyobjc_framework_searchkit-11.0.tar.gz", hash = "sha256:36f3109e74bc5e6fab60c02be804d5ed1c511ad51ea0d597a6c6a9653573ddf5", size = 31182 }
wheels = [
@@ -3828,8 +3863,8 @@ name = "pyobjc-framework-security"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c5/75/4b916bff8c650e387077a35916b7a7d331d5ff03bed7275099d96dcc6cd9/pyobjc_framework_security-11.0.tar.gz", hash = "sha256:ac078bb9cc6762d6f0f25f68325dcd7fe77acdd8c364bf4378868493f06a0758", size = 347059 }
wheels = [
@@ -3842,9 +3877,9 @@ name = "pyobjc-framework-securityfoundation"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-security" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/84/d6/0d817edb11d2bdb0f536059e913191e587f1984e39397bb3341209d92c21/pyobjc_framework_securityfoundation-11.0.tar.gz", hash = "sha256:5ae906ded5dd40046c013a7e0c1f59416abafb4b72bc947b6cd259749745e637", size = 13526 }
wheels = [
@@ -3857,9 +3892,9 @@ name = "pyobjc-framework-securityinterface"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-security" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/88/d7c4942650707fe5b1d3b45b42684f58f2cab7d2772ec74ca96ecef575eb/pyobjc_framework_securityinterface-11.0.tar.gz", hash = "sha256:8843a27cf30a8e4dd6e2cb7702a6d65ad4222429f0ccc6c062537af4683b1c08", size = 37118 }
wheels = [
@@ -3872,9 +3907,9 @@ name = "pyobjc-framework-sensitivecontentanalysis"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/00/e4/f1e0f150ae6c6ad7dde9b248f34f324f4f8b1c42260dbf62420f80d79ba9/pyobjc_framework_sensitivecontentanalysis-11.0.tar.gz", hash = "sha256:0f09034688f894c0f4409c16adaf857d78714d55472de4aa2ac40fbd7ba233d6", size = 13060 }
wheels = [
@@ -3887,8 +3922,8 @@ name = "pyobjc-framework-servicemanagement"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1b/59/8d38b5cdbcfb57ab842e080436dbd04d5a5d2080e99a2ea1e286cfad12a8/pyobjc_framework_servicemanagement-11.0.tar.gz", hash = "sha256:10b1bbcee3de5bb2b9fc3d6763eb682b7a1d9ddd4bd2c882fece62783cb17885", size = 16882 }
wheels = [
@@ -3901,8 +3936,8 @@ name = "pyobjc-framework-sharedwithyou"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-sharedwithyoucore" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-sharedwithyoucore", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/20/84/db667061f815537717a6cac891df01a45b65e6feaa2dfa0c9d2e3803a1ef/pyobjc_framework_sharedwithyou-11.0.tar.gz", hash = "sha256:a3a03daac77ad7364ed22109ca90c6cd2dcb7611a96cbdf37d30543ef1579399", size = 33696 }
wheels = [
@@ -3915,8 +3950,8 @@ name = "pyobjc-framework-sharedwithyoucore"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/52/2a/86904cd9cc3bf5cdb9101481e17e67358f39f81ffa0f36768097287e34b3/pyobjc_framework_sharedwithyoucore-11.0.tar.gz", hash = "sha256:3932452677df5d67ea27845ab26ccaaa1d1779196bf16b62c5655f13d822c82d", size = 28877 }
wheels = [
@@ -3929,8 +3964,8 @@ name = "pyobjc-framework-shazamkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/dd/2a/1f4ad92260860e500cb61119e8e7fe604b0788c32f5b00446b5a56705a2b/pyobjc_framework_shazamkit-11.0.tar.gz", hash = "sha256:cea736cefe90b6bb989d0a8abdc21ef4b3b431b27657abb09d6deb0b2c1bd37a", size = 25172 }
wheels = [
@@ -3943,8 +3978,8 @@ name = "pyobjc-framework-social"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6f/56/ed483f85105ef929241ab1a6ed3dbfd0be558bb900e36b274f997db9c869/pyobjc_framework_social-11.0.tar.gz", hash = "sha256:ccedd6eddb6744049467bce19b4ec4f0667ec60552731c02dcbfa8938a3ac798", size = 14806 }
wheels = [
@@ -3957,8 +3992,8 @@ name = "pyobjc-framework-soundanalysis"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9a/14/697ca1b76228a96bb459f3cf43234798b05fdf11691202449d98d9d887af/pyobjc_framework_soundanalysis-11.0.tar.gz", hash = "sha256:f541fcd04ec5d7528dd2ae2d873a92a3092e87fb70b8df229c79defb4d807d1a", size = 16789 }
wheels = [
@@ -3971,8 +4006,8 @@ name = "pyobjc-framework-speech"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5f/39/e9f0a73243c38d85f8da6a1a2afda73503e2fcc31a72f5479770bceae0c1/pyobjc_framework_speech-11.0.tar.gz", hash = "sha256:92a191c3ecfe7032eea2140ab5dda826a59c7bb84b13a2edb0ebc471a76e6d7b", size = 40620 }
wheels = [
@@ -3985,9 +4020,9 @@ name = "pyobjc-framework-spritekit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b7/6e/642e64f5b62a7777c784931c7f018788b5620e307907d416c837fd0c4315/pyobjc_framework_spritekit-11.0.tar.gz", hash = "sha256:aa43927e325d4ac253b7c0ec4df95393b0354bd278ebe9871803419d12d1ef80", size = 129851 }
wheels = [
@@ -4000,8 +4035,8 @@ name = "pyobjc-framework-storekit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/69/ca/f4e5a1ff8c98bbbf208639b2bef7bf3b88936bccda1d8ed34aa7d052f589/pyobjc_framework_storekit-11.0.tar.gz", hash = "sha256:ef7e75b28f1fa8b0b6413e64b9d5d78b8ca358fc2477483d2783f688ff8d75e0", size = 75855 }
wheels = [
@@ -4014,8 +4049,8 @@ name = "pyobjc-framework-symbols"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/dc/92/a20a3d7af3c99e0ea086e43715675160a04b86c1d069bdaeb3acdb015d92/pyobjc_framework_symbols-11.0.tar.gz", hash = "sha256:e3de7736dfb8107f515cfd23f03e874dd9468e88ab076d01d922a73fefb620fa", size = 13682 }
wheels = [
@@ -4028,9 +4063,9 @@ name = "pyobjc-framework-syncservices"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coredata" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5a/22/642186906f672461bab1d7773b35ef74e432b9789ca2248186b766e9fd3b/pyobjc_framework_syncservices-11.0.tar.gz", hash = "sha256:7867c23895a8289da8d56e962c144c36ed16bd101dc07d05281c55930b142471", size = 57453 }
wheels = [
@@ -4043,8 +4078,8 @@ name = "pyobjc-framework-systemconfiguration"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/70/70/ebebf311523f436df2407f35d7ce62482c01e530b77aceb3ca6356dcef43/pyobjc_framework_systemconfiguration-11.0.tar.gz", hash = "sha256:06487f0fdd43c6447b5fd3d7f3f59826178d32bcf74f848c5b3ea597191d471d", size = 142949 }
wheels = [
@@ -4057,8 +4092,8 @@ name = "pyobjc-framework-systemextensions"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/62/4b/904d818debf6216b7be009d492d998c819bf2f2791bfb75870a952e32cf9/pyobjc_framework_systemextensions-11.0.tar.gz", hash = "sha256:da293c99b428fb7f18a7a1d311b17177f73a20c7ffa94de3f72d760df924255e", size = 22531 }
wheels = [
@@ -4071,8 +4106,8 @@ name = "pyobjc-framework-threadnetwork"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c4/17/fc8fde4eeb6697e0a5ba1a306cd62d3a95b53f3334744cd22b87037d8a14/pyobjc_framework_threadnetwork-11.0.tar.gz", hash = "sha256:f5713579380f6fb89c877796de86cb4e98428d7a9cbfebe566fb827ba23b2d8e", size = 13820 }
wheels = [
@@ -4085,8 +4120,8 @@ name = "pyobjc-framework-uniformtypeidentifiers"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/56/4f/fd571c1f87d5ee3d86c4d2008806e9623d2662bbc788d9001b3fff35275f/pyobjc_framework_uniformtypeidentifiers-11.0.tar.gz", hash = "sha256:6ae6927a3ed1f0197a8c472226f11f46ccd5ed398b4449613e1d10346d9ed15d", size = 20860 }
wheels = [
@@ -4099,8 +4134,8 @@ name = "pyobjc-framework-usernotifications"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/78/f5/ca3e6a7d940b3aca4323e4f5409b14b5d2eb45432158430c584e3800ce4d/pyobjc_framework_usernotifications-11.0.tar.gz", hash = "sha256:7950a1c6a8297f006c26c3d286705ffc2a07061d6e844f1106290572097b872c", size = 54857 }
wheels = [
@@ -4113,9 +4148,9 @@ name = "pyobjc-framework-usernotificationsui"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-usernotifications" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-usernotifications", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e9/e8/f0d50cdc678260a628b92e55b5752155f941c2f72b96fe3f2412a28c5d79/pyobjc_framework_usernotificationsui-11.0.tar.gz", hash = "sha256:d0ec597d189b4d228b0b836474aef318652c1c287b33442a1403c49dc59fdb7f", size = 14369 }
wheels = [
@@ -4128,8 +4163,8 @@ name = "pyobjc-framework-videosubscriberaccount"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7e/2e/6a7debd84911a9384b4e7a9cc3f308e3461a00a9d74f33b153bdd872f15f/pyobjc_framework_videosubscriberaccount-11.0.tar.gz", hash = "sha256:163b32f361f48b9d20f317461464abd4427b3242693ae011633fc443c7d5449c", size = 29100 }
wheels = [
@@ -4142,10 +4177,10 @@ name = "pyobjc-framework-videotoolbox"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coremedia" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ba/2d/c031a132b142fcd20846cc1ac3ba92abaa58ec04164fd36ca978d9374f1c/pyobjc_framework_videotoolbox-11.0.tar.gz", hash = "sha256:a54ed8f8bcbdd2bdea2a296dc02a8a7d42f81e2b6ccbf4d1f10cec5e7a09bec0", size = 81157 }
wheels = [
@@ -4158,8 +4193,8 @@ name = "pyobjc-framework-virtualization"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/65/8d/e57e1f2c5ac950dc3da6c977effde4a55b8b70424b1bdb97b5530559f5bc/pyobjc_framework_virtualization-11.0.tar.gz", hash = "sha256:03e1c1fa20950aa7c275e5f11f1257108b6d1c6a7403afb86f4e9d5fae87b73c", size = 78144 }
wheels = [
@@ -4172,10 +4207,10 @@ name = "pyobjc-framework-vision"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
- { name = "pyobjc-framework-coreml" },
- { name = "pyobjc-framework-quartz" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-coreml", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ef/53/dc2e0562a177af9306efceb84bc21f5cf7470acaa8f28f64e62bf828b7e1/pyobjc_framework_vision-11.0.tar.gz", hash = "sha256:45342e5253c306dbcd056a68bff04ffbfa00e9ac300a02aabf2e81053b771e39", size = 133175 }
wheels = [
@@ -4188,8 +4223,8 @@ name = "pyobjc-framework-webkit"
version = "11.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pyobjc-core" },
- { name = "pyobjc-framework-cocoa" },
+ { name = "pyobjc-core", marker = "sys_platform == 'darwin'" },
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/79/4f/02a6270acf225c2a34339677e796002c77506238475059ae6e855358a40c/pyobjc_framework_webkit-11.0.tar.gz", hash = "sha256:fa6bedf9873786b3376a74ce2ea9dcd311f2a80f61e33dcbd931cc956aa29644", size = 767210 }
wheels = [
@@ -4202,9 +4237,9 @@ name = "pyopencl"
version = "2025.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "numpy" },
- { name = "platformdirs" },
- { name = "pytools" },
+ { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "pytools", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510 }
wheels = [
@@ -4420,7 +4455,7 @@ name = "python-xlib"
version = "0.33"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "six" },
+ { name = "six", marker = "sys_platform != 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068 }
wheels = [
@@ -4438,9 +4473,9 @@ name = "pytools"
version = "2024.1.10"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "platformdirs" },
- { name = "siphash24" },
- { name = "typing-extensions" },
+ { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "siphash24", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
+ { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741 }
wheels = [
@@ -4769,7 +4804,7 @@ name = "shapely"
version = "2.0.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "numpy" },
+ { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/21/c0/a911d1fd765d07a2b6769ce155219a281bfbe311584ebe97340d75c5bdb1/shapely-2.0.7.tar.gz", hash = "sha256:28fe2997aab9a9dc026dc6a355d04e85841546b2a5d232ed953e3321ab958ee5", size = 283413 }
wheels = [
@@ -4994,7 +5029,7 @@ name = "yapf"
version = "0.43.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "platformdirs" },
+ { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907 }
wheels = [
From 18db927fc1c6289f66ce1b8a8569ccc0bb9dad21 Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Tue, 11 Mar 2025 14:58:05 -0700
Subject: [PATCH 16/68] test_models: convert can data to namedtuple (#34845)
* stash
* fastest
* faster but not fastest
* clean up
* here too
* fix that
* revert
* already sorted
* rev
* clean up
* allow empty
* lower tm time for cache miss
---
.github/workflows/selfdrive_tests.yaml | 2 +-
selfdrive/car/tests/test_models.py | 30 +++++++++----------
.../debug/check_can_parser_performance.py | 7 ++---
3 files changed, 17 insertions(+), 22 deletions(-)
diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml
index a91e9862ac..d9dc5dd455 100644
--- a/.github/workflows/selfdrive_tests.yaml
+++ b/.github/workflows/selfdrive_tests.yaml
@@ -227,7 +227,7 @@ jobs:
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Test car models
- timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.routes-cache.outputs.cache-hit == 'true') && 1 || 20 }}
+ timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.routes-cache.outputs.cache-hit == 'true') && 1 || 3 }}
run: |
${{ env.RUN }} "MAX_EXAMPLES=1 $PYTEST selfdrive/car/tests/test_models.py && \
chmod -R 777 /tmp/comma_download_cache"
diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py
index 7f0cf4a979..9a042ed372 100644
--- a/selfdrive/car/tests/test_models.py
+++ b/selfdrive/car/tests/test_models.py
@@ -1,5 +1,4 @@
import time
-import capnp
import os
import pytest
import random
@@ -67,7 +66,7 @@ class TestCarModelBase(unittest.TestCase):
platform: Platform | None = None
test_route: CarTestRoute | None = None
- can_msgs: list[capnp.lib.capnp._DynamicStructReader]
+ can_msgs: list[tuple[int, list[CanData]]]
fingerprint: dict[int, dict[int, int]]
elm_frame: int | None
car_safety_mode_frame: int | None
@@ -82,7 +81,8 @@ class TestCarModelBase(unittest.TestCase):
experimental_long = False
for msg in lr:
if msg.which() == "can":
- can_msgs.append(msg)
+ can = can_capnp_to_list((msg.as_builder().to_bytes(),))[0]
+ can_msgs.append((can[0], [CanData(*can) for can in can[1]]))
if len(can_msgs) <= FRAME_FINGERPRINT:
for m in msg.can:
if m.src < 64:
@@ -146,13 +146,11 @@ class TestCarModelBase(unittest.TestCase):
raise unittest.SkipTest
raise Exception(f"missing test route for {cls.platform}")
- car_fw, can_msgs, experimental_long = cls.get_testing_data()
+ car_fw, cls.can_msgs, experimental_long = cls.get_testing_data()
# if relay is expected to be open in the route
cls.openpilot_enabled = cls.car_safety_mode_frame is not None
- cls.can_msgs = sorted(can_msgs, key=lambda msg: msg.logMonoTime)
-
cls.CarInterface, cls.CarController, cls.CarState, cls.RadarInterface = interfaces[cls.platform]
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False)
assert cls.CP
@@ -199,8 +197,8 @@ class TestCarModelBase(unittest.TestCase):
CC = structs.CarControl().as_reader()
for i, msg in enumerate(self.can_msgs):
- CS = self.CI.update(can_capnp_to_list((msg.as_builder().to_bytes(),)))
- self.CI.apply(CC, msg.logMonoTime)
+ CS = self.CI.update(msg)
+ self.CI.apply(CC, msg[0])
if CS.canValid:
can_valid = True
@@ -219,7 +217,7 @@ class TestCarModelBase(unittest.TestCase):
# start parsing CAN messages after we've left ELM mode and can expect CAN traffic
error_cnt = 0
for i, msg in enumerate(self.can_msgs[self.elm_frame:]):
- rr: structs.RadarData | None = RI.update(can_capnp_to_list((msg.as_builder().to_bytes(),)))
+ rr: structs.RadarData | None = RI.update(msg)
if rr is not None and i > 50:
error_cnt += structs.RadarData.Error.canError in rr.errors
self.assertEqual(error_cnt, 0)
@@ -228,16 +226,16 @@ class TestCarModelBase(unittest.TestCase):
if self.CP.dashcamOnly:
self.skipTest("no need to check panda safety for dashcamOnly")
- start_ts = self.can_msgs[0].logMonoTime
+ start_ts = self.can_msgs[0][0]
failed_addrs = Counter()
for can in self.can_msgs:
# update panda timer
- t = (can.logMonoTime - start_ts) / 1e3
+ t = (can[0] - start_ts) / 1e3
self.safety.set_timer(int(t))
# run all msgs through the safety RX hook
- for msg in can.can:
+ for msg in can[1]:
if msg.src >= 64:
continue
@@ -389,8 +387,8 @@ class TestCarModelBase(unittest.TestCase):
# warm up pass, as initial states may be different
for can in self.can_msgs[:300]:
- self.CI.update(can_capnp_to_list((can.as_builder().to_bytes(), )))
- for msg in filter(lambda m: m.src in range(64), can.can):
+ self.CI.update(can)
+ for msg in filter(lambda m: m.src < 64, can[1]):
to_send = libsafety_py.make_CANPacket(msg.address, msg.src % 4, msg.dat)
self.safety.safety_rx_hook(to_send)
@@ -399,8 +397,8 @@ class TestCarModelBase(unittest.TestCase):
checks = defaultdict(int)
vehicle_speed_seen = self.CP.steerControlType == SteerControlType.angle and not self.CP.notCar
for idx, can in enumerate(self.can_msgs):
- CS = self.CI.update(can_capnp_to_list((can.as_builder().to_bytes(), ))).as_reader()
- for msg in filter(lambda m: m.src in range(64), can.can):
+ CS = self.CI.update(can).as_reader()
+ for msg in filter(lambda m: m.src < 64, can[1]):
to_send = libsafety_py.make_CANPacket(msg.address, msg.src % 4, msg.dat)
ret = self.safety.safety_rx_hook(to_send)
self.assertEqual(1, ret, f"safety rx failed ({ret=}): {(msg.address, msg.src % 4)}")
diff --git a/selfdrive/debug/check_can_parser_performance.py b/selfdrive/debug/check_can_parser_performance.py
index 61aff0244f..20987b3cf4 100755
--- a/selfdrive/debug/check_can_parser_performance.py
+++ b/selfdrive/debug/check_can_parser_performance.py
@@ -6,7 +6,6 @@ from tqdm import tqdm
from cereal import car
from opendbc.car.tests.routes import CarTestRoute
from openpilot.selfdrive.car.tests.test_models import TestCarModelBase
-from openpilot.selfdrive.pandad import can_capnp_to_list
from openpilot.tools.plotjuggler.juggle import DEMO_ROUTE
N_RUNS = 10
@@ -25,13 +24,11 @@ if __name__ == '__main__':
CC = car.CarControl.new_message()
ets = []
for _ in tqdm(range(N_RUNS)):
- msgs = [m.as_builder().to_bytes() for m in tm.can_msgs]
start_t = time.process_time_ns()
- for msg in msgs:
- can_list = can_capnp_to_list([msg])
+ for msg in tm.can_msgs:
for cp in tm.CI.can_parsers.values():
if cp is not None:
- cp.update_strings(can_list)
+ cp.update_strings(msg)
ets.append((time.process_time_ns() - start_t) * 1e-6)
print(f'{len(tm.can_msgs)} CAN packets, {N_RUNS} runs')
From 3f6fd3da78f07cac76238dda4102091d3ad9d37d Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Tue, 11 Mar 2025 16:03:00 -0700
Subject: [PATCH 17/68] test_onroad threshold tuning
---
selfdrive/test/test_onroad.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py
index 9756010614..1ea59c4856 100644
--- a/selfdrive/test/test_onroad.py
+++ b/selfdrive/test/test_onroad.py
@@ -391,7 +391,7 @@ class TestOnroad:
result += "----------------- Model Timing -----------------\n"
result += "------------------------------------------------\n"
cfgs = [
- ("modelV2", 0.045, 0.035),
+ ("modelV2", 0.045, 0.040), # TODO: this should be stricter but it's hard to measure exactly
("driverStateV2", 0.045, 0.035),
]
for (s, instant_max, avg_max) in cfgs:
From b732e8eee9a49b56e8f331067e8305e45e8bb2ec Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Wed, 12 Mar 2025 00:47:34 -0700
Subject: [PATCH 18/68] bump opendbc (#34848)
* bump
* bump
* adds EV flag
---
opendbc_repo | 2 +-
selfdrive/test/process_replay/ref_commit | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/opendbc_repo b/opendbc_repo
index 75d4e0eb5c..eca696984e 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit 75d4e0eb5c8fa961a665ec020eb816a50eed231c
+Subproject commit eca696984e467b909814ea1e594163ceea03ab3a
diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit
index 54df53fd91..cfd179fb79 100644
--- a/selfdrive/test/process_replay/ref_commit
+++ b/selfdrive/test/process_replay/ref_commit
@@ -1 +1 @@
-465979047295dad5da3a552db9227ed776a26a79
\ No newline at end of file
+37041a45841e83f0641ef1e87c0e567181d47172
\ No newline at end of file
From 4e469a2987ddfa7c7c82037e17026d6ad1132bbe Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Wed, 12 Mar 2025 00:48:49 -0700
Subject: [PATCH 19/68] raise timeout for forks
---
.github/workflows/selfdrive_tests.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml
index d9dc5dd455..67a76a4232 100644
--- a/.github/workflows/selfdrive_tests.yaml
+++ b/.github/workflows/selfdrive_tests.yaml
@@ -227,7 +227,7 @@ jobs:
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Test car models
- timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.routes-cache.outputs.cache-hit == 'true') && 1 || 3 }}
+ timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.routes-cache.outputs.cache-hit == 'true') && 1 || 6 }}
run: |
${{ env.RUN }} "MAX_EXAMPLES=1 $PYTEST selfdrive/car/tests/test_models.py && \
chmod -R 777 /tmp/comma_download_cache"
From 04aed6ba89dd088ad1fd04bf8339758774eeaf87 Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Wed, 12 Mar 2025 01:38:13 -0700
Subject: [PATCH 20/68] Firehose: fix plural is/are (#34849)
* use qt plurals
* remove old
---
selfdrive/ui/qt/offroad/firehose.cc | 2 +-
selfdrive/ui/translations/main_ar.ts | 15 +++++++++++----
selfdrive/ui/translations/main_de.ts | 11 +++++++----
selfdrive/ui/translations/main_en.ts | 10 ++++++++++
selfdrive/ui/translations/main_es.ts | 11 +++++++----
selfdrive/ui/translations/main_fr.ts | 11 +++++++----
selfdrive/ui/translations/main_ja.ts | 10 ++++++----
selfdrive/ui/translations/main_ko.ts | 10 ++++++----
selfdrive/ui/translations/main_pt-BR.ts | 11 +++++++----
selfdrive/ui/translations/main_th.ts | 10 ++++++----
selfdrive/ui/translations/main_tr.ts | 10 ++++++----
selfdrive/ui/translations/main_zh-CHS.ts | 10 ++++++----
selfdrive/ui/translations/main_zh-CHT.ts | 10 ++++++----
13 files changed, 86 insertions(+), 45 deletions(-)
diff --git a/selfdrive/ui/qt/offroad/firehose.cc b/selfdrive/ui/qt/offroad/firehose.cc
index fe42178a4c..2923dbffe5 100644
--- a/selfdrive/ui/qt/offroad/firehose.cc
+++ b/selfdrive/ui/qt/offroad/firehose.cc
@@ -88,7 +88,7 @@ FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent)
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
QJsonObject json = doc.object();
int count = json["firehose"].toInt();
- contribution_label->setText(tr("%1 %2 of your driving are in the training dataset so far.").arg(count).arg(count == 1 ? "segment" : "segments"));
+ contribution_label->setText(tr("%n segment(s) of your driving is in the training dataset so far.", "", count));
contribution_label->show();
}
});
diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts
index bb5cec335c..a41bc45713 100644
--- a/selfdrive/ui/translations/main_ar.ts
+++ b/selfdrive/ui/translations/main_ar.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,17 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+
+
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts
index 34aadcc733..d440e790fc 100644
--- a/selfdrive/ui/translations/main_de.ts
+++ b/selfdrive/ui/translations/main_de.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,13 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_en.ts b/selfdrive/ui/translations/main_en.ts
index 3f9692e5fa..fbccbedb20 100644
--- a/selfdrive/ui/translations/main_en.ts
+++ b/selfdrive/ui/translations/main_en.ts
@@ -1,6 +1,16 @@
+
+ FirehosePanel
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+ <b>%n segment</b> of your driving is in the training dataset so far.
+ <b>%n segments</b> of your driving are in the training dataset so far.
+
+
+InputDialog
diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts
index 9c7510856f..235051bb73 100644
--- a/selfdrive/ui/translations/main_es.ts
+++ b/selfdrive/ui/translations/main_es.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,13 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts
index 08b310dcd9..4ed97e2338 100644
--- a/selfdrive/ui/translations/main_fr.ts
+++ b/selfdrive/ui/translations/main_fr.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,13 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts
index 591594edb4..7c67d83020 100644
--- a/selfdrive/ui/translations/main_ja.ts
+++ b/selfdrive/ui/translations/main_ja.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,12 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index 5b9024bfd3..5abc6ce9b2 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -323,10 +323,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE파이어호스 모드: 활성화
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
- <b>%1 %2</b> 의 운전이 지금까지의 학습 데이터셋에 포함되어 있습니다.
- ACTIVE활성화
@@ -339,6 +335,12 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.최대한의 효과를 얻으려면 매주 장치를 실내로 가져와 좋은 USB-C 케이블에 연결하고 Wi-Fi에 연결하세요.<br><br>파이어호스 모드는 핫스팟 또는 무제한 SIM 카드에 연결된 경우에는 운전 중에도 작동할 수 있습니다.<br><br><br><b>자주 묻는 질문</b><br><br><i>운전 방법이나 장소가 중요한가요?</i> 아니요, 평소처럼 운전하시면 됩니다.<br><br><i>파이어호스 모드에서 제 모든 구간을 가져오나요?.<br><br><i> 아니요, 저희는 여러분의 구간 중 일부를 선별적으로 가져옵니다.<br><br><i>좋은 USB-C 케이블은 무엇인가요?</i> 휴대폰이나 노트북 고속 충전기가 있으면 됩니다.<br><br><i>어떤 소프트웨어를 실행하는지가 중요한가요?</i> 예, 오직 공식 openpilot의 특정 포크만 트레이닝에 사용할 수 있습니다.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index fb67a5d9e1..5cfd6a9bf7 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -323,10 +323,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar
Firehose Mode: ACTIVEModo Firehose: ATIVO
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
- <b>%1 %2</b> da sua direção já está no conjunto de dados de treinamento até agora.
- ACTIVEATIVO
@@ -339,6 +335,13 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.Para maior eficácia, leve seu dispositivo para dentro de casa e conecte-o a um bom adaptador USB-C e Wi-Fi semanalmente.<br><br>O Modo Firehose também pode funcionar enquanto você dirige, se estiver conectado a um hotspot ou a um chip SIM com dados ilimitados.<br><br><br><b>Perguntas Frequentes</b><br><br><i>Importa como ou onde eu dirijo?</i> Não, basta dirigir normalmente.<br><br><i>Todos os meus segmentos são enviados no Modo Firehose?</i> Não, selecionamos apenas um subconjunto dos seus segmentos.<br><br><i>Qual é um bom adaptador USB-C?</i> Qualquer carregador rápido de telefone ou laptop deve ser suficiente.<br><br><i>Importa qual software eu uso?</i> Sim, apenas o openpilot oficial (e alguns forks específicos) podem ser usados para treinamento.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts
index e041d799ef..6db0085567 100644
--- a/selfdrive/ui/translations/main_th.ts
+++ b/selfdrive/ui/translations/main_th.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,12 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts
index 1147ff0b94..e640eeab4d 100644
--- a/selfdrive/ui/translations/main_tr.ts
+++ b/selfdrive/ui/translations/main_tr.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,12 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index 91839fd68a..f78d8dcdff 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,12 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+ HudRenderer
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index 29cc095698..8547c90410 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -321,10 +321,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Firehose Mode: ACTIVE
-
- <b>%1 %2</b> of your driving are in the training dataset so far.
-
- ACTIVE
@@ -337,6 +333,12 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
+
+ <b>%n segment(s)</b> of your driving is in the training dataset so far.
+
+
+
+ HudRenderer
From dbbb938ae01e6134550e128f1b23b2e6afdf8ed9 Mon Sep 17 00:00:00 2001
From: Dean Lee
Date: Thu, 13 Mar 2025 01:44:04 +0800
Subject: [PATCH 21/68] commonmodel: release input_frame_cl and input_frames_cl
(#34850)
release input_frame_cl and input_frames_cl
---
selfdrive/modeld/models/commonmodel.cc | 2 ++
1 file changed, 2 insertions(+)
diff --git a/selfdrive/modeld/models/commonmodel.cc b/selfdrive/modeld/models/commonmodel.cc
index 99155d9f1e..d3341e76ec 100644
--- a/selfdrive/modeld/models/commonmodel.cc
+++ b/selfdrive/modeld/models/commonmodel.cc
@@ -37,6 +37,7 @@ cl_mem* DrivingModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_hei
DrivingModelFrame::~DrivingModelFrame() {
deinit_transform();
loadyuv_destroy(&loadyuv);
+ CL_CHECK(clReleaseMemObject(input_frames_cl));
CL_CHECK(clReleaseMemObject(img_buffer_20hz_cl));
CL_CHECK(clReleaseMemObject(last_img_cl));
CL_CHECK(clReleaseCommandQueue(q));
@@ -58,5 +59,6 @@ cl_mem* MonitoringModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_
MonitoringModelFrame::~MonitoringModelFrame() {
deinit_transform();
+ CL_CHECK(clReleaseMemObject(input_frame_cl));
CL_CHECK(clReleaseCommandQueue(q));
}
From 8c14985c008edc6109608ddd02259193f30bcf58 Mon Sep 17 00:00:00 2001
From: Alexandre Nobuharu Sato
<66435071+AlexandreSato@users.noreply.github.com>
Date: Wed, 12 Mar 2025 15:04:55 -0300
Subject: [PATCH 22/68] Multilang: update pt_BR translations (#34851)
* Multilang: update pt_BR translations
* no need (s)
---
selfdrive/ui/translations/main_pt-BR.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index 5cfd6a9bf7..57b5b4ffa5 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -337,9 +337,9 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar
<b>%n segment(s)</b> of your driving is in the training dataset so far.
-
-
-
+
+ <b>%n segmento</b> da sua direção está no conjunto de dados de treinamento até agora.
+ <b>%n segmentos</b> da sua direção estão no conjunto de dados de treinamento até agora.
From 800a41c4e141b005a6a6a9bc78f6c1ec32f29633 Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Wed, 12 Mar 2025 14:02:54 -0700
Subject: [PATCH 23/68] agnos 11.11 (#34816)
---
launch_env.sh | 2 +-
system/hardware/tici/agnos.json | 22 ++++++------
system/hardware/tici/all-partitions.json | 46 ++++++++++++------------
3 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/launch_env.sh b/launch_env.sh
index 049b9da279..9bee47837a 100755
--- a/launch_env.sh
+++ b/launch_env.sh
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
- export AGNOS_VERSION="11.10"
+ export AGNOS_VERSION="11.11"
fi
export STAGING_ROOT="/data/safe_staging"
diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json
index d42c8151ab..04e5fd3365 100644
--- a/system/hardware/tici/agnos.json
+++ b/system/hardware/tici/agnos.json
@@ -56,28 +56,28 @@
},
{
"name": "boot",
- "url": "https://commadist.azureedge.net/agnosupdate/boot-bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba.img.xz",
- "hash": "bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba",
- "hash_raw": "bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba",
- "size": 18475008,
+ "url": "https://commadist.azureedge.net/agnosupdate/boot-0d0d4d5a32e00b46fa36180b4a96337f2a53302d8bd0faee95f8fe1063d1e24a.img.xz",
+ "hash": "0d0d4d5a32e00b46fa36180b4a96337f2a53302d8bd0faee95f8fe1063d1e24a",
+ "hash_raw": "0d0d4d5a32e00b46fa36180b4a96337f2a53302d8bd0faee95f8fe1063d1e24a",
+ "size": 18479104,
"sparse": false,
"full_check": true,
"has_ab": true,
- "ondevice_hash": "8907415564e8a242548e871b534dcd53240fe4e4517700c6c85b5637e365f0b0"
+ "ondevice_hash": "47b2096995578a5078e393c33108b42756009dbb361c43c508fc93cd8bda99cc"
},
{
"name": "system",
- "url": "https://commadist.azureedge.net/agnosupdate/system-5612484e7f255659c0845de620e7c733afd2e1b939f9464f5ef039721bb7cba9.img.xz",
- "hash": "e4872f4132111b7b28586d978dd01bb48ffa031e103d029ebede7613c1bc2aa6",
- "hash_raw": "5612484e7f255659c0845de620e7c733afd2e1b939f9464f5ef039721bb7cba9",
+ "url": "https://commadist.azureedge.net/agnosupdate/system-ff2da7bc34a1ad8e2831a13a49685243957a51b762d8f28e14e98ac90e84d211.img.xz",
+ "hash": "fbda983c40e75d8c4784a45c687f27dd27f97f7f0e9a71b4981ec2092ff68a72",
+ "hash_raw": "ff2da7bc34a1ad8e2831a13a49685243957a51b762d8f28e14e98ac90e84d211",
"size": 4404019200,
"sparse": true,
"full_check": false,
"has_ab": true,
- "ondevice_hash": "4e5e680b4ac387ddc974b32dd3d5ec1d76282511eab974866b3b72399034985e",
+ "ondevice_hash": "428c744458d5f35199aab96a0928876c9deeff3e753e95044291408ba244f0af",
"alt": {
- "hash": "5612484e7f255659c0845de620e7c733afd2e1b939f9464f5ef039721bb7cba9",
- "url": "https://commadist.azureedge.net/agnosupdate/system-5612484e7f255659c0845de620e7c733afd2e1b939f9464f5ef039721bb7cba9.img",
+ "hash": "ff2da7bc34a1ad8e2831a13a49685243957a51b762d8f28e14e98ac90e84d211",
+ "url": "https://commadist.azureedge.net/agnosupdate/system-ff2da7bc34a1ad8e2831a13a49685243957a51b762d8f28e14e98ac90e84d211.img",
"size": 4404019200
}
}
diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json
index e90e8c2ec4..4d15e76b0e 100644
--- a/system/hardware/tici/all-partitions.json
+++ b/system/hardware/tici/all-partitions.json
@@ -339,62 +339,62 @@
},
{
"name": "boot",
- "url": "https://commadist.azureedge.net/agnosupdate/boot-bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba.img.xz",
- "hash": "bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba",
- "hash_raw": "bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba",
- "size": 18475008,
+ "url": "https://commadist.azureedge.net/agnosupdate/boot-0d0d4d5a32e00b46fa36180b4a96337f2a53302d8bd0faee95f8fe1063d1e24a.img.xz",
+ "hash": "0d0d4d5a32e00b46fa36180b4a96337f2a53302d8bd0faee95f8fe1063d1e24a",
+ "hash_raw": "0d0d4d5a32e00b46fa36180b4a96337f2a53302d8bd0faee95f8fe1063d1e24a",
+ "size": 18479104,
"sparse": false,
"full_check": true,
"has_ab": true,
- "ondevice_hash": "8907415564e8a242548e871b534dcd53240fe4e4517700c6c85b5637e365f0b0"
+ "ondevice_hash": "47b2096995578a5078e393c33108b42756009dbb361c43c508fc93cd8bda99cc"
},
{
"name": "system",
- "url": "https://commadist.azureedge.net/agnosupdate/system-5612484e7f255659c0845de620e7c733afd2e1b939f9464f5ef039721bb7cba9.img.xz",
- "hash": "e4872f4132111b7b28586d978dd01bb48ffa031e103d029ebede7613c1bc2aa6",
- "hash_raw": "5612484e7f255659c0845de620e7c733afd2e1b939f9464f5ef039721bb7cba9",
+ "url": "https://commadist.azureedge.net/agnosupdate/system-ff2da7bc34a1ad8e2831a13a49685243957a51b762d8f28e14e98ac90e84d211.img.xz",
+ "hash": "fbda983c40e75d8c4784a45c687f27dd27f97f7f0e9a71b4981ec2092ff68a72",
+ "hash_raw": "ff2da7bc34a1ad8e2831a13a49685243957a51b762d8f28e14e98ac90e84d211",
"size": 4404019200,
"sparse": true,
"full_check": false,
"has_ab": true,
- "ondevice_hash": "4e5e680b4ac387ddc974b32dd3d5ec1d76282511eab974866b3b72399034985e",
+ "ondevice_hash": "428c744458d5f35199aab96a0928876c9deeff3e753e95044291408ba244f0af",
"alt": {
- "hash": "5612484e7f255659c0845de620e7c733afd2e1b939f9464f5ef039721bb7cba9",
- "url": "https://commadist.azureedge.net/agnosupdate/system-5612484e7f255659c0845de620e7c733afd2e1b939f9464f5ef039721bb7cba9.img",
+ "hash": "ff2da7bc34a1ad8e2831a13a49685243957a51b762d8f28e14e98ac90e84d211",
+ "url": "https://commadist.azureedge.net/agnosupdate/system-ff2da7bc34a1ad8e2831a13a49685243957a51b762d8f28e14e98ac90e84d211.img",
"size": 4404019200
}
},
{
"name": "userdata_90",
- "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-16c037fa42ee99bc6ec92909efc8a8075a0e8a0232a7d90e39e7d40a7bd0ee8e.img.xz",
- "hash": "c6fb215f2b297f7ff5b8f133bc5d687772b37f2fee42a44aa730e37a84a14e52",
- "hash_raw": "16c037fa42ee99bc6ec92909efc8a8075a0e8a0232a7d90e39e7d40a7bd0ee8e",
+ "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-17f46ba0d0eec10cf14804c84d9bcf4f6fa864074f1b90ff2fb4873399d64b1a.img.xz",
+ "hash": "1442ae218daff960783bc493a32dd489fa787e4025c64fa3132f48b1e627647f",
+ "hash_raw": "17f46ba0d0eec10cf14804c84d9bcf4f6fa864074f1b90ff2fb4873399d64b1a",
"size": 96636764160,
"sparse": true,
"full_check": true,
"has_ab": false,
- "ondevice_hash": "7199262f209abbb07be5eece505ac4d4c7ba8957f2d9ff7b1ac1ef2063461665"
+ "ondevice_hash": "a64fea9c3a7bec11b850a55d50ceb30a032742c90aadf0ce35cdbef9349bebba"
},
{
"name": "userdata_89",
- "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-62c2c41470282b581ec1bbbe0375fb3b6c66df2f4bc3dc6c6fdf796f1797f136.img.xz",
- "hash": "d66f894436fa11d4ff00f8a84e54d9e23a6492b0087f69bb958d2ab0bdc6dfba",
- "hash_raw": "62c2c41470282b581ec1bbbe0375fb3b6c66df2f4bc3dc6c6fdf796f1797f136",
+ "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-69b2404839f87b1de5c05766550ca0217d5ed566c9f2ea41c46b5a301540a3ac.img.xz",
+ "hash": "332e6cf5c221690a01ebfd0582420e039c7796a8fb2d499e44a8b657dffe4af2",
+ "hash_raw": "69b2404839f87b1de5c05766550ca0217d5ed566c9f2ea41c46b5a301540a3ac",
"size": 95563022336,
"sparse": true,
"full_check": true,
"has_ab": false,
- "ondevice_hash": "f1d3685618f6d1bde24ce6109284c5d30ece2f4fd015be67e8b52ef7e06067a4"
+ "ondevice_hash": "c560c76007fd8997e01e516ab16ff445048945a13bf830bb2a4c7f6e9d2fe5f6"
},
{
"name": "userdata_30",
- "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-3e71d8804c90a6dff5048edb976149f9ea177efa139dea47cb585cef76b26f6e.img.xz",
- "hash": "1b201ecbd0e1573777811bf18fa90cb080bfbccb34a6dcfd39b412632e7ca699",
- "hash_raw": "3e71d8804c90a6dff5048edb976149f9ea177efa139dea47cb585cef76b26f6e",
+ "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-ffc34d8753520e684448185db801c05bb81e23c54a499cc40a2e38b7009cdf36.img.xz",
+ "hash": "8bfb1fdec9c8f96c97bcce1efbc7d502447bdb3b481cc408b24b5feef66839fd",
+ "hash_raw": "ffc34d8753520e684448185db801c05bb81e23c54a499cc40a2e38b7009cdf36",
"size": 32212254720,
"sparse": true,
"full_check": true,
"has_ab": false,
- "ondevice_hash": "47c28e63209556442cd9d2fd06a6c0e7fcf35c8a4e4fbcc550b9c089429ad0e0"
+ "ondevice_hash": "8f840157d73fa4469aa650d668e046c9750304af84248ab83453cbd285918859"
}
]
\ No newline at end of file
From 96641ac12c93c95ae65b666e11f7ac63fa968886 Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Wed, 12 Mar 2025 14:56:40 -0700
Subject: [PATCH 24/68] no gh actions in release
---
release/release_files.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/release/release_files.py b/release/release_files.py
index 6aabdf0a4c..36910293a4 100755
--- a/release/release_files.py
+++ b/release/release_files.py
@@ -8,6 +8,7 @@ ROOT = HERE + "/.."
blacklist = [
".git/",
+ ".github/workflows/",
"matlab.*.md",
From 196eb50d231fabf8cb24605984fb40a170770b86 Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Wed, 12 Mar 2025 15:59:27 -0700
Subject: [PATCH 25/68] camerad: log whether sync fails in IFE or BPS (#34854)
---
system/camerad/cameras/spectra.cc | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc
index 1bd6f00352..3005a4a550 100644
--- a/system/camerad/cameras/spectra.cc
+++ b/system/camerad/cameras/spectra.cc
@@ -1406,7 +1406,9 @@ bool SpectraCamera::waitForFrameReady(uint64_t request_id) {
struct cam_sync_wait sync_wait = {};
sync_wait.sync_obj = sync_obj;
sync_wait.timeout_ms = stress_test(sync_type) ? 1 : timeout_ms;
- return do_sync_control(m->cam_sync_fd, CAM_SYNC_WAIT, &sync_wait, sizeof(sync_wait)) == 0;
+ bool ret = do_sync_control(m->cam_sync_fd, CAM_SYNC_WAIT, &sync_wait, sizeof(sync_wait)) == 0;
+ if (!ret) LOGE("camera %d %s failed", cc.camera_num, sync_type);
+ return ret;
};
// wait for frame from IFE
From 41536f6e48d52959a5e82fb02d28c625ff1b2813 Mon Sep 17 00:00:00 2001
From: royjr
Date: Wed, 12 Mar 2025 22:21:28 -0400
Subject: [PATCH 26/68] ui: update arabic translations (#34857)
Update main_ar.ts
---
selfdrive/ui/translations/main_ar.ts | 40 +++++++++++++++-------------
1 file changed, 21 insertions(+), 19 deletions(-)
diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts
index a41bc45713..8f43e64e8a 100644
--- a/selfdrive/ui/translations/main_ar.ts
+++ b/selfdrive/ui/translations/main_ar.ts
@@ -309,39 +309,41 @@
FirehosePanel🔥 Firehose Mode 🔥
-
+ 🔥 وضع خرطوم الحريق 🔥openpilot learns to drive by watching humans, like you, drive.
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.
-
+ يتعلم تطبيق openpilot كيفية القيادة من خلال مشاهدة البشر، مثلك، أثناء القيادة.
+
+يتيح لك وضع خرطوم الحريق زيادة تحميلات بيانات التدريب لتحسين نماذج القيادة في OpenPilot. كلما زادت البيانات، زادت النماذج، مما يعني وضعًا تجريبيًا أفضل.Firehose Mode: ACTIVE
-
+ وضع خرطوم الحريق: نشطACTIVE
-
+ نشط<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
-
+ <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>غير نشط</span>: اتصل بشبكة غير محسوبةFor maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
+ للحصول على أقصى فعالية، أحضر جهازك إلى الداخل واتصل بمحول USB-C جيد وشبكة Wi-Fi أسبوعياً.<br><br>يمكن أن يعمل وضع خرطوم الحريق أيضاً أثناء القيادة إذا كنت متصلاً بنقطة اتصال أو ببطاقة SIM غير محدودة.<br><br><br><b>الأسئلة المتكررة</b><br><br><i>هل يهم كيف أو أين أقود؟</i> لا، فقط قد كما تفعل عادة.<br><br><i>هل يتم سحب كل مقاطع رحلاتي في وضع خرطوم الحريق؟</i> لا، نقوم بسحب مجموعة مختارة من مقاطع رحلاتك.<br><br><i>ما هو محول USB-C الجيد؟</i> أي شاحن سريع للهاتف أو اللابتوب يجب أن يكون مناسباً.<br><br><i>هل يهم أي برنامج أستخدم؟</i> نعم، فقط النسخة الأصلية من openpilot (وأفرع معينة) يمكن استخدامها للتدريب.<b>%n segment(s)</b> of your driving is in the training dataset so far.
-
-
-
-
-
-
-
+
+ حتى الآن، يوجد </b>%n مقطع<b>%n من قيادتك في مجموعة بيانات التدريب.
+ حتى الآن، يوجد </b>%n مقطع<b>%n من قيادتك في مجموعة بيانات التدريب.
+ حتى الآن، يوجد </b>%n مقطع<b>%n من قيادتك في مجموعة بيانات التدريب.
+ حتى الآن، يوجد </b>%n مقطع<b>%n من قيادتك في مجموعة بيانات التدريب.
+ حتى الآن، يوجد </b>%n مقطع<b>%n من قيادتك في مجموعة بيانات التدريب.
+ حتى الآن، يوجد </b>%n مقطع<b>%n من قيادتك في مجموعة بيانات التدريب.
@@ -699,7 +701,7 @@ This may take up to a minute.
Firehose
-
+ خرطوم الحريق
@@ -1020,11 +1022,11 @@ This may take up to a minute.
Welcome to openpilot
-
+ مرحباً بكم في openpilotYou must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.
-
+ يجب عليك قبول الشروط والأحكام لاستخدام openpilot. اقرأ أحدث الشروط على <span style='color: #465BEA;'>https://comma.ai/terms</span> قبل الاستمرار.
@@ -1177,15 +1179,15 @@ This may take up to a minute.
WiFiPromptWidgetOpen
-
+ انفتحMaximize your training data uploads to improve openpilot's driving models.
-
+ قم بزيادة تحميلات بيانات التدريب الخاصة بك لتحسين نماذج القيادة الخاصة بـ openpilot.<span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span>
-
+ <span style='font-family: "Noto Color Emoji";'>🔥</span> وضع خرطوم الحريق <span style='font-family: Noto Color Emoji;'>🔥</span>
From 3652dff77a1220b3482c99b34f1309defa5c31d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20R=C4=85czy?=
Date: Thu, 13 Mar 2025 02:51:25 -0400
Subject: [PATCH 27/68] paramsd: allow uncalibrated values while calibrating
(#34852)
* Use uncalibrated values
* Use calib_valid
* Move valids together
* Add calibration valid field
* Add migration block for new field
* No paramsdTemporaryError while calibrating
* comment
* Fix static issues
* Update ref commit
* Comment
* Remove redundant field
* check for calstatus in selfdrived
* Remove comment
* Update ref commit
---
selfdrive/locationd/paramsd.py | 10 +++++-----
selfdrive/selfdrived/selfdrived.py | 2 +-
selfdrive/test/process_replay/ref_commit | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py
index 258fc97e5a..3819fba080 100755
--- a/selfdrive/locationd/paramsd.py
+++ b/selfdrive/locationd/paramsd.py
@@ -52,14 +52,14 @@ class ParamsLearner:
device_pose = Pose.from_live_pose(msg)
calibrated_pose = self.calibrator.build_calibrated_pose(device_pose)
- yaw_rate_valid = msg.angularVelocityDevice.valid and self.calibrator.calib_valid
+ yaw_rate_valid = msg.angularVelocityDevice.valid
yaw_rate_valid = yaw_rate_valid and 0 < self.yaw_rate_std < 10 # rad/s
yaw_rate_valid = yaw_rate_valid and abs(self.yaw_rate) < 1 # rad/s
if yaw_rate_valid:
self.yaw_rate, self.yaw_rate_std = calibrated_pose.angular_velocity.z, calibrated_pose.angular_velocity.z_std
else:
# This is done to bound the yaw rate estimate when localizer values are invalid or calibrating
- self.yaw_rate, self.yaw_rate_std = 0.0, np.radians(1)
+ self.yaw_rate, self.yaw_rate_std = 0.0, np.radians(10.0)
localizer_roll, localizer_roll_std = device_pose.orientation.x, device_pose.orientation.x_std
localizer_roll_std = np.radians(1) if np.isnan(localizer_roll_std) else localizer_roll_std
@@ -225,13 +225,13 @@ def main():
liveParameters.posenetValid = True
liveParameters.sensorValid = sensors_valid
liveParameters.steerRatio = float(x[States.STEER_RATIO].item())
- liveParameters.steerRatioValid = min_sr <= liveParameters.steerRatio <= max_sr
liveParameters.stiffnessFactor = float(x[States.STIFFNESS].item())
- liveParameters.stiffnessFactorValid = 0.2 <= liveParameters.stiffnessFactor <= 5.0
liveParameters.roll = float(roll)
liveParameters.angleOffsetAverageDeg = float(angle_offset_average)
- liveParameters.angleOffsetAverageValid = bool(avg_offset_valid)
liveParameters.angleOffsetDeg = float(angle_offset)
+ liveParameters.steerRatioValid = min_sr <= liveParameters.steerRatio <= max_sr
+ liveParameters.stiffnessFactorValid = 0.2 <= liveParameters.stiffnessFactor <= 5.0
+ liveParameters.angleOffsetAverageValid = bool(avg_offset_valid)
liveParameters.angleOffsetValid = bool(total_offset_valid)
liveParameters.valid = all((
liveParameters.angleOffsetAverageValid,
diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py
index 8b7db19c9a..b81882723e 100755
--- a/selfdrive/selfdrived/selfdrived.py
+++ b/selfdrive/selfdrived/selfdrived.py
@@ -305,7 +305,7 @@ class SelfdriveD:
self.events.add(EventName.posenetInvalid)
if not self.sm['livePose'].inputsOK:
self.events.add(EventName.locationdTemporaryError)
- if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY):
+ if not self.sm['liveParameters'].valid and cal_status == log.LiveCalibrationData.Status.calibrated and not TESTING_CLOSET and (not SIMULATION or REPLAY):
self.events.add(EventName.paramsdTemporaryError)
# conservative HW alert. if the data or frequency are off, locationd will throw an error
diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit
index cfd179fb79..86bdc46fea 100644
--- a/selfdrive/test/process_replay/ref_commit
+++ b/selfdrive/test/process_replay/ref_commit
@@ -1 +1 @@
-37041a45841e83f0641ef1e87c0e567181d47172
\ No newline at end of file
+98672ccf23dc08fcd08b53cae3ec305c04219fe8
\ No newline at end of file
From 1cd6ca467d0549db55bf65aeb896ca8f6c299c3c Mon Sep 17 00:00:00 2001
From: Dean Lee
Date: Fri, 14 Mar 2025 01:40:26 +0800
Subject: [PATCH 28/68] pandad: forward debug logs to cloudlog (#34859)
forward debug logs to cloudlog
---
selfdrive/pandad/panda.cc | 19 +++++++++++++++++++
selfdrive/pandad/panda.h | 2 ++
selfdrive/pandad/pandad.cc | 8 ++++++++
3 files changed, 29 insertions(+)
diff --git a/selfdrive/pandad/panda.cc b/selfdrive/pandad/panda.cc
index 7b5fc9a999..93e139f0ec 100644
--- a/selfdrive/pandad/panda.cc
+++ b/selfdrive/pandad/panda.cc
@@ -66,6 +66,25 @@ void Panda::set_alternative_experience(uint16_t alternative_experience) {
handle->control_write(0xdf, alternative_experience, 0);
}
+std::string Panda::serial_read(int port_number) {
+ std::string ret;
+ char buffer[USBPACKET_MAX_SIZE] = {};
+
+ while (true) {
+ int bytes_read = handle->control_read(0xe0, port_number, 0, (unsigned char *)buffer, USBPACKET_MAX_SIZE);
+ if (bytes_read <= 0) {
+ break;
+ }
+ ret.append(buffer, bytes_read);
+ }
+
+ return ret;
+}
+
+void Panda::set_uart_baud(int uart, int rate) {
+ handle->control_write(0xe4, uart, int(rate / 300));
+}
+
cereal::PandaState::PandaType Panda::get_hw_type() {
unsigned char hw_query[1] = {0};
diff --git a/selfdrive/pandad/panda.h b/selfdrive/pandad/panda.h
index 6ae2c77755..5cbce44f28 100644
--- a/selfdrive/pandad/panda.h
+++ b/selfdrive/pandad/panda.h
@@ -64,6 +64,8 @@ public:
cereal::PandaState::PandaType get_hw_type();
void set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param=0U);
void set_alternative_experience(uint16_t alternative_experience);
+ std::string serial_read(int port_number = 0);
+ void set_uart_baud(int uart, int rate);
void set_fan_speed(uint16_t fan_speed);
uint16_t get_fan_speed();
void set_ir_pwr(uint16_t ir_pwr);
diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc
index e6ef4a4072..db7dd387e0 100644
--- a/selfdrive/pandad/pandad.cc
+++ b/selfdrive/pandad/pandad.cc
@@ -452,6 +452,14 @@ void pandad_run(std::vector &pandas) {
send_peripheral_state(peripheral_panda, &pm);
}
+ // Forward logs from pandas to cloudlog if available
+ for (auto *panda : pandas) {
+ std::string log = panda->serial_read();
+ if (!log.empty()) {
+ LOGD("%s", log.c_str());
+ }
+ }
+
rk.keepTime();
}
From c0b6d5823642717a7033395e002066f7be564475 Mon Sep 17 00:00:00 2001
From: Dean Lee
Date: Fri, 14 Mar 2025 01:42:22 +0800
Subject: [PATCH 29/68] setup: fix dangling pointer Issue in curl header setup
(#34860)
fix dangling pointer in curl header setup
---
selfdrive/ui/qt/setup/setup.cc | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc
index aff9b015b3..dfa9e12b4e 100644
--- a/selfdrive/ui/qt/setup/setup.cc
+++ b/selfdrive/ui/qt/setup/setup.cc
@@ -48,7 +48,8 @@ void Setup::download(QString url) {
auto version = util::read_file("/VERSION");
struct curl_slist *list = NULL;
- list = curl_slist_append(list, ("X-openpilot-serial: " + Hardware::get_serial()).c_str());
+ std::string header = "X-openpilot-serial: " + Hardware::get_serial();
+ list = curl_slist_append(list, header.c_str());
char tmpfile[] = "/tmp/installer_XXXXXX";
FILE *fp = fdopen(mkstemp(tmpfile), "wb");
From bd2f0017ff51d86c8269751fa5b612dbcc85c17c Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Thu, 13 Mar 2025 20:49:55 -0700
Subject: [PATCH 30/68] ignore
---
pyproject.toml | 1 +
1 file changed, 1 insertion(+)
diff --git a/pyproject.toml b/pyproject.toml
index 1fb751e2d5..72070dbc55 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -232,6 +232,7 @@ lint.ignore = [
"B024",
"NPY002", # new numpy random syntax is worse
"UP038", # (x, y) -> x|y for isinstance
+ "C420",
]
line-length = 160
target-version ="py311"
From a8bb9cfe410fa1916dfb26634d25cfef4ffcb02e Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Thu, 13 Mar 2025 20:50:51 -0700
Subject: [PATCH 31/68] Revert "ignore"
This reverts commit bd2f0017ff51d86c8269751fa5b612dbcc85c17c.
---
pyproject.toml | 1 -
1 file changed, 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 72070dbc55..1fb751e2d5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -232,7 +232,6 @@ lint.ignore = [
"B024",
"NPY002", # new numpy random syntax is worse
"UP038", # (x, y) -> x|y for isinstance
- "C420",
]
line-length = 160
target-version ="py311"
From 02519b7a6e6c8fdc446bd1d76143ce0992d9d6da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20R=C4=85czy?=
Date: Fri, 14 Mar 2025 18:24:41 -0400
Subject: [PATCH 32/68] process_replay: refactor migrate_deviceState (#34864)
* Refactor migrate_deviceState
* Fix again
---
selfdrive/test/process_replay/migration.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/selfdrive/test/process_replay/migration.py b/selfdrive/test/process_replay/migration.py
index 2d42d31619..fd13c4f239 100644
--- a/selfdrive/test/process_replay/migration.py
+++ b/selfdrive/test/process_replay/migration.py
@@ -241,14 +241,16 @@ def migrate_gpsLocation(msgs):
@migration(inputs=["deviceState", "initData"])
def migrate_deviceState(msgs):
+ init_data = next((m.initData for _, m in msgs if m.which() == 'initData'), None)
+ device_state = next((m.deviceState for _, m in msgs if m.which() == 'deviceState'), None)
+ if init_data is None or device_state is None:
+ return [], [], []
+
ops = []
- dt = None
for i, msg in msgs:
- if msg.which() == 'initData':
- dt = msg.initData.deviceType
if msg.which() == 'deviceState':
n = msg.as_builder()
- n.deviceState.deviceType = dt
+ n.deviceState.deviceType = init_data.deviceType
ops.append((i, n.as_reader()))
return ops, [], []
From 7ab558a7894ecf85b8ec73e986080986825310c9 Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Fri, 14 Mar 2025 16:09:23 -0700
Subject: [PATCH 33/68] run_process_on_route: support multiple processes
(#34867)
support multiple procs
---
selfdrive/debug/run_process_on_route.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/selfdrive/debug/run_process_on_route.py b/selfdrive/debug/run_process_on_route.py
index 45665e8a48..2ccb0fb3e7 100755
--- a/selfdrive/debug/run_process_on_route.py
+++ b/selfdrive/debug/run_process_on_route.py
@@ -10,21 +10,21 @@ if __name__ == "__main__":
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--fingerprint", help="The fingerprint to use")
parser.add_argument("route", help="The route name to use")
- parser.add_argument("process", help="The process to run")
+ parser.add_argument("process", nargs='+', help="The process(s) to run")
args = parser.parse_args()
- cfg = [c for c in CONFIGS if c.proc_name == args.process][0]
+ cfgs = [c for c in CONFIGS if c.proc_name in args.process]
lr = LogReader(args.route)
inputs = list(lr)
- outputs = replay_process(cfg, inputs, fingerprint=args.fingerprint)
+ outputs = replay_process(cfgs, inputs, fingerprint=args.fingerprint)
# Remove message generated by the process under test and merge in the new messages
produces = {o.which() for o in outputs}
inputs = [i for i in inputs if i.which() not in produces]
outputs = sorted(inputs + outputs, key=lambda x: x.logMonoTime)
- fn = f"{args.route.replace('/', '_')}_{args.process}.zst"
+ fn = f"{args.route.replace('/', '_')}_{'_'.join(args.process)}.zst"
print(f"Saving log to {fn}")
save_log(fn, outputs)
From 07edc54db65690d95b53876221164dd571898b0d Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Fri, 14 Mar 2025 16:16:10 -0700
Subject: [PATCH 34/68] bump opendbc (#34868)
bump
---
opendbc_repo | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/opendbc_repo b/opendbc_repo
index eca696984e..de96aaf51b 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit eca696984e467b909814ea1e594163ceea03ab3a
+Subproject commit de96aaf51b60be02b4978b15fbf3b6dd0d829cfd
From 349d569dfa08b036169e12539d2dcf0e47167f8b Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Fri, 14 Mar 2025 17:21:02 -0700
Subject: [PATCH 35/68] radard: radar errors are captured in valid flag
(#34870)
* already set valid from this
* clean up
---
selfdrive/controls/radard.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py
index 0267166795..9f5bea53ff 100755
--- a/selfdrive/controls/radard.py
+++ b/selfdrive/controls/radard.py
@@ -231,7 +231,7 @@ class RadarD:
self.tracks[ids].update(rpt[0], rpt[1], rpt[2], v_lead, rpt[3])
# *** publish radarState ***
- self.radar_state_valid = sm.all_checks() and len(rr.errors) == 0
+ self.radar_state_valid = sm.all_checks()
self.radar_state = log.RadarState.new_message()
self.radar_state.mdMonoTime = sm.logMonoTime['modelV2']
self.radar_state.radarErrors = list(rr.errors)
From e1eac057cabd2fa6a4841e9d3428ff9e14beb958 Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Fri, 14 Mar 2025 19:30:09 -0700
Subject: [PATCH 36/68] camerad: fix rare BPS startup issues (#34871)
* repros
* handle old frames
* cleanup
* more freq
* fix request id skipping
---------
Co-authored-by: Comma Device
---
system/camerad/cameras/spectra.cc | 21 ++++++++++++++++++---
system/camerad/cameras/spectra.h | 14 ++++++++++----
2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc
index 3005a4a550..247b0dd215 100644
--- a/system/camerad/cameras/spectra.cc
+++ b/system/camerad/cameras/spectra.cc
@@ -237,7 +237,7 @@ SpectraCamera::SpectraCamera(SpectraMaster *master, const CameraConfig &config)
: m(master),
enabled(config.enabled),
cc(config) {
- ife_buf_depth = (cc.output_type == ISP_RAW_OUTPUT) ? 4 : VIPC_BUFFER_COUNT;
+ ife_buf_depth = VIPC_BUFFER_COUNT;
assert(ife_buf_depth < MAX_IFE_BUFS);
}
@@ -1330,7 +1330,16 @@ bool SpectraCamera::handle_camera_event(const cam_req_mgr_message *event_data) {
uint64_t timestamp = event_data->u.frame_msg.timestamp; // timestamped in the kernel's SOF IRQ callback
//LOGD("handle cam %d ts %lu req id %lu frame id %lu", cc.camera_num, timestamp, request_id, frame_id_raw);
- if (stress_test("skipping SOF event")) return false;
+ // if there's a lag, some more frames could have already come in before
+ // we cleared the queue, so we'll still get them with valid (> 0) request IDs.
+ if (timestamp < last_requeue_ts) {
+ LOGD("skipping frame: ts before requeue / cam %d ts %lu req id %lu frame id %lu", cc.camera_num, timestamp, request_id, frame_id_raw);
+ return false;
+ }
+
+ if (stress_test("skipping SOF event")) {
+ return false;
+ }
if (!validateEvent(request_id, frame_id_raw)) {
return false;
@@ -1381,7 +1390,7 @@ bool SpectraCamera::validateEvent(uint64_t request_id, uint64_t frame_id_raw) {
if (request_id != request_id_last + 1) {
LOGE("camera %d requests skipped %ld -> %ld", cc.camera_num, request_id_last, request_id);
- clearAndRequeue(request_id_last + 1);
+ clearAndRequeue(request_id + 1);
return false;
}
}
@@ -1392,6 +1401,7 @@ void SpectraCamera::clearAndRequeue(uint64_t from_request_id) {
// clear everything, then queue up a fresh set of frames
LOGW("clearing and requeuing camera %d from %lu", cc.camera_num, from_request_id);
clear_req_queue();
+ last_requeue_ts = nanos_since_boot();
for (uint64_t id = from_request_id; id < from_request_id + ife_buf_depth; ++id) {
enqueue_frame(id);
}
@@ -1402,6 +1412,11 @@ bool SpectraCamera::waitForFrameReady(uint64_t request_id) {
int buf_idx = request_id % ife_buf_depth;
assert(sync_objs_ife[buf_idx]);
+ if (stress_test("sync sleep time")) {
+ util::sleep_for(350);
+ return false;
+ }
+
auto waitForSync = [&](uint32_t sync_obj, int timeout_ms, const char *sync_type) {
struct cam_sync_wait sync_wait = {};
sync_wait.sync_obj = sync_obj;
diff --git a/system/camerad/cameras/spectra.h b/system/camerad/cameras/spectra.h
index 21f1970326..13cb13f98f 100644
--- a/system/camerad/cameras/spectra.h
+++ b/system/camerad/cameras/spectra.h
@@ -181,6 +181,7 @@ public:
int sync_objs_ife[MAX_IFE_BUFS] = {};
int sync_objs_bps[MAX_IFE_BUFS] = {};
uint64_t request_id_last = 0;
+ uint64_t last_requeue_ts = 0;
uint64_t frame_id_raw_last = 0;
int invalid_request_count = 0;
bool skip_expected = true;
@@ -202,11 +203,16 @@ private:
inline static bool first_frame_synced = false;
// a mode for stressing edge cases: realignment, sync failures, etc.
- inline bool stress_test(const char* log) {
- static double prob = std::stod(util::getenv("SPECTRA_ERROR_PROB", "-1"));;
- bool triggered = (prob > 0) && ((static_cast(rand()) / RAND_MAX) < prob);
+ inline bool stress_test(std::string log) {
+ static double last_trigger = 0;
+ static double prob = std::stod(util::getenv("SPECTRA_ERROR_PROB", "-1"));
+ static double dt = std::stod(util::getenv("SPECTRA_ERROR_DT", "1"));
+ bool triggered = (prob > 0) && \
+ ((static_cast(rand()) / RAND_MAX) < prob) && \
+ (millis_since_boot() - last_trigger) > dt;
if (triggered) {
- LOGE("stress test (cam %d): %s", cc.camera_num, log);
+ last_trigger = millis_since_boot();
+ LOGE("stress test (cam %d): %s", cc.camera_num, log.c_str());
}
return triggered;
}
From 811272dc28f82229586d6ffe90ea1dbddac8959b Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Fri, 14 Mar 2025 19:37:27 -0700
Subject: [PATCH 37/68] radarState: check average frequency (#34872)
* always check radarState freq since it polls on modelV2 and sends at a fixed rate
* fine
* can just check this since valid is all_checks on liveTracks, and liveTracks.valid is len(errors) as well
full circle
---
selfdrive/controls/plannerd.py | 2 +-
selfdrive/selfdrived/selfdrived.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py
index a74b40e15f..bec7eede0b 100755
--- a/selfdrive/controls/plannerd.py
+++ b/selfdrive/controls/plannerd.py
@@ -20,7 +20,7 @@ def main():
longitudinal_planner = LongitudinalPlanner(CP)
pm = messaging.PubMaster(['longitudinalPlan', 'driverAssistance'])
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', 'selfdriveState'],
- poll='modelV2', ignore_avg_freq=['radarState'])
+ poll='modelV2')
while True:
sm.update()
diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py
index b81882723e..93f8f59133 100755
--- a/selfdrive/selfdrived/selfdrived.py
+++ b/selfdrive/selfdrived/selfdrived.py
@@ -80,7 +80,7 @@ class SelfdriveD:
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'controlsState', 'carControl', 'driverAssistance', 'alertDebug'] + \
self.camera_packets + self.sensor_packets + self.gps_packets,
- ignore_alive=ignore, ignore_avg_freq=ignore+['radarState',],
+ ignore_alive=ignore, ignore_avg_freq=ignore,
ignore_valid=ignore, frequency=int(1/DT_CTRL))
# read params
@@ -269,7 +269,7 @@ class SelfdriveD:
self.events.add(EventName.cameraFrameRate)
if not REPLAY and self.rk.lagging:
self.events.add(EventName.selfdrivedLagging)
- if len(self.sm['radarState'].radarErrors) or ((not self.rk.lagging or REPLAY) and not self.sm.all_checks(['radarState'])):
+ if not self.sm.valid['radarState']:
self.events.add(EventName.radarFault)
if not self.sm.valid['pandaStates']:
self.events.add(EventName.usbError)
From 714348e17e145c1dc97cc08a11280f63cbe376f2 Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Fri, 14 Mar 2025 20:40:28 -0700
Subject: [PATCH 38/68] liveTracks: bool error fields (#34869)
* bool fields
* liveTracks.valid is already set using errors
* fix
* clean up
* same but diff
* fix
* fix
* fix test
* clean up
* rm radarErrors
* whoops
* no struct
* Revert "no struct"
This reverts commit ed8ec02493673f850800344c73c969508fc38991.
* bump
* update
---
cereal/log.capnp | 2 +-
opendbc_repo | 2 +-
selfdrive/car/card.py | 2 +-
selfdrive/car/tests/test_models.py | 2 +-
selfdrive/controls/radard.py | 1 -
selfdrive/controls/tests/test_leads.py | 2 +-
selfdrive/test/process_replay/ref_commit | 2 +-
7 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/cereal/log.capnp b/cereal/log.capnp
index 78b511ca64..1cc37cf642 100644
--- a/cereal/log.capnp
+++ b/cereal/log.capnp
@@ -728,7 +728,6 @@ struct PeripheralState {
struct RadarState @0x9a185389d6fdd05f {
mdMonoTime @6 :UInt64;
carStateMonoTime @11 :UInt64;
- radarErrors @12 :List(Car.RadarData.Error);
leadOne @3 :LeadData;
leadTwo @4 :LeadData;
@@ -762,6 +761,7 @@ struct RadarState @0x9a185389d6fdd05f {
calPercDEPRECATED @9 :Int8;
canMonoTimesDEPRECATED @10 :List(UInt64);
cumLagMsDEPRECATED @5 :Float32;
+ radarErrorsDEPRECATED @12 :List(Car.RadarData.ErrorDEPRECATED);
}
struct LiveCalibrationData {
diff --git a/opendbc_repo b/opendbc_repo
index de96aaf51b..5d74dd6e73 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit de96aaf51b60be02b4978b15fbf3b6dd0d829cfd
+Subproject commit 5d74dd6e7344900805c58eb2fb2b5a8d52e83050
diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py
index f00cbe55e1..8a21df3340 100755
--- a/selfdrive/car/card.py
+++ b/selfdrive/car/card.py
@@ -227,7 +227,7 @@ class Car:
if RD is not None:
tracks_msg = messaging.new_message('liveTracks')
- tracks_msg.valid = len(RD.errors) == 0
+ tracks_msg.valid = not any(RD.errors.to_dict().values())
tracks_msg.liveTracks = RD
self.pm.send('liveTracks', tracks_msg)
diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py
index 9a042ed372..9885358e71 100644
--- a/selfdrive/car/tests/test_models.py
+++ b/selfdrive/car/tests/test_models.py
@@ -219,7 +219,7 @@ class TestCarModelBase(unittest.TestCase):
for i, msg in enumerate(self.can_msgs[self.elm_frame:]):
rr: structs.RadarData | None = RI.update(msg)
if rr is not None and i > 50:
- error_cnt += structs.RadarData.Error.canError in rr.errors
+ error_cnt += rr.errors.canError
self.assertEqual(error_cnt, 0)
def test_panda_safety_rx_checks(self):
diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py
index 9f5bea53ff..f91c53431d 100755
--- a/selfdrive/controls/radard.py
+++ b/selfdrive/controls/radard.py
@@ -234,7 +234,6 @@ class RadarD:
self.radar_state_valid = sm.all_checks()
self.radar_state = log.RadarState.new_message()
self.radar_state.mdMonoTime = sm.logMonoTime['modelV2']
- self.radar_state.radarErrors = list(rr.errors)
self.radar_state.carStateMonoTime = sm.logMonoTime['carState']
if len(sm['modelV2'].velocity.x):
diff --git a/selfdrive/controls/tests/test_leads.py b/selfdrive/controls/tests/test_leads.py
index 89582d1e64..77384fea20 100644
--- a/selfdrive/controls/tests/test_leads.py
+++ b/selfdrive/controls/tests/test_leads.py
@@ -26,6 +26,6 @@ class TestLeads:
msgs = [m for _ in range(3) for m in single_iter_pkg()]
out = replay_process_with_name("card", msgs, fingerprint=TOYOTA.TOYOTA_COROLLA_TSS2)
states = [m for m in out if m.which() == "liveTracks"]
- failures = [not state.valid and len(state.liveTracks.errors) for state in states]
+ failures = [not state.valid for state in states]
assert len(states) == 0 or all(failures)
diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit
index 86bdc46fea..353b8fa7ce 100644
--- a/selfdrive/test/process_replay/ref_commit
+++ b/selfdrive/test/process_replay/ref_commit
@@ -1 +1 @@
-98672ccf23dc08fcd08b53cae3ec305c04219fe8
\ No newline at end of file
+e9d57157494480637a8ffb52257d2b660a48be67
\ No newline at end of file
From 4e7c605a7948ec4928f79beb37fb6dc0638e7bf2 Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Fri, 14 Mar 2025 21:08:30 -0700
Subject: [PATCH 39/68] Ford: radar is invalid while in reverse (#34866)
* temp unavailable
* fix
* replay multiple
* clean up
* bump
* rename
* reason is in liveTracks
* more rename
* bump
* fix
* bump
* fix
* need to totally ignore it, or it will throw commIssues for radarErrors or not alive
* this is just simpler -- good thing kept struct
* rm
---
cereal/log.capnp | 2 ++
opendbc_repo | 2 +-
selfdrive/controls/radard.py | 1 +
selfdrive/selfdrived/events.py | 5 +++++
selfdrive/selfdrived/selfdrived.py | 4 ++++
5 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/cereal/log.capnp b/cereal/log.capnp
index 1cc37cf642..7b6076311f 100644
--- a/cereal/log.capnp
+++ b/cereal/log.capnp
@@ -59,6 +59,7 @@ struct OnroadEvent @0xc4fa6047f024e718 {
pcmEnable @23;
pcmDisable @24;
radarFault @25;
+ radarTempUnavailable @93;
brakeHold @26;
parkBrake @27;
manualRestart @28;
@@ -728,6 +729,7 @@ struct PeripheralState {
struct RadarState @0x9a185389d6fdd05f {
mdMonoTime @6 :UInt64;
carStateMonoTime @11 :UInt64;
+ radarErrors @13 :Car.RadarData.Error;
leadOne @3 :LeadData;
leadTwo @4 :LeadData;
diff --git a/opendbc_repo b/opendbc_repo
index 5d74dd6e73..96896d3509 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit 5d74dd6e7344900805c58eb2fb2b5a8d52e83050
+Subproject commit 96896d35098464661de5c96f075bf5053eeefede
diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py
index f91c53431d..84420e3584 100755
--- a/selfdrive/controls/radard.py
+++ b/selfdrive/controls/radard.py
@@ -234,6 +234,7 @@ class RadarD:
self.radar_state_valid = sm.all_checks()
self.radar_state = log.RadarState.new_message()
self.radar_state.mdMonoTime = sm.logMonoTime['modelV2']
+ self.radar_state.radarErrors = rr.errors
self.radar_state.carStateMonoTime = sm.logMonoTime['carState']
if len(sm['modelV2'].velocity.x):
diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py
index d89418e177..ee2c418b6e 100755
--- a/selfdrive/selfdrived/events.py
+++ b/selfdrive/selfdrived/events.py
@@ -832,6 +832,11 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
ET.NO_ENTRY: NoEntryAlert("Radar Error: Restart the Car"),
},
+ EventName.radarTempUnavailable: {
+ ET.SOFT_DISABLE: soft_disable_alert("Radar Temporarily Unavailable"),
+ ET.NO_ENTRY: NoEntryAlert("Radar Temporarily Unavailable"),
+ },
+
# Every frame from the camera should be processed by the model. If modeld
# is not processing frames fast enough they have to be dropped. This alert is
# thrown when over 20% of frames are dropped.
diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py
index 93f8f59133..fdf1b77871 100755
--- a/selfdrive/selfdrived/selfdrived.py
+++ b/selfdrive/selfdrived/selfdrived.py
@@ -270,6 +270,10 @@ class SelfdriveD:
if not REPLAY and self.rk.lagging:
self.events.add(EventName.selfdrivedLagging)
if not self.sm.valid['radarState']:
+ if self.sm['radarState'].radarErrors.radarUnavailableTemporary:
+ self.events.add(EventName.radarTempUnavailable)
+ else:
+ self.events.add(EventName.radarFault)
self.events.add(EventName.radarFault)
if not self.sm.valid['pandaStates']:
self.events.add(EventName.usbError)
From 5c3020844e2abaae7ce25236723c61ce658b22d8 Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Fri, 14 Mar 2025 22:37:49 -0700
Subject: [PATCH 40/68] car interfaces: each specify their own dependencies
(#34874)
* remove these
* fix
* oops
* clean up
* nl
* bump
---
opendbc_repo | 2 +-
selfdrive/car/card.py | 4 ++--
selfdrive/car/tests/test_car_interfaces.py | 4 ++--
selfdrive/car/tests/test_models.py | 10 +++++-----
selfdrive/controls/controlsd.py | 4 ++--
selfdrive/controls/lib/tests/test_latcontrol.py | 4 ++--
selfdrive/test/process_replay/process_replay.py | 2 +-
7 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/opendbc_repo b/opendbc_repo
index 96896d3509..b46e6340c1 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit 96896d35098464661de5c96f075bf5053eeefede
+Subproject commit b46e6340c15428ef72435b163ce03ffe66ffe563
diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py
index 8a21df3340..607f5590c4 100755
--- a/selfdrive/car/card.py
+++ b/selfdrive/car/card.py
@@ -15,7 +15,7 @@ from opendbc.car import DT_CTRL, structs
from opendbc.car.can_definitions import CanData, CanRecvCallable, CanSendCallable
from opendbc.car.carlog import carlog
from opendbc.car.fw_versions import ObdCallback
-from opendbc.car.car_helpers import get_car, get_radar_interface
+from opendbc.car.car_helpers import get_car, interfaces
from opendbc.car.interfaces import CarInterfaceBase, RadarInterfaceBase
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp
@@ -99,7 +99,7 @@ class Car:
cached_params = _cached_params
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params)
- self.RI = get_radar_interface(self.CI.CP)
+ self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP)
self.CP = self.CI.CP
# continue onto next fingerprinting step in pandad
diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py
index ca96884d7c..e0cd5a1e91 100644
--- a/selfdrive/car/tests/test_car_interfaces.py
+++ b/selfdrive/car/tests/test_car_interfaces.py
@@ -34,14 +34,14 @@ class TestCarInterfaces:
phases=(Phase.reuse, Phase.generate, Phase.shrink))
@given(data=st.data())
def test_car_interfaces(self, car_name, data):
- CarInterface, CarController, CarState, RadarInterface = interfaces[car_name]
+ CarInterface = interfaces[car_name]
args = get_fuzzy_car_interface_args(data.draw)
car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'],
experimental_long=args['experimental_long'], docs=False)
car_params = car_params.as_reader()
- car_interface = CarInterface(car_params, CarController, CarState)
+ car_interface = CarInterface(car_params)
assert car_params
assert car_interface
diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py
index 9885358e71..906fa99320 100644
--- a/selfdrive/car/tests/test_models.py
+++ b/selfdrive/car/tests/test_models.py
@@ -151,8 +151,8 @@ class TestCarModelBase(unittest.TestCase):
# if relay is expected to be open in the route
cls.openpilot_enabled = cls.car_safety_mode_frame is not None
- cls.CarInterface, cls.CarController, cls.CarState, cls.RadarInterface = interfaces[cls.platform]
- cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False)
+ cls.CarInterface = interfaces[cls.platform]
+ cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False)
assert cls.CP
assert cls.CP.carFingerprint == cls.platform
@@ -163,7 +163,7 @@ class TestCarModelBase(unittest.TestCase):
del cls.can_msgs
def setUp(self):
- self.CI = self.CarInterface(self.CP.copy(), self.CarController, self.CarState)
+ self.CI = self.CarInterface(self.CP.copy())
assert self.CI
# TODO: check safetyModel is in release panda build
@@ -210,7 +210,7 @@ class TestCarModelBase(unittest.TestCase):
self.assertEqual(can_invalid_cnt, 0)
def test_radar_interface(self):
- RI = self.RadarInterface(self.CP)
+ RI = self.CarInterface.RadarInterface(self.CP)
assert RI
# Since OBD port is multiplexed to bus 1 (commonly radar bus) while fingerprinting,
@@ -273,7 +273,7 @@ class TestCarModelBase(unittest.TestCase):
def test_car_controller(car_control):
now_nanos = 0
msgs_sent = 0
- CI = self.CarInterface(self.CP, self.CarController, self.CarState)
+ CI = self.CarInterface(self.CP)
for _ in range(round(10.0 / DT_CTRL)): # make sure we hit the slowest messages
CI.update([])
_, sendcan = CI.apply(car_control, now_nanos)
diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py
index dcc1c74794..f6fabfc421 100755
--- a/selfdrive/controls/controlsd.py
+++ b/selfdrive/controls/controlsd.py
@@ -9,7 +9,7 @@ from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper
from openpilot.common.swaglog import cloudlog
-from opendbc.car.car_helpers import get_car_interface
+from opendbc.car.car_helpers import interfaces
from opendbc.car.vehicle_model import VehicleModel
from openpilot.selfdrive.controls.lib.drive_helpers import clip_curvature
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
@@ -33,7 +33,7 @@ class Controls:
self.CP = messaging.log_from_bytes(self.params.get("CarParams", block=True), car.CarParams)
cloudlog.info("controlsd got CarParams")
- self.CI = get_car_interface(self.CP)
+ self.CI = interfaces[self.CP.carFingerprint](self.CP)
self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/lib/tests/test_latcontrol.py
index 564c93be0d..a01f013ddd 100644
--- a/selfdrive/controls/lib/tests/test_latcontrol.py
+++ b/selfdrive/controls/lib/tests/test_latcontrol.py
@@ -17,9 +17,9 @@ class TestLatControl:
@parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque), (NISSAN.NISSAN_LEAF, LatControlAngle)])
def test_saturation(self, car_name, controller):
- CarInterface, CarController, CarState, RadarInterface = interfaces[car_name]
+ CarInterface = interfaces[car_name]
CP = CarInterface.get_non_essential_params(car_name)
- CI = CarInterface(CP, CarController, CarState)
+ CI = CarInterface(CP)
VM = VehicleModel(CP)
controller = controller(CP.as_reader(), CI)
diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py
index 34ca98fdc2..16761225d3 100755
--- a/selfdrive/test/process_replay/process_replay.py
+++ b/selfdrive/test/process_replay/process_replay.py
@@ -342,7 +342,7 @@ def card_fingerprint_callback(rc, pm, msgs, fingerprint):
def get_car_params_callback(rc, pm, msgs, fingerprint):
params = Params()
if fingerprint:
- CarInterface, _, _, _ = interfaces[fingerprint]
+ CarInterface = interfaces[fingerprint]
CP = CarInterface.get_non_essential_params(fingerprint)
else:
can = DummySocket()
From fb7b9c0f9420d228f03362970ebcfb7237095cf3 Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Sat, 15 Mar 2025 14:02:03 -0700
Subject: [PATCH 41/68] camerad: log failed sync time (#34878)
* camerad: log failed sync time
* mv
---------
Co-authored-by: Comma Device
---
system/camerad/cameras/spectra.cc | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc
index 247b0dd215..d12dbb2444 100644
--- a/system/camerad/cameras/spectra.cc
+++ b/system/camerad/cameras/spectra.cc
@@ -266,7 +266,7 @@ int SpectraCamera::clear_req_queue() {
req_mgr_flush_request.link_hdl = link_handle;
req_mgr_flush_request.flush_type = CAM_REQ_MGR_FLUSH_TYPE_ALL;
int ret = do_cam_control(m->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request));
- LOGD("flushed all req: %d", ret);
+ LOGD("flushed all req: %d", ret); // returns a "time until timeout" on clearing the workq
for (int i = 0; i < MAX_IFE_BUFS; ++i) {
destroySyncObjectAt(i);
@@ -1418,11 +1418,13 @@ bool SpectraCamera::waitForFrameReady(uint64_t request_id) {
}
auto waitForSync = [&](uint32_t sync_obj, int timeout_ms, const char *sync_type) {
+ double st = millis_since_boot();
struct cam_sync_wait sync_wait = {};
sync_wait.sync_obj = sync_obj;
sync_wait.timeout_ms = stress_test(sync_type) ? 1 : timeout_ms;
bool ret = do_sync_control(m->cam_sync_fd, CAM_SYNC_WAIT, &sync_wait, sizeof(sync_wait)) == 0;
- if (!ret) LOGE("camera %d %s failed", cc.camera_num, sync_type);
+ double et = millis_since_boot();
+ if (!ret) LOGE("camera %d %s failed after %.2fms", cc.camera_num, sync_type, et-st);
return ret;
};
@@ -1434,6 +1436,7 @@ bool SpectraCamera::waitForFrameReady(uint64_t request_id) {
// BPS is typically 7ms
success = waitForSync(sync_objs_bps[buf_idx], 50, "BPS sync");
}
+
return success;
}
From 4879e2f5ec7f38bfe938d79de0be9f787f893dcf Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Thu, 13 Mar 2025 04:26:18 -0400
Subject: [PATCH 42/68] Bump opendbc
---
opendbc_repo | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/opendbc_repo b/opendbc_repo
index 9cb8254180..9b83f9519d 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit 9cb8254180ae2462e25cd2abce6485b55f02b605
+Subproject commit 9b83f9519db61c96facb5e20b35d69c13ef3bd91
From db0ce99c016cbd7a8668d837702999e57f183c47 Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Sat, 15 Mar 2025 17:55:41 -0400
Subject: [PATCH 43/68] Car interface: custom safety params (#659)
* init
* do it for hyundai
* Bump submodules
* no complaints
* use latest opendbc pr ref
* don't need this for leaf for now
* bump opendbc
* gotta set the sp safety flags in test models and safety replay!
* actually fetch CP_SP
* set safety param for lda button availability (lol forgot)
* use GET_FLAG
* Add TODO-SP
* bump submodules
---
cereal/custom.capnp | 3 ++-
panda | 2 +-
selfdrive/car/tests/test_models.py | 3 +++
selfdrive/pandad/panda.cc | 4 ++--
selfdrive/pandad/panda.h | 2 +-
selfdrive/pandad/panda_safety.cc | 21 ++++++++++++++-------
selfdrive/pandad/pandad.h | 4 ++--
sunnypilot/mads/helpers.py | 5 ++---
8 files changed, 27 insertions(+), 17 deletions(-)
diff --git a/cereal/custom.capnp b/cereal/custom.capnp
index 36cc2c9498..524ecdd657 100644
--- a/cereal/custom.capnp
+++ b/cereal/custom.capnp
@@ -136,7 +136,8 @@ struct OnroadEventSP @0xda96579883444c35 {
}
struct CarParamsSP @0x80ae746ee2596b11 {
- flags @0 :UInt32; # flags for car specific quirks in sunnypilot
+ flags @0 :UInt32; # flags for car specific quirks in sunnypilot
+ safetyParam @1 : Int16; # flags for sunnypilot's custom safety flags
}
struct CarControlSP @0xa5cd762cd951a455 {
diff --git a/panda b/panda
index 5479e1572a..ccb9581b1c 160000
--- a/panda
+++ b/panda
@@ -1 +1 @@
-Subproject commit 5479e1572ad1d2733c54e0acdd1a8fa6a960fab9
+Subproject commit ccb9581b1ce08ff899b50d986c5e2b155ce8c339
diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py
index 7698c12c07..573d818365 100644
--- a/selfdrive/car/tests/test_models.py
+++ b/selfdrive/car/tests/test_models.py
@@ -172,6 +172,9 @@ class TestCarModelBase(unittest.TestCase):
# TODO: check safetyModel is in release panda build
self.safety = libsafety_py.libsafety
+ safety_param_sp = self.CP_SP.safetyParam
+ self.safety.set_current_safety_param_sp(safety_param_sp)
+
cfg = self.CP.safetyConfigs[-1]
set_status = self.safety.set_safety_hooks(cfg.safetyModel.raw, cfg.safetyParam)
self.assertEqual(0, set_status, f"failed to set safetyModel {cfg}")
diff --git a/selfdrive/pandad/panda.cc b/selfdrive/pandad/panda.cc
index 45d835e5a4..8d8984c2d2 100644
--- a/selfdrive/pandad/panda.cc
+++ b/selfdrive/pandad/panda.cc
@@ -62,8 +62,8 @@ void Panda::set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16
handle->control_write(0xdc, (uint16_t)safety_model, safety_param);
}
-void Panda::set_alternative_experience(uint16_t alternative_experience) {
- handle->control_write(0xdf, alternative_experience, 0);
+void Panda::set_alternative_experience(uint16_t alternative_experience, uint16_t safety_param_sp) {
+ handle->control_write(0xdf, alternative_experience, safety_param_sp);
}
cereal::PandaState::PandaType Panda::get_hw_type() {
diff --git a/selfdrive/pandad/panda.h b/selfdrive/pandad/panda.h
index 3e0f4b8507..5ed87104df 100644
--- a/selfdrive/pandad/panda.h
+++ b/selfdrive/pandad/panda.h
@@ -63,7 +63,7 @@ public:
// Panda functionality
cereal::PandaState::PandaType get_hw_type();
void set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param=0U);
- void set_alternative_experience(uint16_t alternative_experience);
+ void set_alternative_experience(uint16_t alternative_experience, uint16_t safety_param_sp=0U);
void set_fan_speed(uint16_t fan_speed);
uint16_t get_fan_speed();
void set_ir_pwr(uint16_t ir_pwr);
diff --git a/selfdrive/pandad/panda_safety.cc b/selfdrive/pandad/panda_safety.cc
index 6d5e5ce4f3..ef9b459e5b 100644
--- a/selfdrive/pandad/panda_safety.cc
+++ b/selfdrive/pandad/panda_safety.cc
@@ -10,7 +10,8 @@ void PandaSafety::configureSafetyMode() {
auto car_params = fetchCarParams();
if (!car_params.empty()) {
- LOGW("got %lu bytes CarParams", car_params.size());
+ LOGW("got %lu bytes CarParams", car_params[0].size());
+ LOGW("got %lu bytes CarParamsSP", car_params[1].size());
setSafetyMode(car_params);
safety_configured_ = true;
}
@@ -43,7 +44,8 @@ void PandaSafety::updateMultiplexingMode() {
}
}
-std::string PandaSafety::fetchCarParams() {
+// TODO-SP: Use structs instead of vector
+std::vector PandaSafety::fetchCarParams() {
if (!params_.getBool("FirmwareQueryDone")) {
return {};
}
@@ -56,16 +58,21 @@ std::string PandaSafety::fetchCarParams() {
if (!params_.getBool("ControlsReady")) {
return {};
}
- return params_.get("CarParams");
+ return {params_.get("CarParams"), params_.get("CarParamsSP")};
}
-void PandaSafety::setSafetyMode(const std::string ¶ms_string) {
+// TODO-SP: Use structs instead of vector
+void PandaSafety::setSafetyMode(const std::vector ¶ms_string) {
AlignedBuffer aligned_buf;
- capnp::FlatArrayMessageReader cmsg(aligned_buf.align(params_string.data(), params_string.size()));
+ capnp::FlatArrayMessageReader cmsg(aligned_buf.align(params_string[0].data(), params_string[0].size()));
cereal::CarParams::Reader car_params = cmsg.getRoot();
+ capnp::FlatArrayMessageReader cmsg_sp(aligned_buf.align(params_string[1].data(), params_string[1].size()));
+ cereal::CarParamsSP::Reader car_params_sp = cmsg_sp.getRoot();
+
auto safety_configs = car_params.getSafetyConfigs();
uint16_t alternative_experience = car_params.getAlternativeExperience();
+ uint16_t safety_param_sp = car_params_sp.getSafetyParam();
for (int i = 0; i < pandas_.size(); ++i) {
// Default to SILENT safety model if not specified
@@ -76,8 +83,8 @@ void PandaSafety::setSafetyMode(const std::string ¶ms_string) {
safety_param = safety_configs[i].getSafetyParam();
}
- LOGW("Panda %d: setting safety model: %d, param: %d, alternative experience: %d", i, (int)safety_model, safety_param, alternative_experience);
- pandas_[i]->set_alternative_experience(alternative_experience);
+ LOGW("Panda %d: setting safety model: %d, param: %d, alternative experience: %d, param_sp: %d", i, (int)safety_model, safety_param, alternative_experience, safety_param_sp);
+ pandas_[i]->set_alternative_experience(alternative_experience, safety_param_sp);
pandas_[i]->set_safety_model(safety_model, safety_param);
}
}
diff --git a/selfdrive/pandad/pandad.h b/selfdrive/pandad/pandad.h
index b338b70378..7b37fbe59a 100644
--- a/selfdrive/pandad/pandad.h
+++ b/selfdrive/pandad/pandad.h
@@ -16,8 +16,8 @@ public:
private:
void updateMultiplexingMode();
- std::string fetchCarParams();
- void setSafetyMode(const std::string ¶ms_string);
+ std::vector fetchCarParams();
+ void setSafetyMode(const std::vector ¶ms_string);
bool initialized_ = false;
bool log_once_ = false;
diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py
index 9dc47b26a8..16a5a74dac 100644
--- a/sunnypilot/mads/helpers.py
+++ b/sunnypilot/mads/helpers.py
@@ -27,8 +27,7 @@ Last updated: July 29, 2024
from openpilot.common.params import Params
from opendbc.safety import ALTERNATIVE_EXPERIENCE
-from opendbc.car.hyundai.values import HyundaiSafetyFlags
-from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
+from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP, HyundaiSafetyFlagsSP
class MadsParams:
@@ -55,7 +54,7 @@ class MadsParams:
hyundai_cruise_main_toggleable = True
if hyundai_cruise_main_toggleable:
CP_SP.flags |= HyundaiFlagsSP.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE.value
- CP.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.FLAG_HYUNDAI_LONG_MAIN_CRUISE_TOGGLEABLE.value
+ CP_SP.safetyParam |= HyundaiSafetyFlagsSP.LONG_MAIN_CRUISE_TOGGLEABLE
# MADS is currently not supported in Tesla due to lack of consistent states to engage controls
# TODO-SP: To enable MADS for Tesla, identify consistent signals for MADS toggling
From 519ea9dcab2affdd3edcd10401675fee8dbdeaff Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Sat, 15 Mar 2025 15:55:29 -0700
Subject: [PATCH 44/68] Make model daemons Python processes (#34880)
---
system/manager/process.py | 6 +++++-
system/manager/process_config.py | 6 ++----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/system/manager/process.py b/system/manager/process.py
index 0e9c9b9804..6c233c21ab 100644
--- a/system/manager/process.py
+++ b/system/manager/process.py
@@ -221,8 +221,12 @@ class PythonProcess(ManagerProcess):
if self.proc is not None:
return
+ # TODO: this is just a workaround for this tinygrad check:
+ # https://github.com/tinygrad/tinygrad/blob/ac9c96dae1656dc220ee4acc39cef4dd449aa850/tinygrad/device.py#L26
+ name = self.name if "modeld" not in self.name else "MainProcess"
+
cloudlog.info(f"starting python {self.module}")
- self.proc = Process(name=self.name, target=self.launcher, args=(self.module, self.name))
+ self.proc = Process(name=name, target=self.launcher, args=(self.module, self.name))
self.proc.start()
self.watchdog_seen = False
self.shutting_down = False
diff --git a/system/manager/process_config.py b/system/manager/process_config.py
index 96315eeb29..6e048c339e 100644
--- a/system/manager/process_config.py
+++ b/system/manager/process_config.py
@@ -75,10 +75,8 @@ procs = [
PythonProcess("micd", "system.micd", iscar),
PythonProcess("timed", "system.timed", always_run, enabled=not PC),
- # TODO: Make python process once TG allows opening QCOM from child pro
- # https://github.com/tinygrad/tinygrad/blob/ac9c96dae1656dc220ee4acc39cef4dd449aa850/tinygrad/device.py#L26
- NativeProcess("modeld", "selfdrive/modeld", ["./modeld.py"], only_onroad),
- NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld.py"], driverview, enabled=(WEBCAM or not PC)),
+ PythonProcess("modeld", "selfdrive.modeld.modeld", only_onroad),
+ PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)),
NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC),
NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)),
From 7f669ec1e7a47640a3213f5b9aced78bc6ff81fe Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Sat, 15 Mar 2025 22:28:32 -0400
Subject: [PATCH 45/68] MADS: Disable for Rivian (#663)
---
sunnypilot/mads/helpers.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py
index 16a5a74dac..8a7ffe80ec 100644
--- a/sunnypilot/mads/helpers.py
+++ b/sunnypilot/mads/helpers.py
@@ -60,3 +60,8 @@ class MadsParams:
# TODO-SP: To enable MADS for Tesla, identify consistent signals for MADS toggling
if CP.brand == "tesla":
self.params.remove("Mads")
+
+ # MADS is currently not supported in Rivian due to lack of consistent states to engage controls
+ # TODO-SP: To enable MADS for Rivian, identify consistent signals for MADS toggling
+ if CP.brand == "rivian":
+ self.params.remove("Mads")
From 561d33d81e4cf64d22897bc8ca1e09c19d205312 Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Sat, 15 Mar 2025 20:55:03 -0700
Subject: [PATCH 46/68] remove '-release' suffix
---
release/build_release.sh | 2 --
1 file changed, 2 deletions(-)
diff --git a/release/build_release.sh b/release/build_release.sh
index 735ef5e043..220da05c17 100755
--- a/release/build_release.sh
+++ b/release/build_release.sh
@@ -40,8 +40,6 @@ rm -f panda/board/obj/panda.bin.signed
rm -f panda/board/obj/panda_h7.bin.signed
VERSION=$(cat common/version.h | awk -F[\"-] '{print $2}')
-echo "#define COMMA_VERSION \"$VERSION-release\"" > common/version.h
-
echo "[-] committing version $VERSION T=$SECONDS"
git add -f .
git commit -a -m "openpilot v$VERSION release"
From 4ef46251a7a9267ee24f37288d7e185ddaf91faa Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Sun, 16 Mar 2025 00:09:11 -0400
Subject: [PATCH 47/68] ui: Trim panel name for Firehose panel redirect (#664)
* ui: Trim panel name for Firehose panel redirect
* unused
* extra
---
selfdrive/ui/qt/offroad/settings.cc | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc
index c6d3520712..a5bfe61ffd 100644
--- a/selfdrive/ui/qt/offroad/settings.cc
+++ b/selfdrive/ui/qt/offroad/settings.cc
@@ -344,7 +344,11 @@ void SettingsWindow::setCurrentPanel(int index, const QString ¶m) {
// Find the panel by name
for (int i = 0; i < nav_btns->buttons().size(); i++) {
- if (nav_btns->buttons()[i]->text() == tr(panelName.toStdString().c_str())) {
+ bool panel_trimmed = false;
+#ifdef SUNNYPILOT
+ panel_trimmed = nav_btns->buttons()[i]->text().trimmed() == tr(panelName.toStdString().c_str());
+#endif
+ if ((nav_btns->buttons()[i]->text() == tr(panelName.toStdString().c_str())) || panel_trimmed) {
index = i;
break;
}
From c0b39d328db4e467a67557e63fd67cd64177da0e Mon Sep 17 00:00:00 2001
From: Adeeb Shihadeh
Date: Sat, 15 Mar 2025 22:08:43 -0700
Subject: [PATCH 48/68] fix typo
---
RELEASES.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/RELEASES.md b/RELEASES.md
index 8a5638bc84..071622f89a 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -6,7 +6,7 @@ Version 0.9.8 (2025-02-28)
========================
* New driving model
* Model now gates applying positive acceleration in Chill mode
-* New driving monitoring model
+* New driver monitoring model
* Reduced false positives related to passengers
* Image processing pipeline moved to the ISP
* More GPU time for bigger driving models
From abc1c5fb959810a654c6115e80b29fb18fc4bb19 Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Sat, 15 Mar 2025 22:21:58 -0700
Subject: [PATCH 49/68] selfdrived: add canError for radar can errors (#34873)
radar can invalid
---
selfdrive/selfdrived/selfdrived.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py
index fdf1b77871..c7320f9350 100755
--- a/selfdrive/selfdrived/selfdrived.py
+++ b/selfdrive/selfdrived/selfdrived.py
@@ -270,7 +270,9 @@ class SelfdriveD:
if not REPLAY and self.rk.lagging:
self.events.add(EventName.selfdrivedLagging)
if not self.sm.valid['radarState']:
- if self.sm['radarState'].radarErrors.radarUnavailableTemporary:
+ if self.sm['radarState'].radarErrors.canError:
+ self.events.add(EventName.canError)
+ elif self.sm['radarState'].radarErrors.radarUnavailableTemporary:
self.events.add(EventName.radarTempUnavailable)
else:
self.events.add(EventName.radarFault)
From f50d7a1842bf93f4f5b6ff08707089046d4061ca Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Sun, 16 Mar 2025 02:34:25 -0400
Subject: [PATCH 50/68] ui: Update FirehosePanel with sunnypilot ui updates
(#666)
---
selfdrive/ui/qt/offroad/firehose.cc | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/selfdrive/ui/qt/offroad/firehose.cc b/selfdrive/ui/qt/offroad/firehose.cc
index fd173a1c7a..e6a9c5c6e9 100644
--- a/selfdrive/ui/qt/offroad/firehose.cc
+++ b/selfdrive/ui/qt/offroad/firehose.cc
@@ -12,6 +12,10 @@
#include
#include
+#ifdef SUNNYPILOT
+#define UIState UIStateSP
+#endif
+
FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) {
layout = new QVBoxLayout(this);
layout->setContentsMargins(40, 40, 40, 40);
From 9571fc56079bf13f6ca74ed9cb0eb98c7b8755eb Mon Sep 17 00:00:00 2001
From: Shane Smiskol
Date: Sat, 15 Mar 2025 23:49:22 -0700
Subject: [PATCH 51/68] radard: use a filter for aLeadTau (#34883)
use a filter
---
selfdrive/controls/radard.py | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py
index 84420e3584..98fce1cb26 100755
--- a/selfdrive/controls/radard.py
+++ b/selfdrive/controls/radard.py
@@ -6,6 +6,7 @@ from typing import Any
import capnp
from cereal import messaging, log, car
+from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.params import Params
from openpilot.common.realtime import DT_MDL, Priority, config_realtime_process
from openpilot.common.swaglog import cloudlog
@@ -51,7 +52,7 @@ class Track:
def __init__(self, identifier: int, v_lead: float, kalman_params: KalmanParams):
self.identifier = identifier
self.cnt = 0
- self.aLeadTau = _LEAD_ACCEL_TAU
+ self.aLeadTau = FirstOrderFilter(_LEAD_ACCEL_TAU, 0.45, DT_MDL)
self.K_A = kalman_params.A
self.K_C = kalman_params.C
self.K_K = kalman_params.K
@@ -74,17 +75,12 @@ class Track:
# Learn if constant acceleration
if abs(self.aLeadK) < 0.5:
- self.aLeadTau = _LEAD_ACCEL_TAU
+ self.aLeadTau.x = _LEAD_ACCEL_TAU
else:
- self.aLeadTau *= 0.9
+ self.aLeadTau.update(0.0)
self.cnt += 1
- def reset_a_lead(self, aLeadK: float, aLeadTau: float):
- self.kf = KF1D([[self.vLead], [aLeadK]], self.K_A, self.K_C, self.K_K)
- self.aLeadK = aLeadK
- self.aLeadTau = aLeadTau
-
def get_RadarState(self, model_prob: float = 0.0):
return {
"dRel": float(self.dRel),
@@ -93,7 +89,7 @@ class Track:
"vLead": float(self.vLead),
"vLeadK": float(self.vLeadK),
"aLeadK": float(self.aLeadK),
- "aLeadTau": float(self.aLeadTau),
+ "aLeadTau": float(self.aLeadTau.x),
"status": True,
"fcw": self.is_potential_fcw(model_prob),
"modelProb": model_prob,
From 4fbf7a81b43a2030355f4194fe8374857a0eb350 Mon Sep 17 00:00:00 2001
From: DevTekVE
Date: Sun, 16 Mar 2025 16:12:43 +0100
Subject: [PATCH 52/68] sunnylink: Fix IMEI fetching during registration (#661)
Fix getting the imei for the registration
---
sunnypilot/sunnylink/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sunnypilot/sunnylink/api.py b/sunnypilot/sunnylink/api.py
index 4abefa12f8..9f82fd2d20 100644
--- a/sunnypilot/sunnylink/api.py
+++ b/sunnypilot/sunnylink/api.py
@@ -55,7 +55,7 @@ class SunnylinkApi(BaseApi):
imei_try = 0
while imei1 is None and imei2 is None and imei_try < MAX_RETRIES:
try:
- imei1, imei2 = self.params.get("IMEI", encoding='utf8') or HARDWARE.get_imei(0), HARDWARE.get_imei(1)
+ imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
except Exception:
self._status_update(f"Error getting imei, trying again... [{imei_try + 1}/{MAX_RETRIES}]")
time.sleep(1)
From 61b9d81ba6aa6dd3ad1c8620939957fe2d252644 Mon Sep 17 00:00:00 2001
From: DevTekVE
Date: Sun, 16 Mar 2025 17:08:21 +0100
Subject: [PATCH 53/68] CI: identify new screenshots in UI preview (#668)
Added checks for new UI files in PRs
The git workflow script `ui_preview.yaml` has been modified. The script now checks if the master branch contains a file corresponding to a UI file present in the PR. If a UI file in the PR does not have a match on the master branch, it is marked as new. These enhancements improve the comparison of UI changes between the master and PR branches, particularly with the identification of new UI files.
---
.github/workflows/ui_preview.yaml | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml
index 2ded4a2d2a..28198d7dbb 100644
--- a/.github/workflows/ui_preview.yaml
+++ b/.github/workflows/ui_preview.yaml
@@ -95,8 +95,21 @@ jobs:
for ((i=0; i<${#A[*]}; i=i+1));
do
+ # Check if the master file exists
+ if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then
+ # This is a new file in PR UI that doesn't exist in master
+ DIFF="${DIFF}"
+ DIFF="${DIFF}${A[$i]} : \$\${\\color{blue}\\text{NEW}}\$\$"
+ DIFF="${DIFF}
"
- if ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
+ DIFF="${DIFF}
"
+ DIFF="${DIFF}
master
"
+ DIFF="${DIFF}
proposed
"
+ DIFF="${DIFF}
"
+
+ DIFF="${DIFF}
"
+ DIFF="${DIFF}"
+ elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png
composite mask.png ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png
convert -delay 100 ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
From 89e385b692bb09b1cefd8ee3df4a4be82d0fcc9b Mon Sep 17 00:00:00 2001
From: DevTekVE
Date: Sun, 16 Mar 2025 18:01:31 +0100
Subject: [PATCH 54/68] CI: update default screenshot in UI preview (#669)
Jason Jason...
---
.github/workflows/ui_preview.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml
index 28198d7dbb..9963f2958c 100644
--- a/.github/workflows/ui_preview.yaml
+++ b/.github/workflows/ui_preview.yaml
@@ -99,11 +99,11 @@ jobs:
if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then
# This is a new file in PR UI that doesn't exist in master
DIFF="${DIFF}"
- DIFF="${DIFF}${A[$i]} : \$\${\\color{blue}\\text{NEW}}\$\$"
+ DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$"
DIFF="${DIFF}
"
DIFF="${DIFF}
"
- DIFF="${DIFF}
master
"
+ DIFF="${DIFF}
master
"
DIFF="${DIFF}
proposed
"
DIFF="${DIFF}
"
From cac85271150a2e421233073c2269092868fb13d6 Mon Sep 17 00:00:00 2001
From: DevTekVE
Date: Sun, 16 Mar 2025 21:22:14 +0100
Subject: [PATCH 55/68] sunnylink: pairing and sponsorship (#523)
* Refactor sunnylink panel code for clarity and initialization fixes.
Replaced explicit pointer types with `auto` for cleaner code and added proper initialization for the `offroad` boolean member. Simplified toggle logic by consolidating description updates for enabling/disabling sunnylink. These changes improve code readability and maintainability.
* Add Sunnylink sponsor and GitHub pairing functionality
This update introduces a feature to manage sponsorship-based roles and GitHub account pairing for Sunnylink. It includes new sponsor popups, sponsor-specific widgets, QR code logic, and backend API integrations. Additionally, new models and services support sponsor tier management and user-role synchronization.
* Translation files
* Param keys
* Add setup functions for SunnyLink sponsor and pair buttons
Introduce `setup_settings_sunnylink_sponsor_button` and `setup_settings_sunnylink_pair_button` to handle specific SunnyLink UI interactions. These functions streamline button clicks for sponsor and pairing actions within SunnyLink settings.
* Add new SunnyLink test cases for sponsor and pair buttons
Added `settings_sunnylink_sponsor_button` and `settings_sunnylink_pair_button` to the UI test case dictionary. This extends the SunnyLink test coverage to include sponsor and pairing functionalities.
* No need to import sunnylink from here, and it causes just circular dependency
* Enhance SunnylinkPanel functionality in off-road settings
This commit enhances the functionality of the SunnylinkPanel in the off-road settings of the SunnyPilot user interface. A paramWatcher is added to the SunnylinkPanel to observe "SunnylinkEnabled" parameter changes. Update functionalities are enhanced to handle showing and hiding of components based on various circumstances, such as whether the system is 'on-road' or 'off-road', and whether Sunnylink is enabled or not. The stopSunnylink and startSunnylink functions were also added to start or stop processes accordingly when Sunnylink is enabled or disabled. Additionally, the ui.h file is updated to efficiently handle Sunnylink roles and device users.
* Refactor SunnylinkPanel initialization and handling.
Reorganized SunnylinkPanel to improve structure and clarity by separating sunnylink client initialization and list widget setup. Enabled automatic sunnylink startup when the feature is enabled. Added minor formatting fixes for label display consistency.
* Add missing include for in ui.h
Including ensures compatibility with standard C++ features and prevents potential compilation errors. This addition aligns with best practices for maintaining robust and clean code.
* Updated setup_settings_sunnylink_sponsor_button and setup_settings_sunnylink_pair_button function signatures
Added an optional 'scroll' parameter to the setup_settings_sunnylink_sponsor_button and setup_settings_sunnylink_pair_button functions in the test_ui module. The modifications were made to allow for more flexible function usage by potentially enabling scroll operations during the execution of these UI setup steps.
* Enable Sunnylink initialization on panel show event
Begin Sunnylink connection automatically when the panel is displayed, ensuring the feature is active if enabled. Additionally, update the sponsor button text formatting for more concise styling.
* Translations
* Added checks for new UI files in PRs
The git workflow script `ui_preview.yaml` has been modified. The script now checks if the master branch contains a file corresponding to a UI file present in the PR. If a UI file in the PR does not have a match on the master branch, it is marked as new. These enhancements improve the comparison of UI changes between the master and PR branches, particularly with the identification of new UI files.
* cleanup
* duh
---------
Co-authored-by: Jason Wen
---
common/api/__init__.py | 13 +-
common/params_keys.h | 2 +
selfdrive/ui/qt/api.cc | 25 +--
selfdrive/ui/qt/api.h | 13 +-
selfdrive/ui/sunnypilot/SConscript | 12 +-
selfdrive/ui/sunnypilot/qt/api.cc | 63 ++++++++
selfdrive/ui/sunnypilot/qt/api.h | 36 +++++
.../qt/network/sunnylink/models/role_model.h | 56 +++++++
.../sunnylink/models/sponsor_role_model.h | 83 ++++++++++
.../qt/network/sunnylink/models/user_model.h | 37 +++++
.../sunnylink/services/base_device_service.cc | 57 +++++++
.../sunnylink/services/base_device_service.h | 31 ++++
.../sunnylink/services/role_service.cc | 36 +++++
.../network/sunnylink/services/role_service.h | 32 ++++
.../sunnylink/services/user_service.cc | 42 ++++++
.../network/sunnylink/services/user_service.h | 32 ++++
.../qt/network/sunnylink/sunnylink_client.cc | 14 ++
.../qt/network/sunnylink/sunnylink_client.h | 22 +++
.../settings/sunnylink/sponsor_widget.cc | 142 ++++++++++++++++++
.../settings/sunnylink/sponsor_widget.h | 48 ++++++
.../qt/offroad/settings/sunnylink_panel.cc | 139 ++++++++++++++---
.../qt/offroad/settings/sunnylink_panel.h | 20 ++-
.../ui/sunnypilot/qt/request_repeater.cc | 41 +++++
selfdrive/ui/sunnypilot/qt/request_repeater.h | 33 ++++
selfdrive/ui/sunnypilot/ui.cc | 10 ++
selfdrive/ui/sunnypilot/ui.h | 47 ++++++
selfdrive/ui/tests/test_ui/run.py | 12 ++
selfdrive/ui/translations/main_ar.ts | 85 ++++++++++-
selfdrive/ui/translations/main_de.ts | 83 ++++++++++
selfdrive/ui/translations/main_es.ts | 87 +++++++++++
selfdrive/ui/translations/main_fr.ts | 83 ++++++++++
selfdrive/ui/translations/main_ja.ts | 85 ++++++++++-
selfdrive/ui/translations/main_ko.ts | 85 ++++++++++-
selfdrive/ui/translations/main_pt-BR.ts | 85 ++++++++++-
selfdrive/ui/translations/main_th.ts | 83 ++++++++++
selfdrive/ui/translations/main_tr.ts | 83 ++++++++++
selfdrive/ui/translations/main_zh-CHS.ts | 85 ++++++++++-
selfdrive/ui/translations/main_zh-CHT.ts | 85 ++++++++++-
38 files changed, 1973 insertions(+), 54 deletions(-)
create mode 100644 selfdrive/ui/sunnypilot/qt/api.cc
create mode 100644 selfdrive/ui/sunnypilot/qt/api.h
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.cc
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.cc
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.cc
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.cc
create mode 100644 selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h
create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc
create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.h
create mode 100644 selfdrive/ui/sunnypilot/qt/request_repeater.cc
create mode 100644 selfdrive/ui/sunnypilot/qt/request_repeater.h
diff --git a/common/api/__init__.py b/common/api/__init__.py
index 5542760802..3e238ccc7a 100644
--- a/common/api/__init__.py
+++ b/common/api/__init__.py
@@ -1,13 +1,9 @@
from openpilot.common.api.comma_connect import CommaConnectApi
-from sunnypilot.sunnylink.api import SunnylinkApi
class Api:
- def __init__(self, dongle_id, use_sunnylink=False):
- if use_sunnylink:
- self.service = SunnylinkApi(dongle_id)
- else:
- self.service = CommaConnectApi(dongle_id)
+ def __init__(self, dongle_id):
+ self.service = CommaConnectApi(dongle_id)
def request(self, method, endpoint, **params):
return self.service.request(method, endpoint, **params)
@@ -22,6 +18,5 @@ class Api:
return self.service.get_token(expiry_hours)
-def api_get(endpoint, method='GET', timeout=None, access_token=None, use_sunnylink=False, **params):
- return SunnylinkApi(None).api_get(endpoint, method, timeout, access_token, **params) if use_sunnylink \
- else CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
+def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
+ return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
diff --git a/common/params_keys.h b/common/params_keys.h
index 307f7ada46..6bcf897c3e 100644
--- a/common/params_keys.h
+++ b/common/params_keys.h
@@ -144,6 +144,8 @@ inline static std::unordered_map keys = {
// sunnylink params
{"EnableSunnylinkUploader", PERSISTENT | BACKUP},
{"LastSunnylinkPingTime", CLEAR_ON_MANAGER_START},
+ {"SunnylinkCache_Roles", PERSISTENT},
+ {"SunnylinkCache_Users", PERSISTENT},
{"SunnylinkDongleId", PERSISTENT},
{"SunnylinkdPid", PERSISTENT},
{"SunnylinkEnabled", PERSISTENT},
diff --git a/selfdrive/ui/qt/api.cc b/selfdrive/ui/qt/api.cc
index 6889b40e51..85a3144bd2 100644
--- a/selfdrive/ui/qt/api.cc
+++ b/selfdrive/ui/qt/api.cc
@@ -79,31 +79,36 @@ bool HttpRequest::timeout() const {
return reply && reply->error() == QNetworkReply::OperationCanceledError;
}
-void HttpRequest::sendRequest(const QString &requestURL, const HttpRequest::Method method) {
- if (active()) {
- qDebug() << "HttpRequest is active";
- return;
- }
+QNetworkRequest HttpRequest::prepareRequest(const QString &requestURL) {
+ QNetworkRequest request;
QString token;
if (create_jwt) {
- token = CommaApi::create_jwt();
+ token = GetJwtToken();
} else {
QString token_json = QString::fromStdString(util::read_file(util::getenv("HOME") + "/.comma/auth.json"));
QJsonDocument json_d = QJsonDocument::fromJson(token_json.toUtf8());
token = json_d["access_token"].toString();
}
- QNetworkRequest request;
request.setUrl(QUrl(requestURL));
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
+ request.setRawHeader("User-Agent", GetUserAgent().toUtf8());
if (!token.isEmpty()) {
request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8());
}
+ return request;
+}
- if (method == HttpRequest::Method::GET) {
+void HttpRequest::sendRequest(const QString &requestURL, const Method method) {
+ if (active()) {
+ qDebug() << "HttpRequest is active";
+ return;
+ }
+
+ QNetworkRequest request = prepareRequest(requestURL);
+ if (method == Method::GET) {
reply = nam()->get(request);
- } else if (method == HttpRequest::Method::DELETE) {
+ } else if (method == Method::DELETE) {
reply = nam()->deleteResource(request);
}
diff --git a/selfdrive/ui/qt/api.h b/selfdrive/ui/qt/api.h
index ad64d7e722..80b85f7ca7 100644
--- a/selfdrive/ui/qt/api.h
+++ b/selfdrive/ui/qt/api.h
@@ -5,6 +5,7 @@
#include
#include
+#include "util.h"
#include "common/util.h"
namespace CommaApi {
@@ -23,10 +24,11 @@ class HttpRequest : public QObject {
Q_OBJECT
public:
- enum class Method {GET, DELETE};
+ enum class Method {GET, DELETE, POST, PUT};
explicit HttpRequest(QObject* parent, bool create_jwt = true, int timeout = 20000);
- void sendRequest(const QString &requestURL, const Method method = Method::GET);
+ virtual void sendRequest(const QString &requestURL, Method method);
+ void sendRequest(const QString &requestURL) { sendRequest(requestURL, Method::GET);}
bool active() const;
bool timeout() const;
@@ -35,13 +37,14 @@ signals:
protected:
QNetworkReply *reply = nullptr;
-
-private:
static QNetworkAccessManager *nam();
QTimer *networkTimer = nullptr;
bool create_jwt;
+ virtual QNetworkRequest prepareRequest(const QString& requestURL);
+ [[nodiscard]] virtual QString GetJwtToken() const { return CommaApi::create_jwt(); }
+ [[nodiscard]] virtual QString GetUserAgent() const { return getUserAgent(); }
-private slots:
+protected slots:
void requestTimeout();
void requestFinished();
};
diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript
index 2975450124..4ebfc10940 100644
--- a/selfdrive/ui/sunnypilot/SConscript
+++ b/selfdrive/ui/sunnypilot/SConscript
@@ -1,4 +1,5 @@
widgets_src = [
+ "sunnypilot/qt/request_repeater.cc",
"sunnypilot/qt/widgets/toggle.cc",
"sunnypilot/qt/widgets/controls.cc",
"sunnypilot/qt/widgets/drive_stats.cc",
@@ -13,6 +14,7 @@ qt_util = [
qt_src = [
"sunnypilot/ui.cc",
+ "sunnypilot/qt/api.cc",
"sunnypilot/qt/sidebar.cc",
"sunnypilot/qt/window.cc",
"sunnypilot/qt/home.cc",
@@ -21,6 +23,7 @@ qt_src = [
"sunnypilot/qt/offroad/settings/settings.cc",
"sunnypilot/qt/offroad/settings/software_panel.cc",
"sunnypilot/qt/offroad/settings/sunnylink_panel.cc",
+ "sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc",
"sunnypilot/qt/offroad/settings/sunnypilot_panel.cc",
"sunnypilot/qt/offroad/settings/trips_panel.cc",
"sunnypilot/qt/offroad/settings/vehicle_panel.cc",
@@ -35,11 +38,18 @@ sunnypilot_panel_qt_src = [
"sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc",
]
+network_src = [
+ "sunnypilot/qt/network/sunnylink/sunnylink_client.cc",
+ "sunnypilot/qt/network/sunnylink/services/base_device_service.cc",
+ "sunnypilot/qt/network/sunnylink/services/role_service.cc",
+ "sunnypilot/qt/network/sunnylink/services/user_service.cc",
+]
+
vehicle_panel_qt_src = [
"sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc",
]
-sp_widgets_src = widgets_src
+sp_widgets_src = widgets_src + network_src
sp_qt_src = qt_src + sunnypilot_panel_qt_src + vehicle_panel_qt_src
sp_qt_util = qt_util
diff --git a/selfdrive/ui/sunnypilot/qt/api.cc b/selfdrive/ui/sunnypilot/qt/api.cc
new file mode 100644
index 0000000000..d75d8d39f0
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/api.cc
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#include "selfdrive/ui/sunnypilot/qt/api.h"
+
+#include
+#include
+
+#include "util.h"
+#include "selfdrive/ui/qt/util.h"
+
+namespace SunnylinkApi {
+ QString create_jwt(const QJsonObject &payloads, int expiry, bool sunnylink) {
+ QJsonObject header = {{"alg", "RS256"}};
+
+ auto t = QDateTime::currentSecsSinceEpoch();
+ auto dongle_id = sunnylink ? getSunnylinkDongleId() : getDongleId();
+ QJsonObject payload = {{"identity", dongle_id.value_or("")}, {"nbf", t}, {"iat", t}, {"exp", t + expiry}};
+ for (auto it = payloads.begin(); it != payloads.end(); ++it) {
+ payload.insert(it.key(), it.value());
+ }
+
+ auto b64_opts = QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals;
+ QString jwt = QJsonDocument(header).toJson(QJsonDocument::Compact).toBase64(b64_opts) + '.' +
+ QJsonDocument(payload).toJson(QJsonDocument::Compact).toBase64(b64_opts);
+
+ auto hash = QCryptographicHash::hash(jwt.toUtf8(), QCryptographicHash::Sha256);
+ return jwt + "." + CommaApi::rsa_sign(hash).toBase64(b64_opts);
+ }
+} // namespace SunnylinkApi
+
+void HttpRequestSP::sendRequest(const QString& requestURL, Method method, const QByteArray& payload) {
+ if (active()) {
+ return;
+ }
+ QNetworkRequest request = prepareRequest(requestURL);
+
+ if(!payload.isEmpty() && (method == Method::POST || method == Method::PUT)) {
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ }
+
+ switch (method) {
+ case Method::GET:
+ reply = nam()->get(request);
+ break;
+ case Method::DELETE:
+ reply = nam()->deleteResource(request);
+ break;
+ case Method::POST:
+ reply = nam()->post(request, payload);
+ break;
+ case Method::PUT:
+ reply = nam()->put(request, payload);
+ break;
+ }
+
+ networkTimer->start();
+ connect(reply, &QNetworkReply::finished, this, &HttpRequestSP::requestFinished);
+}
diff --git a/selfdrive/ui/sunnypilot/qt/api.h b/selfdrive/ui/sunnypilot/qt/api.h
new file mode 100644
index 0000000000..592b2e668f
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/api.h
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include "selfdrive/ui/qt/api.h"
+#include "selfdrive/ui/sunnypilot/qt/util.h"
+#include "common/util.h"
+
+namespace SunnylinkApi {
+ QByteArray rsa_encrypt(const QByteArray& data);
+ QByteArray rsa_decrypt(const QByteArray& data);
+ QString create_jwt(const QJsonObject& payloads = {}, int expiry = 3600, bool sunnylink = false);
+}
+
+class HttpRequestSP : public HttpRequest {
+ Q_OBJECT
+
+public:
+ explicit HttpRequestSP(QObject* parent, bool create_jwt = true, int timeout = 20000, bool sunnylink = false) :
+ HttpRequest(parent, create_jwt, timeout), sunnylink(sunnylink) {}
+
+ using HttpRequest::sendRequest;
+ void sendRequest(const QString& requestURL, Method method, const QByteArray& payload);
+
+private:
+ bool sunnylink;
+
+protected:
+ [[nodiscard]] QString GetJwtToken() const override { return SunnylinkApi::create_jwt({}, 3600, sunnylink); }
+ [[nodiscard]] QString GetUserAgent() const override { return getUserAgent(sunnylink); }
+};
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h
new file mode 100644
index 0000000000..04b4dccf14
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include
+
+enum class RoleType {
+ ReadOnly,
+ Sponsor,
+ Admin
+};
+
+// haha, a role model xD
+class RoleModel {
+protected:
+ QJsonObject m_raw_json_object;
+
+public:
+ RoleType roleType;
+
+ explicit RoleModel(const RoleType &roleType) : roleType(roleType) { m_raw_json_object = toJson(); }
+ explicit RoleModel(const QJsonObject &json) : RoleModel(stringToRoleType(json["role_type"].toString())) { m_raw_json_object = json; }
+
+ [[nodiscard]] QJsonObject toJson() const {
+ QJsonObject json;
+ json["role_type"] = roleTypeToString(roleType);
+ return json;
+ }
+
+ static RoleType stringToRoleType(const QString &roleTypeString) {
+ if (roleTypeString == "ReadOnly") return RoleType::ReadOnly;
+ if (roleTypeString == "Sponsor") return RoleType::Sponsor;
+
+ return RoleType::Admin; // Default to Admin
+ }
+
+ static QString roleTypeToString(const RoleType &roleType) {
+ switch (roleType) {
+ case RoleType::ReadOnly:
+ return "ReadOnly";
+ case RoleType::Sponsor:
+ return "Sponsor";
+ default: // RoleType::Admin
+ return "Admin";
+ }
+ }
+
+ template ::value>::type> T as() const {
+ return T(m_raw_json_object);
+ }
+};
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h
new file mode 100644
index 0000000000..7e1772b591
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include
+
+enum class SponsorTier {
+ Free,
+ Novice,
+ Supporter,
+ Contributor,
+ Benefactor,
+ Guardian,
+};
+
+// haha, a role model xD
+class SponsorRoleModel final : RoleModel {
+public:
+ SponsorTier roleTier;
+
+ explicit SponsorRoleModel(const RoleType &roleType, const SponsorTier &roleTier) : RoleModel(roleType), roleTier(roleTier) {}
+ explicit SponsorRoleModel(const QJsonObject &json) : RoleModel(json), roleTier(stringToSponsorTier(json["role_tier"].toString())) {}
+
+ [[nodiscard]] QJsonObject toJson() const {
+ QJsonObject json = RoleModel::toJson();
+ json["role_tier"] = sponsorTierToString(roleTier);
+ return json;
+ }
+
+ static SponsorTier stringToSponsorTier(const QString &sponsorTierString) {
+ const auto sponsorTierStringLower = sponsorTierString.toLower();
+ if (sponsorTierStringLower == "guardian") return SponsorTier::Guardian;
+ if (sponsorTierStringLower == "novice") return SponsorTier::Novice;
+ if (sponsorTierStringLower == "supporter") return SponsorTier::Supporter;
+ if (sponsorTierStringLower == "contributor") return SponsorTier::Contributor;
+ if (sponsorTierStringLower == "benefactor") return SponsorTier::Benefactor;
+
+ // Default to Free
+ return SponsorTier::Free;
+ }
+
+ static QString sponsorTierToString(const SponsorTier &sponsorTier) {
+ switch (sponsorTier) {
+ case SponsorTier::Guardian:
+ return "Guardian";
+ case SponsorTier::Novice:
+ return "Novice";
+ case SponsorTier::Supporter:
+ return "Supporter";
+ case SponsorTier::Contributor:
+ return "Contributor";
+ case SponsorTier::Benefactor:
+ return "Benefactor";
+
+ default: // SponsorTier::Free
+ return "Free";
+ }
+ }
+ [[nodiscard]] auto getSponsorTierString() const { return sponsorTierToString(roleTier); }
+
+ static QString sponsorTierToColor(const SponsorTier &sponsorTier) {
+ switch (sponsorTier) {
+ case SponsorTier::Guardian:
+ return "gold";
+ case SponsorTier::Benefactor:
+ return "mediumseagreen";
+ case SponsorTier::Contributor:
+ return "steelblue";
+ case SponsorTier::Supporter:
+ return "mediumpurple";
+ case SponsorTier::Novice:
+ return "white";
+ default: // SponsorTier::Free
+ return "silver";
+ }
+ }
+ [[nodiscard]] auto getSponsorTierColor() const { return sponsorTierToColor(roleTier); }
+};
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h
new file mode 100644
index 0000000000..4d255ac8e3
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include
+
+class UserModel {
+public:
+ QString device_id;
+ QString user_id;
+ qint64 created_at;
+ qint64 updated_at;
+ QString token_hash;
+
+ explicit UserModel(const QJsonObject &json) {
+ device_id = json["device_id"].toString();
+ user_id = json["user_id"].toString();
+ created_at = json["created_at"].toInt();
+ updated_at = json["updated_at"].toInt();
+ token_hash = json["token_hash"].toString();
+ }
+
+ [[nodiscard]] QJsonObject toJson() const {
+ QJsonObject json;
+ json["device_id"] = device_id;
+ json["user_id"] = user_id;
+ json["created_at"] = created_at;
+ json["updated_at"] = updated_at;
+ json["token_hash"] = token_hash;
+ return json;
+ }
+};
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.cc b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.cc
new file mode 100644
index 0000000000..3919a1821f
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.cc
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink//services/base_device_service.h"
+
+#include "selfdrive/ui/sunnypilot/qt/request_repeater.h"
+
+#include "common/swaglog.h"
+#include "selfdrive/ui/qt/util.h"
+#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h"
+
+BaseDeviceService::BaseDeviceService(QObject* parent) : QObject(parent) {
+ param_watcher = new ParamWatcher(this);
+ connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) {
+ paramsRefresh();
+ });
+ param_watcher->addParam("SunnylinkEnabled");
+}
+
+void BaseDeviceService::paramsRefresh() {
+}
+
+void BaseDeviceService::loadDeviceData(const QString &url, bool poll) {
+ if (!is_sunnylink_enabled()) {
+ LOGW("Sunnylink is not enabled, refusing to load data.");
+ return;
+ }
+
+ auto sl_dongle_id = getSunnylinkDongleId();
+ if (!sl_dongle_id.has_value())
+ return;
+
+ QString fullUrl = SUNNYLINK_BASE_URL + "/device/" + *sl_dongle_id + url;
+ if (poll && !isCurrentyPolling()) {
+ LOGD("Polling %s", qPrintable(fullUrl));
+ LOGD("Cache key: SunnylinkCache_%s", qPrintable(QString(getCacheKey())));
+ repeater = new RequestRepeaterSP(this, fullUrl, "SunnylinkCache_" + getCacheKey(), 60, false, true);
+ connect(repeater, &RequestRepeaterSP::requestDone, this, &BaseDeviceService::handleResponse);
+ } else if (isCurrentyPolling()) {
+ repeater->ForceUpdate();
+ } else {
+ LOGD("Sending one-time %s", qPrintable(fullUrl));
+ initial_request = new HttpRequestSP(this, true, 10000, true);
+ connect(initial_request, &HttpRequestSP::requestDone, this, &BaseDeviceService::handleResponse);
+ }
+}
+
+void BaseDeviceService::stopPolling() {
+ if (repeater != nullptr) {
+ repeater->deleteLater();
+ repeater = nullptr;
+ }
+}
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h
new file mode 100644
index 0000000000..8ef99d1013
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include "selfdrive/ui/sunnypilot/qt/request_repeater.h"
+#include "selfdrive/ui/qt/util.h"
+
+class BaseDeviceService : public QObject {
+ Q_OBJECT
+
+protected:
+ void paramsRefresh();
+ void loadDeviceData(const QString &url, bool poll = false);
+ virtual void handleResponse(const QString &response, bool success) = 0;
+
+ static bool is_sunnylink_enabled() { return Params().getBool("SunnylinkEnabled");}
+ ParamWatcher* param_watcher;
+ HttpRequestSP* initial_request = nullptr;
+ RequestRepeaterSP* repeater = nullptr;
+
+public:
+ explicit BaseDeviceService(QObject* parent = nullptr);
+ virtual QString getCacheKey() const = 0;
+ bool isCurrentyPolling() {return repeater != nullptr;}
+ void stopPolling();
+};
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.cc b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.cc
new file mode 100644
index 0000000000..bffe32839f
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.cc
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h"
+
+#include
+#include
+
+RoleService::RoleService(QObject* parent) : BaseDeviceService(parent) {}
+
+void RoleService::load() {
+ loadDeviceData(url);
+}
+
+void RoleService::startPolling() {
+ loadDeviceData(url, true);
+}
+
+void RoleService::handleResponse(const QString &response, bool success) {
+ if (!success) return;
+
+ QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
+ QJsonArray jsonArray = doc.array();
+
+ std::vector roles;
+ for (const auto &value : jsonArray) {
+ roles.emplace_back(value.toObject());
+ }
+
+ emit rolesReady(roles);
+ uiStateSP()->setSunnylinkRoles(roles);
+}
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h
new file mode 100644
index 0000000000..abd0e1ab83
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include
+
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h"
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h"
+
+class RoleService : public BaseDeviceService {
+ Q_OBJECT
+
+public:
+ explicit RoleService(QObject* parent = nullptr);
+ void load();
+ void startPolling();
+ [[nodiscard]] QString getCacheKey() const final { return "Roles"; }
+
+signals:
+ void rolesReady(const std::vector &roles);
+
+protected:
+ void handleResponse(const QString&response, bool success) override;
+
+private:
+ QString url = "/roles";
+};
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.cc b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.cc
new file mode 100644
index 0000000000..cd16360c26
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.cc
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h"
+
+#include
+#include
+
+#include "selfdrive/ui/sunnypilot/ui.h"
+
+UserService::UserService(QObject* parent) : BaseDeviceService(parent) {
+ url = "/users";
+}
+
+void UserService::load() {
+ loadDeviceData(url);
+}
+
+void UserService::startPolling() {
+ loadDeviceData(url, true);
+}
+
+void UserService::handleResponse(const QString &response, bool success) {
+ if (!success) {
+ return;
+ }
+
+ QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
+ QJsonArray jsonArray = doc.array();
+
+ std::vector users;
+ for (const auto &value : jsonArray) {
+ users.emplace_back(value.toObject());
+ }
+
+ emit usersReady(users);
+ uiStateSP()->setSunnylinkDeviceUsers(users);
+}
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h
new file mode 100644
index 0000000000..90eb354751
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include
+
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/base_device_service.h"
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h"
+
+class UserService : public BaseDeviceService {
+ Q_OBJECT
+
+public:
+ explicit UserService(QObject* parent = nullptr);
+ void load();
+ void startPolling();
+ [[nodiscard]] QString getCacheKey() const final { return "Users"; };
+
+signals:
+ void usersReady(const std::vector&users);
+
+protected:
+ void handleResponse(const QString&response, bool success) override;
+
+private:
+ QString url = "/users";
+};
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.cc b/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.cc
new file mode 100644
index 0000000000..4428eb55c4
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.cc
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h"
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h"
+
+SunnylinkClient::SunnylinkClient(QObject* parent) : QObject(parent) {
+ role_service = new RoleService(parent);
+ user_service = new UserService(parent);
+}
diff --git a/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h b/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h
new file mode 100644
index 0000000000..0961449926
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include
+
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/role_service.h"
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/services/user_service.h"
+
+class SunnylinkClient : public QObject {
+ Q_OBJECT
+
+public:
+ explicit SunnylinkClient(QObject* parent);
+ RoleService* role_service;
+ UserService* user_service;
+};
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc
new file mode 100644
index 0000000000..7b5ce48238
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.h"
+
+#include "selfdrive/ui/sunnypilot/ui.h"
+#include "selfdrive/ui/sunnypilot/qt/api.h"
+#include "selfdrive/ui/sunnypilot/qt/util.h"
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h"
+
+// Sponsor Upsell
+using qrcodegen::QrCode;
+
+SunnylinkSponsorQRWidget::SunnylinkSponsorQRWidget(bool sponsor_pair, QWidget* parent) : QWidget(parent), sponsor_pair(sponsor_pair) {
+ timer = new QTimer(this);
+ connect(timer, &QTimer::timeout, this, &SunnylinkSponsorQRWidget::refresh);
+}
+
+void SunnylinkSponsorQRWidget::showEvent(QShowEvent *event) {
+ refresh();
+ timer->start(5 * 60 * 1000);
+ device()->setOffroadBrightness(100);
+}
+
+void SunnylinkSponsorQRWidget::hideEvent(QHideEvent *event) {
+ timer->stop();
+ device()->setOffroadBrightness(BACKLIGHT_OFFROAD);
+}
+
+void SunnylinkSponsorQRWidget::refresh() {
+ QString qrString;
+
+ if (sponsor_pair) {
+ QString token = SunnylinkApi::create_jwt({}, 3600, true);
+ auto sl_dongle_id = getSunnylinkDongleId();
+ QByteArray payload = QString("1|" + sl_dongle_id.value_or("") + "|" + token).toUtf8().toBase64();
+ qrString = SUNNYLINK_BASE_URL + "/sso?state=" + payload;
+ } else {
+ qrString = "https://github.com/sponsors/sunnyhaibin";
+ }
+
+ this->updateQrCode(qrString);
+ update();
+}
+
+void SunnylinkSponsorQRWidget::updateQrCode(const QString &text) {
+ QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW);
+ qint32 sz = qr.getSize();
+ QImage im(sz, sz, QImage::Format_RGB32);
+
+ QRgb black = qRgb(0, 0, 0);
+ QRgb white = qRgb(255, 255, 255);
+ for (int y = 0; y < sz; y++) {
+ for (int x = 0; x < sz; x++) {
+ im.setPixel(x, y, qr.getModule(x, y) ? black : white);
+ }
+ }
+
+ // Integer division to prevent anti-aliasing
+ int final_sz = ((width() / sz) - 1) * sz;
+ img = QPixmap::fromImage(im.scaled(final_sz, final_sz, Qt::KeepAspectRatio), Qt::MonoOnly);
+}
+
+void SunnylinkSponsorQRWidget::paintEvent(QPaintEvent *e) {
+ QPainter p(this);
+ p.fillRect(rect(), Qt::white);
+
+ QSize s = (size() - img.size()) / 2;
+ p.drawPixmap(s.width(), s.height(), img);
+}
+
+QStringList SunnylinkSponsorPopup::getInstructions(bool sponsor_pair) {
+ QStringList instructions;
+ if (sponsor_pair) {
+ instructions << tr("Scan the QR code to login to your GitHub account")
+ << tr("Follow the prompts to complete the pairing process")
+ << tr("Re-enter the \"sunnylink\" panel to verify sponsorship status")
+ << tr("If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot");
+ } else {
+ instructions << tr("Scan the QR code to visit sunnyhaibin's GitHub Sponsors page")
+ << tr("Choose your sponsorship tier and confirm your support")
+ << tr("Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status");
+ }
+ return instructions;
+}
+
+SunnylinkSponsorPopup::SunnylinkSponsorPopup(bool sponsor_pair, QWidget *parent) : DialogBase(parent), sponsor_pair(sponsor_pair) {
+ auto *hlayout = new QHBoxLayout(this);
+ auto sunnylink_client = new SunnylinkClient(this);
+ hlayout->setContentsMargins(0, 0, 0, 0);
+ hlayout->setSpacing(0);
+
+ setStyleSheet("SunnylinkSponsorPopup { background-color: #E0E0E0; }");
+
+ // text
+ auto vlayout = new QVBoxLayout();
+ vlayout->setContentsMargins(85, 70, 50, 70);
+ vlayout->setSpacing(50);
+ hlayout->addLayout(vlayout, 1);
+ {
+ auto close = new QPushButton(QIcon(":/icons/close.svg"), "", this);
+ close->setIconSize(QSize(80, 80));
+ close->setStyleSheet("border: none;");
+ vlayout->addWidget(close, 0, Qt::AlignLeft);
+ connect(close, &QPushButton::clicked, this, [=] {
+ sunnylink_client->role_service->load();
+ sunnylink_client->user_service->load();
+ QDialog::reject();
+ });
+
+ //vlayout->addSpacing(30);
+
+ const QString titleText = sponsor_pair ? tr("Pair your GitHub account") : tr("Early Access: Become a sunnypilot Sponsor");
+ const auto title = new QLabel(titleText, this);
+ title->setStyleSheet("font-size: 75px; color: black;");
+ title->setWordWrap(true);
+ vlayout->addWidget(title);
+
+ QStringList instructions = getInstructions(sponsor_pair);
+ QString instructionsHtml = "";
+ for (const auto & instruction : instructions) {
+ instructionsHtml += QString("
%1
").arg(instruction);
+ }
+ instructionsHtml += "";
+ const auto instructionsLabel = new QLabel(instructionsHtml, this);
+
+
+ instructionsLabel->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
+ instructionsLabel->setWordWrap(true);
+ vlayout->addWidget(instructionsLabel);
+
+ vlayout->addStretch();
+ }
+
+ // QR code
+ auto *qr = new SunnylinkSponsorQRWidget(sponsor_pair, this);
+ hlayout->addWidget(qr, 1);
+}
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.h
new file mode 100644
index 0000000000..9f15a6084f
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.h
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "common/util.h"
+#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
+
+const QString SUNNYLINK_BASE_URL = util::getenv("SUNNYLINK_API_HOST", "https://stg.api.sunnypilot.ai").c_str();
+
+class SunnylinkSponsorQRWidget : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit SunnylinkSponsorQRWidget(bool sponsor_pair = false, QWidget* parent = 0);
+ void paintEvent(QPaintEvent*) override;
+
+private:
+ QPixmap img;
+ QTimer *timer;
+ void updateQrCode(const QString &text);
+ void showEvent(QShowEvent *event) override;
+ void hideEvent(QHideEvent *event) override;
+
+ bool sponsor_pair = false;
+
+private slots:
+ void refresh();
+};
+
+// sponsor popup widget
+class SunnylinkSponsorPopup : public DialogBase {
+ Q_OBJECT
+
+public:
+ explicit SunnylinkSponsorPopup(bool sponsor_pair = false, QWidget* parent = 0);
+
+private:
+ static QStringList getInstructions(bool sponsor_pair);
+ bool sponsor_pair = false;
+};
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.cc
index 12ae2209f2..706e89ddf0 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.cc
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.cc
@@ -12,12 +12,26 @@
SunnylinkPanel::SunnylinkPanel(QWidget *parent) : QFrame(parent) {
main_layout = new QStackedLayout(this);
- ListWidget *list = new ListWidget(this, false);
+ sunnylink_client = new SunnylinkClient(this);
+ param_watcher = new ParamWatcher(this);
+ param_watcher->addParam("SunnylinkEnabled");
+ connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) {
+ paramsRefresh(param_name, param_value);
+ });
+
+ is_sunnylink_enabled = Params().getBool("SunnylinkEnabled");
+ connect(uiStateSP(), &UIStateSP::sunnylinkRolesChanged, this, &SunnylinkPanel::updatePanel);
+ connect(uiStateSP(), &UIStateSP::sunnylinkDeviceUsersChanged, this, &SunnylinkPanel::updatePanel);
+ connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) {
+ is_onroad = !offroad;
+ updatePanel();
+ });
sunnylinkScreen = new QWidget(this);
- QVBoxLayout *vlayout = new QVBoxLayout(sunnylinkScreen);
+ auto vlayout = new QVBoxLayout(sunnylinkScreen);
vlayout->setContentsMargins(50, 20, 50, 20);
+ auto *list = new ListWidget(this, false);
QString sunnylinkEnabledBtnDesc = tr("This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that.");
sunnylinkEnabledBtn = new ParamControl(
"SunnylinkEnabled",
@@ -26,23 +40,47 @@ SunnylinkPanel::SunnylinkPanel(QWidget *parent) : QFrame(parent) {
"");
list->addItem(sunnylinkEnabledBtn);
+ status_popup = new SunnylinkSponsorPopup(false, this);
+ sponsorBtn = new ButtonControlSP(
+ tr("Sponsor Status"), tr("SPONSOR"),
+ tr("Become a sponsor of sunnypilot to get early access to sunnylink features when they become available."));
+ list->addItem(sponsorBtn);
+ connect(sponsorBtn, &ButtonControlSP::clicked, [=]() {
+ status_popup->exec();
+ });
+ list->addItem(horizontal_line());
+
+ pair_popup = new SunnylinkSponsorPopup(true, this);
+ pairSponsorBtn = new ButtonControlSP(
+ tr("Pair GitHub Account"), tr("PAIR"),
+ tr("Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.") + "🌟");
+ list->addItem(pairSponsorBtn);
+ connect(pairSponsorBtn, &ButtonControlSP::clicked, [=]() {
+ if (getSunnylinkDongleId().value_or(tr("N/A")) == "N/A") {
+ ConfirmationDialog::alert(tr("sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again."), this);
+ } else {
+ pair_popup->exec();
+ }
+ });
+ list->addItem(horizontal_line());
+
connect(sunnylinkEnabledBtn, &ParamControl::showDescriptionEvent, [=]() {
// resets the description to the default one for the Easter egg
sunnylinkEnabledBtn->setDescription(sunnylinkEnabledBtnDesc);
});
connect(sunnylinkEnabledBtn, &ParamControl::toggleFlipped, [=](bool enabled) {
+ QString description;
if (enabled) {
- auto proud_description = ""+ tr("🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀")+ "";
- sunnylinkEnabledBtn->showDescription();
- sunnylinkEnabledBtn->setDescription(proud_description);
+ description = ""+ tr("🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀")+ "";
} else {
- auto shame_description = ""+ tr("👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉.")+ "";
- sunnylinkEnabledBtn->showDescription();
- sunnylinkEnabledBtn->setDescription(shame_description);
- }
+ description = ""+ tr("👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉.")+ "";
- updatePanel(offroad);
+ }
+ sunnylinkEnabledBtn->showDescription();
+ sunnylinkEnabledBtn->setDescription(description);
+
+ updatePanel();
});
QObject::connect(uiState(), &UIState::offroadTransition, this, &SunnylinkPanel::updatePanel);
@@ -51,22 +89,81 @@ SunnylinkPanel::SunnylinkPanel(QWidget *parent) : QFrame(parent) {
vlayout->addWidget(sunnylinkScroller);
main_layout->addWidget(sunnylinkScreen);
+
+ if (is_sunnylink_enabled) {
+ startSunnylink();
+ }
+}
+
+void SunnylinkPanel::paramsRefresh(const QString ¶m_name, const QString ¶m_value) {
+ // We do it on paramsRefresh because the toggleEvent happens before the value is updated
+ if (param_name == "SunnylinkEnabled" && param_value == "1") {
+ startSunnylink();
+ } else if (param_name == "SunnylinkEnabled" && param_value == "0") {
+ stopSunnylink();
+ }
+
+ updatePanel();
+}
+
+void SunnylinkPanel::startSunnylink() const {
+ if (!sunnylink_client->role_service->isCurrentyPolling()) {
+ sunnylink_client->role_service->startPolling();
+ } else {
+ sunnylink_client->role_service->load();
+ }
+
+ if (!sunnylink_client->user_service->isCurrentyPolling()) {
+ sunnylink_client->user_service->startPolling();
+ } else {
+ sunnylink_client->user_service->load();
+ }
+}
+
+void SunnylinkPanel::stopSunnylink() const {
+ sunnylink_client->role_service->stopPolling();
+ sunnylink_client->user_service->stopPolling();
}
void SunnylinkPanel::showEvent(QShowEvent *event) {
- updatePanel(offroad);
+ updatePanel();
+ if (is_sunnylink_enabled) {
+ startSunnylink();
+ }
}
-void SunnylinkPanel::updatePanel(bool _offroad) {
- QString sunnylink_device_id = tr("Device ID") + " " + getSunnylinkDongleId().value_or(tr("N/A"));
-
- sunnylinkEnabledBtn->setEnabled(_offroad);
-
- if (sunnylinkEnabledBtn->isToggled()) {
- sunnylinkEnabledBtn->setValue(sunnylink_device_id);
- } else {
- sunnylinkEnabledBtn->setValue("");
+void SunnylinkPanel::updatePanel() {
+ if (!isVisible()) {
+ return;
}
- offroad = _offroad;
+ const auto sunnylinkDongleId = getSunnylinkDongleId().value_or(tr("N/A"));
+ sunnylinkEnabledBtn->setEnabled(!is_onroad);
+
+ is_sunnylink_enabled = Params().getBool("SunnylinkEnabled");
+ bool is_sub = uiStateSP()->isSunnylinkSponsor() && is_sunnylink_enabled;
+ auto max_current_sponsor_rule = uiStateSP()->sunnylinkSponsorRole();
+ auto role_name = max_current_sponsor_rule.getSponsorTierString();
+ std::optional role_color = max_current_sponsor_rule.getSponsorTierColor();
+ bool is_paired = uiStateSP()->isSunnylinkPaired();
+ auto paired_users = uiStateSP()->sunnylinkDeviceUsers();
+
+ sunnylinkEnabledBtn->setEnabled(!is_onroad);
+ sunnylinkEnabledBtn->setValue(tr("Device ID") + " " + sunnylinkDongleId);
+
+ sponsorBtn->setEnabled(!is_onroad && is_sunnylink_enabled);
+ sponsorBtn->setText(is_sub ? tr("THANKS ♥")/* + " ♥️"*/ : tr("SPONSOR"));
+ sponsorBtn->setValue(is_sub ? tr(role_name.toStdString().c_str()) : tr("Not Sponsor"), role_color);
+
+ pairSponsorBtn->setEnabled(!is_onroad && is_sunnylink_enabled);
+ pairSponsorBtn->setValue(is_paired ? tr("Paired") : tr("Not Paired"));
+
+
+ if (!is_sunnylink_enabled) {
+ sunnylinkEnabledBtn->setValue("");
+ sponsorBtn->setValue("");
+ pairSponsorBtn->setValue("");
+ }
+
+ update();
}
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h
index 824a081af3..d231757e36 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h
@@ -7,7 +7,10 @@
#pragma once
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/sunnylink_client.h"
+
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
+#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
class SunnylinkPanel : public QFrame {
@@ -16,16 +19,29 @@ class SunnylinkPanel : public QFrame {
public:
explicit SunnylinkPanel(QWidget *parent = nullptr);
void showEvent(QShowEvent *event) override;
+ void paramsRefresh(const QString¶m_name, const QString¶m_value);
public slots:
- void updatePanel(bool _offroad);
+ void updatePanel();
private:
Params params;
QStackedLayout *main_layout = nullptr;
QWidget *sunnylinkScreen = nullptr;
ScrollViewSP *sunnylinkScroller = nullptr;
- bool offroad;
+ SunnylinkSponsorPopup *status_popup;
+ SunnylinkSponsorPopup *pair_popup;
+ ButtonControlSP* sponsorBtn;
+ ButtonControlSP* pairSponsorBtn;
+ SunnylinkClient* sunnylink_client;
ParamControl *sunnylinkEnabledBtn;
+ bool is_onroad = false;
+ bool is_backup = false;
+ bool is_restore = false;
+ bool is_sunnylink_enabled = false;
+ ParamWatcher *param_watcher;
+ QString sunnylinkBtnDescription;
+ void stopSunnylink() const;
+ void startSunnylink() const;
};
diff --git a/selfdrive/ui/sunnypilot/qt/request_repeater.cc b/selfdrive/ui/sunnypilot/qt/request_repeater.cc
new file mode 100644
index 0000000000..e137d56172
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/request_repeater.cc
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#include "selfdrive/ui/sunnypilot/qt/request_repeater.h"
+
+RequestRepeaterSP::RequestRepeaterSP(QObject *parent, const QString &requestURL, const QString &cacheKey,
+ int period, bool whileOnroad, bool sunnylink) : HttpRequestSP(parent, true, 20000, sunnylink) {
+ request_url = requestURL;
+ while_onroad = whileOnroad;
+ timer = new QTimer(this);
+ timer->setTimerType(Qt::VeryCoarseTimer);
+ connect(timer, &QTimer::timeout, [=]() { this->timerTick(); });
+ timer->start(period * 1000);
+
+ if (!cacheKey.isEmpty()) {
+ prevResp = QString::fromStdString(params.get(cacheKey.toStdString()));
+ if (!prevResp.isEmpty()) {
+ QTimer::singleShot(500, [=]() { emit requestDone(prevResp, true, QNetworkReply::NoError); });
+ }
+ connect(this, &HttpRequest::requestDone, [=](const QString &resp, bool success) {
+ if (success && resp != prevResp) {
+ params.put(cacheKey.toStdString(), resp.toStdString());
+ prevResp = resp;
+ }
+ });
+ }
+
+ // Don't wait for the timer to fire to send the first request
+ ForceUpdate();
+}
+
+void RequestRepeaterSP::timerTick() {
+ if ((!uiState()->scene.started || while_onroad) && device()->isAwake() && !active()) {
+ LOGD("Sending request for %s", qPrintable(request_url));
+ sendRequest(request_url);
+ }
+}
diff --git a/selfdrive/ui/sunnypilot/qt/request_repeater.h b/selfdrive/ui/sunnypilot/qt/request_repeater.h
new file mode 100644
index 0000000000..0cdb370f9b
--- /dev/null
+++ b/selfdrive/ui/sunnypilot/qt/request_repeater.h
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ *
+ * This file is part of sunnypilot and is licensed under the MIT License.
+ * See the LICENSE.md file in the root directory for more details.
+ */
+
+#pragma once
+
+#include "selfdrive/ui/qt/request_repeater.h"
+
+#include "common/swaglog.h"
+#include "common/util.h"
+#include "selfdrive/ui/sunnypilot/ui.h"
+#include "selfdrive/ui/sunnypilot/qt/api.h"
+
+class RequestRepeaterSP : public HttpRequestSP {
+
+private:
+ Params params;
+ QTimer *timer;
+ QString prevResp;
+ QString request_url;
+ bool while_onroad;
+ void timerTick();
+
+public:
+ RequestRepeaterSP(QObject *parent, const QString &requestURL, const QString &cacheKey = "", int period = 0, bool whileOnroad=false, bool sunnylink = false);
+ void ForceUpdate() {
+ LOGD("Forcing update for %s", qPrintable(request_url));
+ timerTick();
+ }
+};
diff --git a/selfdrive/ui/sunnypilot/ui.cc b/selfdrive/ui/sunnypilot/ui.cc
index 37da83949b..e27771b9bb 100644
--- a/selfdrive/ui/sunnypilot/ui.cc
+++ b/selfdrive/ui/sunnypilot/ui.cc
@@ -48,6 +48,16 @@ UIStateSP *uiStateSP() {
return &ui_state;
}
+void UIStateSP::setSunnylinkRoles(const std::vector& roles) {
+ sunnylinkRoles = roles;
+ emit sunnylinkRolesChanged(roles);
+}
+
+void UIStateSP::setSunnylinkDeviceUsers(const std::vector& users) {
+ sunnylinkUsers = users;
+ emit sunnylinkDeviceUsersChanged(users);
+}
+
DeviceSP *deviceSP() {
static DeviceSP _device;
return &_device;
diff --git a/selfdrive/ui/sunnypilot/ui.h b/selfdrive/ui/sunnypilot/ui.h
index 65c9237969..08e1d37b0f 100644
--- a/selfdrive/ui/sunnypilot/ui.h
+++ b/selfdrive/ui/sunnypilot/ui.h
@@ -7,6 +7,11 @@
#pragma once
+#include
+
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/user_model.h"
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h"
+#include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h"
#include "selfdrive/ui/ui.h"
class UIStateSP : public UIState {
@@ -15,12 +20,54 @@ class UIStateSP : public UIState {
public:
UIStateSP(QObject *parent = 0);
void updateStatus() override;
+ void setSunnylinkRoles(const std::vector &roles);
+ void setSunnylinkDeviceUsers(const std::vector &users);
+
+ inline std::vector sunnylinkDeviceRoles() const { return sunnylinkRoles; }
+ inline bool isSunnylinkAdmin() const {
+ return std::any_of(sunnylinkRoles.begin(), sunnylinkRoles.end(), [](const RoleModel &role) {
+ return role.roleType == RoleType::Admin;
+ });
+ }
+ inline bool isSunnylinkSponsor() const {
+ return std::any_of(sunnylinkRoles.begin(), sunnylinkRoles.end(), [](const RoleModel &role) {
+ return role.roleType == RoleType::Sponsor && role.as().roleTier != SponsorTier::Free;
+ });
+ }
+ inline SponsorRoleModel sunnylinkSponsorRole() const {
+ std::optional sponsorRoleWithHighestTier = std::nullopt;
+ for (const auto &role : sunnylinkRoles) {
+ if(role.roleType != RoleType::Sponsor)
+ continue;
+
+ if (auto sponsorRole = role.as(); !sponsorRoleWithHighestTier.has_value() || sponsorRoleWithHighestTier->roleTier < sponsorRole.roleTier) {
+ sponsorRoleWithHighestTier = sponsorRole;
+ }
+ }
+ return sponsorRoleWithHighestTier.value_or(SponsorRoleModel(RoleType::Sponsor, SponsorTier::Free));
+ }
+ inline SponsorTier sunnylinkSponsorTier() const {
+ return sunnylinkSponsorRole().roleTier;
+ }
+ inline std::vector sunnylinkDeviceUsers() const { return sunnylinkUsers; }
+ inline bool isSunnylinkPaired() const {
+ return std::any_of(sunnylinkUsers.begin(), sunnylinkUsers.end(), [](const UserModel &user) {
+ return user.user_id.toLower() != "unregisteredsponsor" && user.user_id.toLower() != "temporarysponsor";
+ });
+ }
signals:
+ void sunnylinkRoleChanged(bool subscriber);
+ void sunnylinkRolesChanged(std::vector roles);
+ void sunnylinkDeviceUsersChanged(std::vector users);
void uiUpdate(const UIStateSP &s);
private slots:
void update() override;
+
+private:
+ std::vector sunnylinkRoles = {};
+ std::vector sunnylinkUsers = {};
};
UIStateSP *uiStateSP();
diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py
index f371c7bbae..f3fd6aa915 100755
--- a/selfdrive/ui/tests/test_ui/run.py
+++ b/selfdrive/ui/tests/test_ui/run.py
@@ -207,6 +207,16 @@ def setup_settings_sunnylink(click, pm: PubMaster, scroll=None):
click(278, 522)
time.sleep(UI_DELAY)
+def setup_settings_sunnylink_sponsor_button(click, pm: PubMaster, scroll=None):
+ setup_settings_sunnylink(click, pm)
+ click(1967, 225)
+ time.sleep(UI_DELAY)
+
+def setup_settings_sunnylink_pair_button(click, pm: PubMaster, scroll=None):
+ setup_settings_sunnylink(click, pm)
+ click(1967, 432)
+ time.sleep(UI_DELAY)
+
def setup_settings_sunnypilot(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
click(278, 852)
@@ -268,6 +278,8 @@ CASES = {
CASES.update({
"settings_sunnylink": setup_settings_sunnylink,
+ "settings_sunnylink_sponsor_button": setup_settings_sunnylink_sponsor_button,
+ "settings_sunnylink_pair_button": setup_settings_sunnylink_pair_button,
"settings_sunnypilot": setup_settings_sunnypilot,
"settings_sunnypilot_mads": setup_settings_sunnypilot_mads,
"settings_trips": setup_settings_trips,
diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts
index 40c61f8cd4..4cb648dc3d 100644
--- a/selfdrive/ui/translations/main_ar.ts
+++ b/selfdrive/ui/translations/main_ar.ts
@@ -981,7 +981,7 @@ This may take up to a minute.
Firehose
-
+ خرطوم الحريق
@@ -1450,6 +1450,89 @@ This may take up to a minute.
N/Aغير متاح
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+ إقران
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts
index 773a354d9e..5a994c4ad6 100644
--- a/selfdrive/ui/translations/main_de.ts
+++ b/selfdrive/ui/translations/main_de.ts
@@ -1428,6 +1428,89 @@ This may take up to a minute.
N/ANicht verfügbar
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts
index 5e3b854f8c..77b1ecb6e4 100644
--- a/selfdrive/ui/translations/main_es.ts
+++ b/selfdrive/ui/translations/main_es.ts
@@ -1428,6 +1428,93 @@ Esto puede tardar un minuto.
N/AN/A
+
+ Sponsor Status
+ Estado de patrocinio
+
+
+ SPONSOR
+ PATROCINADOR
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+ Conviértete en patrocinador de sunnypilot para obtener acceso anticipado a las funciones de sunnylink cuando estén disponibles.
+
+
+ Pair GitHub Account
+ Emparejar cuenta de GitHub
+
+
+ PAIR
+ EMPAREJAR
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+ Empareja tu cuenta de GitHub para otorgar a tu dispositivo beneficios de patrocinador, incluido acceso a la API en sunnylink.
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+ ID del dongle de sunnylink no encontrado. Esto puede deberse a una conexión débil o a un problema de registro en sunnylink. Por favor, reinicia y vuelve a intentarlo.
+
+
+ THANKS
+ GRACIAS
+
+
+ Not Sponsor
+ No patrocinador
+
+
+ Paired
+ Emparejado
+
+
+ Not Paired
+ No emparejado
+
+
+ THANKS ♥
+ GRACIAS ♥
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+ Escanea el código QR para iniciar sesión en tu cuenta de GitHub
+
+
+ Follow the prompts to complete the pairing process
+ Sigue las indicaciones para completar el proceso de emparejamiento
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+ Vuelve a ingresar al panel de "sunnylink" para verificar el estado de patrocinio
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+ Si el estado de patrocinio no se actualizó, por favor contacta a un moderador en Discord en https://discord.gg/sunnypilot
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+ Escanea el código QR para visitar la página de GitHub Sponsors de sunnyhaibin
+
+
+ Choose your sponsorship tier and confirm your support
+ Elige tu nivel de patrocinio y confirma tu apoyo
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+ Únete a nuestra comunidad en Discord en https://discord.gg/sunnypilot y contacta a un moderador para confirmar tu estado de patrocinio
+
+
+ Pair your GitHub account
+ Empareja tu cuenta de GitHub
+
+
+ Early Access: Become a sunnypilot Sponsor
+ Acceso temprano: conviértete en patrocinador de sunnypilot
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts
index 88484e6220..94ebdb0a15 100644
--- a/selfdrive/ui/translations/main_fr.ts
+++ b/selfdrive/ui/translations/main_fr.ts
@@ -1428,6 +1428,89 @@ Cela peut prendre jusqu'à une minute.
N/AN/A
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+ ASSOCIER
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts
index 0cda3f4c73..1c6ab9a8b4 100644
--- a/selfdrive/ui/translations/main_ja.ts
+++ b/selfdrive/ui/translations/main_ja.ts
@@ -954,7 +954,7 @@ This may take up to a minute.
Firehose
-
+ データ学習
@@ -1423,6 +1423,89 @@ This may take up to a minute.
N/A該当なし
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+ OK
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index 7e190c12b0..3286cc081f 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -956,7 +956,7 @@ This may take up to a minute.
Firehose
-
+ 파이어호스
@@ -1425,6 +1425,89 @@ This may take up to a minute.
N/AN/A
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+ 동기화
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index 742da57160..7a18c35989 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -961,7 +961,7 @@ Isso pode levar até um minuto.
Firehose
-
+ Firehose
@@ -1430,6 +1430,89 @@ Isso pode levar até um minuto.
N/AN/A
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+ PAREAR
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts
index 4a66da1156..abb3088dfe 100644
--- a/selfdrive/ui/translations/main_th.ts
+++ b/selfdrive/ui/translations/main_th.ts
@@ -1423,6 +1423,89 @@ This may take up to a minute.
N/Aไม่มี
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+ จับคู่
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts
index 6a344607fd..4e0e93c708 100644
--- a/selfdrive/ui/translations/main_tr.ts
+++ b/selfdrive/ui/translations/main_tr.ts
@@ -1421,6 +1421,89 @@ This may take up to a minute.
N/AN/A
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index dd98792569..caa32b4f2b 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -954,7 +954,7 @@ This may take up to a minute.
Firehose
-
+ 训练上传
@@ -1423,6 +1423,89 @@ This may take up to a minute.
N/AN/A
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+ 配对
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index 25c5afbdfc..da6e52702a 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -954,7 +954,7 @@ This may take up to a minute.
Firehose
-
+ 訓練上傳
@@ -1423,6 +1423,89 @@ This may take up to a minute.
N/A無法使用
+
+ Sponsor Status
+
+
+
+ SPONSOR
+
+
+
+ Become a sponsor of sunnypilot to get early access to sunnylink features when they become available.
+
+
+
+ Pair GitHub Account
+
+
+
+ PAIR
+ 配對
+
+
+ Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink.
+
+
+
+ sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again.
+
+
+
+ Not Sponsor
+
+
+
+ Paired
+
+
+
+ Not Paired
+
+
+
+ THANKS ♥
+
+
+
+
+ SunnylinkSponsorPopup
+
+ Scan the QR code to login to your GitHub account
+
+
+
+ Follow the prompts to complete the pairing process
+
+
+
+ Re-enter the "sunnylink" panel to verify sponsorship status
+
+
+
+ If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot
+
+
+
+ Scan the QR code to visit sunnyhaibin's GitHub Sponsors page
+
+
+
+ Choose your sponsorship tier and confirm your support
+
+
+
+ Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status
+
+
+
+ Pair your GitHub account
+
+
+
+ Early Access: Become a sunnypilot Sponsor
+
+ SunnypilotPanel
From 42871f7a9d20363244c944a6b17baf4e09eb3991 Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Sun, 16 Mar 2025 19:53:53 -0400
Subject: [PATCH 56/68] pandad: dedicated aligned buffer for deserialization
(#671)
* pandad: dedicated aligned buffer for deserialization
* ide pls
---
selfdrive/pandad/panda_safety.cc | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/selfdrive/pandad/panda_safety.cc b/selfdrive/pandad/panda_safety.cc
index ef9b459e5b..48589c4146 100644
--- a/selfdrive/pandad/panda_safety.cc
+++ b/selfdrive/pandad/panda_safety.cc
@@ -64,10 +64,12 @@ std::vector PandaSafety::fetchCarParams() {
// TODO-SP: Use structs instead of vector
void PandaSafety::setSafetyMode(const std::vector ¶ms_string) {
AlignedBuffer aligned_buf;
+ AlignedBuffer aligned_buf_sp;
+
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(params_string[0].data(), params_string[0].size()));
cereal::CarParams::Reader car_params = cmsg.getRoot();
- capnp::FlatArrayMessageReader cmsg_sp(aligned_buf.align(params_string[1].data(), params_string[1].size()));
+ capnp::FlatArrayMessageReader cmsg_sp(aligned_buf_sp.align(params_string[1].data(), params_string[1].size()));
cereal::CarParamsSP::Reader car_params_sp = cmsg_sp.getRoot();
auto safety_configs = car_params.getSafetyConfigs();
From d1042df6322c28168ee2edb5fa4aec6d632ef09a Mon Sep 17 00:00:00 2001
From: eFini
Date: Mon, 17 Mar 2025 11:39:50 +0800
Subject: [PATCH 57/68] Multilang: Update ZH translations (#34889)
update zh translations
---
selfdrive/ui/translations/main_zh-CHS.ts | 20 +++++++++++---------
selfdrive/ui/translations/main_zh-CHT.ts | 20 +++++++++++---------
2 files changed, 22 insertions(+), 18 deletions(-)
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index f78d8dcdff..4cc2d401eb 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -315,28 +315,30 @@
openpilot learns to drive by watching humans, like you, drive.
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.
-
+ openpilot 通过观察人类驾驶(包括您)来学习如何驾驶。
+
+“训练数据上传模式”允许您最大化上传训练数据,以改进 openpilot 的驾驶模型。更多数据意味着更强大的模型,也就意味着更优秀的“实验模式”。Firehose Mode: ACTIVE
-
+ 训练数据上传模式:激活中ACTIVE
-
+ 激活中<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
-
+ <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>未激活</span>:请连接至不限流量的网络For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
+ 为了达到最佳效果,请每周将您的设备带回室内,并连接到优质的 USB-C 充电器和 Wi-Fi。<br><br>Firehose 模式在行驶时也能运行,但需连接到移动热点或使用不限流量的 SIM 卡。<br><br><br><b>常见问题</b><br><br><i>我开车的方式或地点有影响吗?</i>不会,请像平常一样驾驶即可。<br><br><i>Firehose 模式会上传所有的驾驶片段吗?</i>不会,我们会选择性地上传部分片段。<br><br><i>什么是好的 USB-C 充电器?</i>任何快速手机或笔记本电脑充电器都应该适用。<br><br><i>我使用的软件版本有影响吗?</i>有的,只有官方 openpilot(以及特定的分支)可以用于训练。<b>%n segment(s)</b> of your driving is in the training dataset so far.
-
-
+
+ <b>目前已有 %n 段</b> 您的驾驶数据被纳入训练数据集。
@@ -995,11 +997,11 @@ This may take up to a minute.
Welcome to openpilot
-
+ 欢迎使用 openpilotYou must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.
-
+ 您必须接受《条款与条件》才能使用 openpilot。在继续之前,请先阅读最新条款:<span style='color: #465BEA;'>https://comma.ai/terms</span>。
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index 8547c90410..78519f2647 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -315,28 +315,30 @@
openpilot learns to drive by watching humans, like you, drive.
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.
-
+ openpilot 透過觀察人類駕駛(包括您)來學習如何駕駛。
+
+「訓練資料上傳模式」可讓您最大化上傳訓練數據,以改進 openpilot 的駕駛模型。更多數據代表更強大的模型,也就意味著更優秀的「實驗模式」。Firehose Mode: ACTIVE
-
+ 訓練資料上傳模式:啟用中ACTIVE
-
+ 啟用中<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network
-
+ <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>未啟用</span>:請連接至不限流量的網路For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.
-
+ 為了達到最佳效果,請每週將您的裝置帶回室內,並連接至優質的 USB-C 充電器與 Wi-Fi。<br><br>訓練資料上傳模式在行駛時也能運作,但需連接至行動熱點或使用不限流量的 SIM 卡。<br><br><br><b>常見問題</b><br><br><i>我開車的方式或地點有影響嗎?</i> 不會,請像平常一樣駕駛即可。<br><br><i>訓練資料上傳模式會上傳所有的駕駛片段嗎?</i>不會,我們會選擇性地上傳部分片段。<br><br><i>什麼是好的 USB-C 充電器?</i>任何快速手機或筆電充電器都應該適用。<br><br><i>我使用的軟體版本有影響嗎?</i>有的,只有官方 openpilot(以及特定的分支)可以用於訓練。<b>%n segment(s)</b> of your driving is in the training dataset so far.
-
-
+
+ <b>目前已有 %n 段</b> 您的駕駛數據被納入訓練資料集。
@@ -995,11 +997,11 @@ This may take up to a minute.
Welcome to openpilot
-
+ 歡迎使用 openpilotYou must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.
-
+ 您必須接受《條款與條件》才能使用 openpilot。在繼續之前,請先閱讀最新條款:<span style='color: #465BEA;'>https://comma.ai/terms</span>。
From b95140666bdb3376b35d39587db2411d4e3c4675 Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Mon, 17 Mar 2025 00:20:13 -0400
Subject: [PATCH 58/68] ci: remove sunnylink pairing QR code from UI preview
(#673)
---
selfdrive/ui/tests/test_ui/run.py | 6 ------
1 file changed, 6 deletions(-)
diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py
index f3fd6aa915..797a5f70b7 100755
--- a/selfdrive/ui/tests/test_ui/run.py
+++ b/selfdrive/ui/tests/test_ui/run.py
@@ -212,11 +212,6 @@ def setup_settings_sunnylink_sponsor_button(click, pm: PubMaster, scroll=None):
click(1967, 225)
time.sleep(UI_DELAY)
-def setup_settings_sunnylink_pair_button(click, pm: PubMaster, scroll=None):
- setup_settings_sunnylink(click, pm)
- click(1967, 432)
- time.sleep(UI_DELAY)
-
def setup_settings_sunnypilot(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
click(278, 852)
@@ -279,7 +274,6 @@ CASES = {
CASES.update({
"settings_sunnylink": setup_settings_sunnylink,
"settings_sunnylink_sponsor_button": setup_settings_sunnylink_sponsor_button,
- "settings_sunnylink_pair_button": setup_settings_sunnylink_pair_button,
"settings_sunnypilot": setup_settings_sunnypilot,
"settings_sunnypilot_mads": setup_settings_sunnypilot_mads,
"settings_trips": setup_settings_trips,
From 4a04819d964ec6360e2e1fc80c614fe4b8bdf01d Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Mon, 17 Mar 2025 00:36:32 -0400
Subject: [PATCH 59/68] Redirect license to LICENSE.md (#672)
* Redirect license to LICENSE.md
* bump
---
opendbc_repo | 2 +-
.../ui/sunnypilot/qt/network/networking.h | 2 +-
sunnypilot/mads/helpers.py | 23 ++-----------------
sunnypilot/mads/mads.py | 23 ++-----------------
sunnypilot/mads/state.py | 23 ++-----------------
.../mads/tests/test_mads_state_machine.py | 23 ++-----------------
sunnypilot/modeld/runners/run_helpers.py | 10 ++++----
sunnypilot/models/fetcher.py | 10 ++++----
sunnypilot/models/helpers.py | 10 ++++----
sunnypilot/models/manager.py | 10 ++++----
.../models/tests/model_manager_audit.py | 10 ++++----
11 files changed, 40 insertions(+), 106 deletions(-)
diff --git a/opendbc_repo b/opendbc_repo
index 9b83f9519d..9fbd19d3a6 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit 9b83f9519db61c96facb5e20b35d69c13ef3bd91
+Subproject commit 9fbd19d3a6d45f5cd0bb979e0b1d2e2247246b85
diff --git a/selfdrive/ui/sunnypilot/qt/network/networking.h b/selfdrive/ui/sunnypilot/qt/network/networking.h
index 3370751bf5..f220b8a50a 100644
--- a/selfdrive/ui/sunnypilot/qt/network/networking.h
+++ b/selfdrive/ui/sunnypilot/qt/network/networking.h
@@ -1,5 +1,5 @@
/**
-* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+ * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py
index 8a7ffe80ec..46f65c999b 100644
--- a/sunnypilot/mads/helpers.py
+++ b/sunnypilot/mads/helpers.py
@@ -1,27 +1,8 @@
"""
-The MIT License
-
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-Last updated: July 29, 2024
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
"""
from openpilot.common.params import Params
diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py
index 9e53a8eeed..991e345ce8 100644
--- a/sunnypilot/mads/mads.py
+++ b/sunnypilot/mads/mads.py
@@ -1,27 +1,8 @@
"""
-The MIT License
-
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-Last updated: July 29, 2024
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
"""
from cereal import car, log, custom
diff --git a/sunnypilot/mads/state.py b/sunnypilot/mads/state.py
index e3acf3a0e0..89ae506ac8 100644
--- a/sunnypilot/mads/state.py
+++ b/sunnypilot/mads/state.py
@@ -1,27 +1,8 @@
"""
-The MIT License
-
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-Last updated: July 29, 2024
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
"""
from cereal import log, custom
diff --git a/sunnypilot/mads/tests/test_mads_state_machine.py b/sunnypilot/mads/tests/test_mads_state_machine.py
index 546893d285..51013b0baf 100644
--- a/sunnypilot/mads/tests/test_mads_state_machine.py
+++ b/sunnypilot/mads/tests/test_mads_state_machine.py
@@ -1,27 +1,8 @@
"""
-The MIT License
-
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-Last updated: July 29, 2024
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
"""
import pytest
diff --git a/sunnypilot/modeld/runners/run_helpers.py b/sunnypilot/modeld/runners/run_helpers.py
index cc4c644583..5470106f1d 100644
--- a/sunnypilot/modeld/runners/run_helpers.py
+++ b/sunnypilot/modeld/runners/run_helpers.py
@@ -1,7 +1,9 @@
-# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-#
-# This file is part of sunnypilot and is licensed under the MIT License.
-# See the LICENSE.md file in the root directory for more details.
+"""
+Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
+"""
import os
import pickle
diff --git a/sunnypilot/models/fetcher.py b/sunnypilot/models/fetcher.py
index f543b00a79..d9aa54aa88 100644
--- a/sunnypilot/models/fetcher.py
+++ b/sunnypilot/models/fetcher.py
@@ -1,7 +1,9 @@
-# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-#
-# This file is part of sunnypilot and is licensed under the MIT License.
-# See the LICENSE.md file in the root directory for more details.
+"""
+Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
+"""
import json
import time
diff --git a/sunnypilot/models/helpers.py b/sunnypilot/models/helpers.py
index 915f1a4206..dbdef55900 100644
--- a/sunnypilot/models/helpers.py
+++ b/sunnypilot/models/helpers.py
@@ -1,7 +1,9 @@
-# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-#
-# This file is part of sunnypilot and is licensed under the MIT License.
-# See the LICENSE.md file in the root directory for more details.
+"""
+Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
+"""
import hashlib
import os
diff --git a/sunnypilot/models/manager.py b/sunnypilot/models/manager.py
index 06705bd5ce..5fbad6cd8e 100644
--- a/sunnypilot/models/manager.py
+++ b/sunnypilot/models/manager.py
@@ -1,7 +1,9 @@
-# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-#
-# This file is part of sunnypilot and is licensed under the MIT License.
-# See the LICENSE.md file in the root directory for more details.
+"""
+Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
+"""
import asyncio
import os
diff --git a/sunnypilot/models/tests/model_manager_audit.py b/sunnypilot/models/tests/model_manager_audit.py
index 1a1ea38e5b..4cd2b7d78e 100644
--- a/sunnypilot/models/tests/model_manager_audit.py
+++ b/sunnypilot/models/tests/model_manager_audit.py
@@ -1,7 +1,9 @@
-# Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
-#
-# This file is part of sunnypilot and is licensed under the MIT License.
-# See the LICENSE.md file in the root directory for more details.
+"""
+Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
+
+This file is part of sunnypilot and is licensed under the MIT License.
+See the LICENSE.md file in the root directory for more details.
+"""
from cereal import messaging, custom
From 3177142bfcb7555976b3de2f092ba0d7e93002d5 Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Mon, 17 Mar 2025 03:49:27 -0400
Subject: [PATCH 60/68] ci: update existing comment during Squash and Merge
(#674)
* format
* ci: update existing comment during Squash and Merge
* dynamic target branch
* test label
* try again
* no f
* test
* should be edit-last
* quote
* fix arg
* final
* bold it
* revert
---
release/ci/squash_and_merge_prs.py | 270 ++++++++++++++++-------------
1 file changed, 152 insertions(+), 118 deletions(-)
diff --git a/release/ci/squash_and_merge_prs.py b/release/ci/squash_and_merge_prs.py
index 28ffd5fc3a..3abe06705a 100755
--- a/release/ci/squash_and_merge_prs.py
+++ b/release/ci/squash_and_merge_prs.py
@@ -9,155 +9,189 @@ from datetime import datetime
def setup_argument_parser():
- parser = argparse.ArgumentParser(description='Process and squash GitHub PRs')
- parser.add_argument('--pr-data', type=str, help='PR data in JSON format')
- parser.add_argument('--source-branch', type=str, default='master-new',
- help='Source branch for merging')
- parser.add_argument('--target-branch', type=str, default='master-dev-c3-new-test',
- help='Target branch for merging')
- parser.add_argument('--squash-script-path', type=str, required=True,
- help='Path to the squash_and_merge.py script')
- return parser
+ parser = argparse.ArgumentParser(description='Process and squash GitHub PRs')
+ parser.add_argument('--pr-data', type=str, help='PR data in JSON format')
+ parser.add_argument('--source-branch', type=str, default='master-new',
+ help='Source branch for merging')
+ parser.add_argument('--target-branch', type=str, default='master-dev-c3-new-test',
+ help='Target branch for merging')
+ parser.add_argument('--squash-script-path', type=str, required=True,
+ help='Path to the squash_and_merge.py script')
+ return parser
def validate_squash_script(script_path):
- if not os.path.isfile(script_path):
- raise FileNotFoundError(f"Squash script not found at: {script_path}")
- if not os.access(script_path, os.X_OK):
- raise PermissionError(f"Squash script is not executable: {script_path}")
+ if not os.path.isfile(script_path):
+ raise FileNotFoundError(f"Squash script not found at: {script_path}")
+ if not os.access(script_path, os.X_OK):
+ raise PermissionError(f"Squash script is not executable: {script_path}")
def sort_prs_by_creation(pr_data):
- """Sort PRs by creation date"""
- nodes = (pr_data.get('data', {}).get('search', {}).get('nodes', []))
+ """Sort PRs by creation date"""
+ nodes = (pr_data.get('data', {}).get('search', {}).get('nodes', []))
- return sorted(
- nodes,
- key=lambda x: datetime.fromisoformat(x.get('createdAt', '').replace('Z', '+00:00'))
- )
+ return sorted(
+ nodes,
+ key=lambda x: datetime.fromisoformat(x.get('createdAt', '').replace('Z', '+00:00'))
+ )
def add_pr_comment(pr_number, comment):
- """Add a comment to a PR using gh cli"""
- try:
- subprocess.run(
- ['gh', 'pr', 'comment', str(pr_number), '--body', comment],
- check=True,
- capture_output=True,
- text=True
- )
- except subprocess.CalledProcessError as e:
- print(f"Failed to add comment to PR #{pr_number}: {e.stderr}")
+ """Add or update a comment to a PR using gh cli"""
+ title = "## Squash and Merge"
+
+ try:
+ result = subprocess.run(
+ ['gh', 'pr', 'view', str(pr_number), '--json', 'comments'],
+ check=True,
+ capture_output=True,
+ text=True
+ )
+
+ comments_data = json.loads(result.stdout)
+ has_existing_comment = False
+
+ for pr_comment in comments_data['comments']:
+ if pr_comment['body'].startswith(title):
+ has_existing_comment = True
+ break
+
+ full_comment = f"{title}\n\n{comment}"
+
+ if has_existing_comment:
+ subprocess.run(
+ ['gh', 'pr', 'comment', '--edit-last', f"#{pr_number}", '--body', full_comment],
+ check=True,
+ capture_output=True,
+ text=True
+ )
+ else:
+ subprocess.run(
+ ['gh', 'pr', 'comment', f"#{pr_number}", '--body', full_comment],
+ check=True,
+ capture_output=True,
+ text=True
+ )
+
+ except subprocess.CalledProcessError as e:
+ print(f"Failed to add/update comment on PR #{pr_number}: {e.stderr}")
+ except json.JSONDecodeError:
+ print(f"Failed to parse comments data for PR #{pr_number}")
+
def validate_pr(pr):
- """Validate a PR and return (is_valid, skip_reason)"""
- pr_number = pr.get('number', 'UNKNOWN')
- branch = pr.get('headRefName', '')
+ """Validate a PR and return (is_valid, skip_reason)"""
+ pr_number = pr.get('number', 'UNKNOWN')
+ branch = pr.get('headRefName', '')
- if not branch:
- return False, f"Missing branch name for PR #{pr_number}"
+ if not branch:
+ return False, f"missing branch name for PR #{pr_number}"
- # Check if checks have passed
- commits = pr.get('commits', {}).get('nodes', [])
- if not commits:
- return False, "No commit data found"
+ # Check if checks have passed
+ commits = pr.get('commits', {}).get('nodes', [])
+ if not commits:
+ return False, "no commit data found"
- status = commits[0].get('commit', {}).get('statusCheckRollup', {})
- if not status or status.get('state') != 'SUCCESS':
- return False, "Not all checks have passed"
+ status = commits[0].get('commit', {}).get('statusCheckRollup', {})
+ if not status or status.get('state') != 'SUCCESS':
+ return False, "not all checks have passed"
- # Check for merge conflicts
- merge_status = subprocess.run(['gh', 'pr', 'view', str(pr_number), '--json', 'mergeable,mergeStateStatus'], capture_output=True, text=True)
- merge_data = json.loads(merge_status.stdout)
- if not merge_data.get('mergeable'):
- return False, "Merge conflicts detected"
+ # Check for merge conflicts
+ merge_status = subprocess.run(['gh', 'pr', 'view', str(pr_number), '--json', 'mergeable,mergeStateStatus'],
+ capture_output=True, text=True)
+ merge_data = json.loads(merge_status.stdout)
+ if not merge_data.get('mergeable'):
+ return False, "merge conflicts detected"
- if (mergeStateStatus := merge_data.get('mergeStateStatus')) == "BEHIND":
- return False, f"Branch is `{mergeStateStatus}`"
+ if (mergeStateStatus := merge_data.get('mergeStateStatus')) == "BEHIND":
+ return False, f"branch is `{mergeStateStatus}`"
- return True, None
+ return True, None
def process_pr(pr_data, source_branch, target_branch, squash_script_path):
- try:
- nodes = sort_prs_by_creation(pr_data)
- if not nodes:
- print("No PRs to squash")
- return 0
+ try:
+ nodes = sort_prs_by_creation(pr_data)
+ if not nodes:
+ print("No PRs to squash")
+ return 0
- print(f"Deleting target branch {target_branch}")
- subprocess.run(['git', 'branch', '-D', target_branch], check=False)
- subprocess.run(['git', 'branch', target_branch, f'origin/{source_branch}'], check=True)
- success_count = 0
- for pr in nodes:
- pr_number = pr.get('number', 'UNKNOWN')
- branch = pr.get('headRefName', '')
- title = pr.get('title', '')
- is_valid, skip_reason = validate_pr(pr)
+ print(f"Deleting target branch {target_branch}")
+ subprocess.run(['git', 'branch', '-D', target_branch], check=False)
+ subprocess.run(['git', 'branch', target_branch, f'origin/{source_branch}'], check=True)
+ success_count = 0
+ for pr in nodes:
+ pr_number = pr.get('number', 'UNKNOWN')
+ branch = pr.get('headRefName', '')
+ title = pr.get('title', '')
+ is_valid, skip_reason = validate_pr(pr)
- if not is_valid:
- print(f"Warning: {skip_reason} for PR #{pr_number}, skipping")
- add_pr_comment(pr_number, f"⚠️ This PR was skipped in the automated `{target_branch}` squash because {skip_reason}.")
- continue
+ if not is_valid:
+ print(f"Warning: {skip_reason} for PR #{pr_number}, skipping")
+ add_pr_comment(pr_number,
+ f"⚠️ This PR was skipped in the automated `{target_branch}` squash because **{skip_reason}**.")
+ continue
- try:
- # Fetch PR branch
- subprocess.run(['git', 'fetch', 'origin', branch], check=True)
- # Delete branch if it exists (ignore errors if it doesn't)
- subprocess.run(['git', 'branch', '-D', branch], check=False)
- # Create new branch pointing to origin's branch
- subprocess.run(['git', 'branch', branch, f'origin/{branch}'], check=True)
+ try:
+ # Fetch PR branch
+ subprocess.run(['git', 'fetch', 'origin', branch], check=True)
+ # Delete branch if it exists (ignore errors if it doesn't)
+ subprocess.run(['git', 'branch', '-D', branch], check=False)
+ # Create new branch pointing to origin's branch
+ subprocess.run(['git', 'branch', branch, f'origin/{branch}'], check=True)
- # Run squash script
- subprocess.run([
- squash_script_path,
- '--target', target_branch,
- '--source', branch,
- '--title', f"{title} (#{pr_number})",
- ], check=True)
+ # Run squash script
+ subprocess.run([
+ squash_script_path,
+ '--target', target_branch,
+ '--source', branch,
+ '--title', f"{title} (#{pr_number})",
+ ], check=True)
- print(f"Successfully processed PR #{pr_number}")
- success_count += 1
+ print(f"Successfully processed PR #{pr_number}")
+ success_count += 1
- except subprocess.CalledProcessError as e:
- print(f"Error processing PR #{pr_number}:")
- print(f"Command failed with exit code {e.returncode}")
- error_output = getattr(e, 'stderr', 'No error output available')
- print(f"Error output: {error_output}")
- add_pr_comment(pr_number, f"⚠️ Error during automated {target_branch} squash:\n```\n{error_output}\n```")
- continue
- except Exception as e:
- print(f"Unexpected error processing PR #{pr_number}: {str(e)}")
- continue
+ except subprocess.CalledProcessError as e:
+ print(f"Error processing PR #{pr_number}:")
+ print(f"Command failed with exit code {e.returncode}")
+ error_output = getattr(e, 'stderr', 'No error output available')
+ print(f"Error output: {error_output}")
+ add_pr_comment(pr_number,
+ f"⚠️ Error during automated `{target_branch}` squash:\n```\n{error_output}\n```")
+ continue
+ except Exception as e:
+ print(f"Unexpected error processing PR #{pr_number}: {str(e)}")
+ continue
- return success_count
-
- except Exception as e:
- import traceback
- print(f"Error in process_pr: {str(e)}")
- print("Full traceback:")
- print(traceback.format_exc())
- return 0
-
-
-def main():
- parser = setup_argument_parser()
- try:
- args = parser.parse_args()
- validate_squash_script(args.squash_script_path)
- pr_data_json = json.loads(args.pr_data)
-
- # Process the PRs
- success_count = process_pr(pr_data_json, args.source_branch, args.target_branch, args.squash_script_path)
- print(f"Successfully processed {success_count} PRs")
-
- except Exception as e:
- print(f"Fatal error: {str(e)}", file=sys.stderr)
- return 1
+ return success_count
+ except Exception as e:
+ import traceback
+ print(f"Error in process_pr: {str(e)}")
+ print("Full traceback:")
+ print(traceback.format_exc())
return 0
+def main():
+ parser = setup_argument_parser()
+ try:
+ args = parser.parse_args()
+ validate_squash_script(args.squash_script_path)
+ pr_data_json = json.loads(args.pr_data)
+
+ # Process the PRs
+ success_count = process_pr(pr_data_json, args.source_branch, args.target_branch, args.squash_script_path)
+ print(f"Successfully processed {success_count} PRs")
+
+ except Exception as e:
+ print(f"Fatal error: {str(e)}", file=sys.stderr)
+ return 1
+
+ return 0
+
+
if __name__ == "__main__":
- sys.exit(main())
+ sys.exit(main())
From 6ad4ebdae64e7e7ecdee6463e40f3af53d461bc6 Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Wed, 19 Mar 2025 00:45:53 -0400
Subject: [PATCH 61/68] MADS: cleanup param assignments (#683)
---
selfdrive/car/card.py | 6 ++---
sunnypilot/mads/helpers.py | 54 +++++++++++++++++---------------------
sunnypilot/mads/mads.py | 16 +++++------
3 files changed, 34 insertions(+), 42 deletions(-)
diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py
index 39344f485f..33c229958d 100755
--- a/selfdrive/car/card.py
+++ b/selfdrive/car/card.py
@@ -24,7 +24,7 @@ from openpilot.selfdrive.car.cruise import VCruiseHelper
from openpilot.selfdrive.car.car_specific import MockCarState
from openpilot.selfdrive.car.helpers import convert_carControlSP, convert_to_capnp
-from openpilot.sunnypilot.mads.mads import MadsParams
+from openpilot.sunnypilot.mads.helpers import set_alternative_experience, set_car_specific_params
from openpilot.sunnypilot.selfdrive.car import interfaces as sunnypilot_interfaces
REPLAY = "REPLAY" in os.environ
@@ -126,8 +126,8 @@ class Car:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
# mads
- MadsParams().set_alternative_experience(self.CP)
- MadsParams().set_car_specific_params(self.CP, self.CP_SP)
+ set_alternative_experience(self.CP, self.params)
+ set_car_specific_params(self.CP, self.CP_SP, self.params)
# Dynamic Experimental Control
self.dynamic_experimental_control = self.params.get_bool("DynamicExperimentalControl")
diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py
index 46f65c999b..831a75699e 100644
--- a/sunnypilot/mads/helpers.py
+++ b/sunnypilot/mads/helpers.py
@@ -6,43 +6,37 @@ See the LICENSE.md file in the root directory for more details.
"""
from openpilot.common.params import Params
-
+from opendbc.car import structs
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP, HyundaiSafetyFlagsSP
-class MadsParams:
- def __init__(self):
- self.params = Params()
+def set_alternative_experience(CP: structs.CarParams, params: Params):
+ enabled = params.get_bool("Mads")
+ pause_lateral_on_brake = params.get_bool("MadsPauseLateralOnBrake")
- def read_param(self, key: str):
- return self.params.get_bool(key)
+ if enabled:
+ CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS
- def set_alternative_experience(self, CP):
- enabled = self.read_param("Mads")
- pause_lateral_on_brake = self.read_param("MadsPauseLateralOnBrake")
+ if pause_lateral_on_brake:
+ CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISENGAGE_LATERAL_ON_BRAKE
- if enabled:
- CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS
- if pause_lateral_on_brake:
- CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISENGAGE_LATERAL_ON_BRAKE
+def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params):
+ if CP.brand == "hyundai":
+ # TODO-SP: This should be separated from MADS module for future implementations
+ # Use "HyundaiLongitudinalMainCruiseToggleable" param
+ hyundai_cruise_main_toggleable = True
+ if hyundai_cruise_main_toggleable:
+ CP_SP.flags |= HyundaiFlagsSP.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE.value
+ CP_SP.safetyParam |= HyundaiSafetyFlagsSP.LONG_MAIN_CRUISE_TOGGLEABLE
- def set_car_specific_params(self, CP, CP_SP):
- if CP.brand == "hyundai":
- # TODO-SP: This should be separated from MADS module for future implementations
- # Use "HyundaiLongitudinalMainCruiseToggleable" param
- hyundai_cruise_main_toggleable = True
- if hyundai_cruise_main_toggleable:
- CP_SP.flags |= HyundaiFlagsSP.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE.value
- CP_SP.safetyParam |= HyundaiSafetyFlagsSP.LONG_MAIN_CRUISE_TOGGLEABLE
+ # MADS is currently not supported in Tesla due to lack of consistent states to engage controls
+ # TODO-SP: To enable MADS for Tesla, identify consistent signals for MADS toggling
+ if CP.brand == "tesla":
+ params.remove("Mads")
- # MADS is currently not supported in Tesla due to lack of consistent states to engage controls
- # TODO-SP: To enable MADS for Tesla, identify consistent signals for MADS toggling
- if CP.brand == "tesla":
- self.params.remove("Mads")
-
- # MADS is currently not supported in Rivian due to lack of consistent states to engage controls
- # TODO-SP: To enable MADS for Rivian, identify consistent signals for MADS toggling
- if CP.brand == "rivian":
- self.params.remove("Mads")
+ # MADS is currently not supported in Rivian due to lack of consistent states to engage controls
+ # TODO-SP: To enable MADS for Rivian, identify consistent signals for MADS toggling
+ if CP.brand == "rivian":
+ params.remove("Mads")
diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py
index 991e345ce8..632ca7f2a4 100644
--- a/sunnypilot/mads/mads.py
+++ b/sunnypilot/mads/mads.py
@@ -8,8 +8,6 @@ See the LICENSE.md file in the root directory for more details.
from cereal import car, log, custom
from opendbc.car.hyundai.values import HyundaiFlags
-
-from openpilot.sunnypilot.mads.helpers import MadsParams
from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
@@ -24,7 +22,7 @@ IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput)
class ModularAssistiveDrivingSystem:
def __init__(self, selfdrive):
- self.mads_params = MadsParams()
+ self.params = selfdrive.params
self.enabled = False
self.active = False
@@ -41,14 +39,14 @@ class ModularAssistiveDrivingSystem:
self.allow_always = True
# read params on init
- self.enabled_toggle = self.mads_params.read_param("Mads")
- self.main_enabled_toggle = self.mads_params.read_param("MadsMainCruiseAllowed")
- self.pause_lateral_on_brake_toggle = self.mads_params.read_param("MadsPauseLateralOnBrake")
- self.unified_engagement_mode = self.mads_params.read_param("MadsUnifiedEngagementMode")
+ self.enabled_toggle = self.params.get_bool("Mads")
+ self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed")
+ self.pause_lateral_on_brake_toggle = self.params.get_bool("MadsPauseLateralOnBrake")
+ self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode")
def read_params(self):
- self.main_enabled_toggle = self.mads_params.read_param("MadsMainCruiseAllowed")
- self.unified_engagement_mode = self.mads_params.read_param("MadsUnifiedEngagementMode")
+ self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed")
+ self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode")
def update_events(self, CS: car.CarState):
def update_unified_engagement_mode():
From ad67bdc853e380f44a80413744171e389fe78607 Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Wed, 19 Mar 2025 10:11:07 -0400
Subject: [PATCH 62/68] ci: disable prebuilts on pull requests (#685)
---
.github/workflows/sunnypilot-build-prebuilt.yaml | 2 --
1 file changed, 2 deletions(-)
diff --git a/.github/workflows/sunnypilot-build-prebuilt.yaml b/.github/workflows/sunnypilot-build-prebuilt.yaml
index 8bb84cda83..cbe2b73be3 100644
--- a/.github/workflows/sunnypilot-build-prebuilt.yaml
+++ b/.github/workflows/sunnypilot-build-prebuilt.yaml
@@ -21,8 +21,6 @@ on:
push:
branches: [ master, master-new, master-dev-c3-new ]
tags: [ '*' ]
- pull_request:
- branches: [ master, master-new ]
workflow_dispatch:
inputs:
extra_version:
From 4525659a7eb56b3a37332479b1c7490c83d1e504 Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Wed, 19 Mar 2025 13:57:15 -0400
Subject: [PATCH 63/68] MADS: Steering Mode on Brake Pedal Press (#684)
* MADS: cleanup param assignments
* MADS: Steering Mode on Brake Pedal Press
* bump opendbc
* translations
* make sure to initialize
* ci try this
* Revert "ci try this"
This reverts commit da975f61c34752cdad8a7dc7f08b196d2e8e2bf4.
* Revert "translations"
This reverts commit a65aad59114d40cf1486146fedc10c3683241b9c.
---
common/params_keys.h | 2 +-
opendbc_repo | 2 +-
selfdrive/pandad/pandad.cc | 4 ++--
.../settings/sunnypilot/mads_settings.cc | 18 +++++++++-------
.../settings/sunnypilot/mads_settings.h | 2 +-
sunnypilot/mads/helpers.py | 21 ++++++++++++++++---
sunnypilot/mads/mads.py | 13 +++++++++---
system/manager/manager.py | 2 +-
8 files changed, 44 insertions(+), 20 deletions(-)
diff --git a/common/params_keys.h b/common/params_keys.h
index 6bcf897c3e..d88189d432 100644
--- a/common/params_keys.h
+++ b/common/params_keys.h
@@ -132,7 +132,7 @@ inline static std::unordered_map keys = {
// MADS params
{"Mads", PERSISTENT | BACKUP},
{"MadsMainCruiseAllowed", PERSISTENT | BACKUP},
- {"MadsPauseLateralOnBrake", PERSISTENT | BACKUP},
+ {"MadsSteeringMode", PERSISTENT | BACKUP},
{"MadsUnifiedEngagementMode", PERSISTENT | BACKUP},
// Model Manager params
diff --git a/opendbc_repo b/opendbc_repo
index 9298a1ac9d..e670860afd 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit 9298a1ac9ddcd0fed977cd518fc62c45b780ef36
+Subproject commit e670860afd704e684c58d7a62a7dfda67bb51e44
diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc
index 3025d0091b..c0ff2db399 100644
--- a/selfdrive/pandad/pandad.cc
+++ b/selfdrive/pandad/pandad.cc
@@ -41,7 +41,7 @@
#define CUTOFF_IL 400
#define SATURATE_IL 1000
-#define ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE 2048
+#define ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE 2048
ExitHandler do_exit;
@@ -57,7 +57,7 @@ bool check_all_connected(const std::vector &pandas) {
bool process_mads_heartbeat(SubMaster *sm) {
const int &alt_exp = (*sm)["carParams"].getCarParams().getAlternativeExperience();
- const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE) != 0;
+ const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE) != 0;
const auto &mads = (*sm)["selfdriveStateSP"].getSelfdriveStateSP().getMads();
const bool heartbeat_type = disengage_lateral_on_brake ? mads.getActive() : mads.getEnabled();
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
index e6e18f262e..985e89ebca 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
@@ -39,17 +39,19 @@ MadsSettings::MadsSettings(QWidget *parent) : QWidget(parent) {
"");
list->addItem(madsUnifiedEngagementModeToggle);
- // Pause Lateral On Brake
- std::vector lateral_on_brake_texts{tr("Remain Active"), tr("Pause Steering")};
- madsPauseLateralOnBrake = new ButtonParamControl(
- "MadsPauseLateralOnBrake",
- tr("Steering Mode After Braking"),
+ // Steering Mode On Brake
+ std::vector lateral_on_brake_texts{tr("Disengage"), tr("Remain Active"), tr("Pause Steering")};
+ madsSteeringMode = new ButtonParamControl(
+ "MadsSteeringMode",
+ tr("Steering Mode on Brake Pedal"),
tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.\n\n"
- "Remain Active: ALC will remain active even after the brake pedal is pressed.\nPause Steering: ALC will be paused after the brake pedal is manually pressed."),
+ "Disengage: ALC will be disengaged after the brake pedal is pressed.\n"
+ "Remain Active: ALC will remain active even after the brake pedal is pressed.\n"
+ "Pause Steering: ALC will be paused when the brake pedal is manually pressed."),
"",
lateral_on_brake_texts,
500);
- list->addItem(madsPauseLateralOnBrake);
+ list->addItem(madsSteeringMode);
QObject::connect(uiState(), &UIState::offroadTransition, this, &MadsSettings::updateToggles);
@@ -61,7 +63,7 @@ void MadsSettings::showEvent(QShowEvent *event) {
}
void MadsSettings::updateToggles(bool _offroad) {
- madsPauseLateralOnBrake->setEnabled(_offroad);
+ madsSteeringMode->setEnabled(_offroad);
offroad = _offroad;
}
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
index fccb4cbb84..312c4f0db0 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
@@ -32,5 +32,5 @@ private:
ParamControl *madsMainCruiseToggle;
ParamControl *madsUnifiedEngagementModeToggle;
- ButtonParamControl *madsPauseLateralOnBrake;
+ ButtonParamControl *madsSteeringMode;
};
diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py
index 831a75699e..4a3f964882 100644
--- a/sunnypilot/mads/helpers.py
+++ b/sunnypilot/mads/helpers.py
@@ -11,15 +11,30 @@ from opendbc.safety import ALTERNATIVE_EXPERIENCE
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP, HyundaiSafetyFlagsSP
+class MadsSteeringModeOnBrake:
+ DISENGAGE = 0
+ REMAIN_ACTIVE = 1
+ PAUSE = 2
+
+
+def read_steering_mode_param(params: Params):
+ try:
+ return int(params.get("MadsSteeringMode"))
+ except (ValueError, TypeError):
+ return f"{MadsSteeringModeOnBrake.REMAIN_ACTIVE}"
+
+
def set_alternative_experience(CP: structs.CarParams, params: Params):
enabled = params.get_bool("Mads")
- pause_lateral_on_brake = params.get_bool("MadsPauseLateralOnBrake")
+ steering_mode = read_steering_mode_param(params)
if enabled:
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS
- if pause_lateral_on_brake:
- CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISENGAGE_LATERAL_ON_BRAKE
+ if steering_mode == MadsSteeringModeOnBrake.DISENGAGE:
+ CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_DISENGAGE_LATERAL_ON_BRAKE
+ elif steering_mode == MadsSteeringModeOnBrake.PAUSE:
+ CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_PAUSE_LATERAL_ON_BRAKE
def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params):
diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py
index 632ca7f2a4..384e48b3fa 100644
--- a/sunnypilot/mads/mads.py
+++ b/sunnypilot/mads/mads.py
@@ -8,6 +8,7 @@ See the LICENSE.md file in the root directory for more details.
from cereal import car, log, custom
from opendbc.car.hyundai.values import HyundaiFlags
+from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake, read_steering_mode_param
from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
@@ -41,7 +42,7 @@ class ModularAssistiveDrivingSystem:
# read params on init
self.enabled_toggle = self.params.get_bool("Mads")
self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed")
- self.pause_lateral_on_brake_toggle = self.params.get_bool("MadsPauseLateralOnBrake")
+ self.steering_mode_on_brake = read_steering_mode_param(self.params)
self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode")
def read_params(self):
@@ -83,11 +84,11 @@ class ModularAssistiveDrivingSystem:
replace_event(EventName.parkBrake, EventNameSP.silentParkBrake)
transition_paused_state()
- if self.pause_lateral_on_brake_toggle:
+ if self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE:
if CS.brakePressed:
transition_paused_state()
- if not (self.pause_lateral_on_brake_toggle and CS.brakePressed) and \
+ if not (self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE and CS.brakePressed) and \
not self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT):
if self.state_machine.state == State.paused:
self.events_sp.add(EventNameSP.silentLkasEnable)
@@ -123,6 +124,12 @@ class ModularAssistiveDrivingSystem:
if self.selfdrive.CS_prev.cruiseState.available:
self.events_sp.add(EventNameSP.lkasDisable)
+ if self.steering_mode_on_brake == MadsSteeringModeOnBrake.DISENGAGE:
+ # Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0
+ if (CS.brakePressed and (not self.selfdrive.CS_prev.brakePressed or not CS.standstill)) or \
+ (CS.regenBraking and (not self.selfdrive.CS_prev.regenBraking or not CS.standstill)):
+ self.events_sp.add(EventNameSP.lkasDisable)
+
self.events.remove(EventName.pcmDisable)
self.events.remove(EventName.buttonCancel)
self.events.remove(EventName.pedalPressed)
diff --git a/system/manager/manager.py b/system/manager/manager.py
index 499ed6474b..d5da672427 100755
--- a/system/manager/manager.py
+++ b/system/manager/manager.py
@@ -46,7 +46,7 @@ def manager_init() -> None:
("DynamicExperimentalControl", "0"),
("Mads", "1"),
("MadsMainCruiseAllowed", "1"),
- ("MadsPauseLateralOnBrake", "0"),
+ ("MadsSteeringMode", "1"),
("MadsUnifiedEngagementMode", "1"),
("ModelManager_LastSyncTime", "0"),
("ModelManager_ModelsCache", "")
From dd37a35b59d921d14b94e361241b3ecf107050fc Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Wed, 19 Mar 2025 16:02:45 -0400
Subject: [PATCH 64/68] Revert "MADS: Steering Mode on Brake Pedal Press"
(#686)
* Revert "MADS: Steering Mode on Brake Pedal Press (#684)"
This reverts commit 4525659a7eb56b3a37332479b1c7490c83d1e504.
* bump
---
common/params_keys.h | 2 +-
opendbc_repo | 2 +-
selfdrive/pandad/pandad.cc | 4 ++--
.../settings/sunnypilot/mads_settings.cc | 18 +++++++---------
.../settings/sunnypilot/mads_settings.h | 2 +-
sunnypilot/mads/helpers.py | 21 +++----------------
sunnypilot/mads/mads.py | 13 +++---------
system/manager/manager.py | 2 +-
8 files changed, 20 insertions(+), 44 deletions(-)
diff --git a/common/params_keys.h b/common/params_keys.h
index d88189d432..6bcf897c3e 100644
--- a/common/params_keys.h
+++ b/common/params_keys.h
@@ -132,7 +132,7 @@ inline static std::unordered_map keys = {
// MADS params
{"Mads", PERSISTENT | BACKUP},
{"MadsMainCruiseAllowed", PERSISTENT | BACKUP},
- {"MadsSteeringMode", PERSISTENT | BACKUP},
+ {"MadsPauseLateralOnBrake", PERSISTENT | BACKUP},
{"MadsUnifiedEngagementMode", PERSISTENT | BACKUP},
// Model Manager params
diff --git a/opendbc_repo b/opendbc_repo
index e670860afd..c00137782e 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit e670860afd704e684c58d7a62a7dfda67bb51e44
+Subproject commit c00137782e6931ec09c109777199ad86d4955955
diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc
index c0ff2db399..3025d0091b 100644
--- a/selfdrive/pandad/pandad.cc
+++ b/selfdrive/pandad/pandad.cc
@@ -41,7 +41,7 @@
#define CUTOFF_IL 400
#define SATURATE_IL 1000
-#define ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE 2048
+#define ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE 2048
ExitHandler do_exit;
@@ -57,7 +57,7 @@ bool check_all_connected(const std::vector &pandas) {
bool process_mads_heartbeat(SubMaster *sm) {
const int &alt_exp = (*sm)["carParams"].getCarParams().getAlternativeExperience();
- const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE) != 0;
+ const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE) != 0;
const auto &mads = (*sm)["selfdriveStateSP"].getSelfdriveStateSP().getMads();
const bool heartbeat_type = disengage_lateral_on_brake ? mads.getActive() : mads.getEnabled();
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
index 985e89ebca..e6e18f262e 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
@@ -39,19 +39,17 @@ MadsSettings::MadsSettings(QWidget *parent) : QWidget(parent) {
"");
list->addItem(madsUnifiedEngagementModeToggle);
- // Steering Mode On Brake
- std::vector lateral_on_brake_texts{tr("Disengage"), tr("Remain Active"), tr("Pause Steering")};
- madsSteeringMode = new ButtonParamControl(
- "MadsSteeringMode",
- tr("Steering Mode on Brake Pedal"),
+ // Pause Lateral On Brake
+ std::vector lateral_on_brake_texts{tr("Remain Active"), tr("Pause Steering")};
+ madsPauseLateralOnBrake = new ButtonParamControl(
+ "MadsPauseLateralOnBrake",
+ tr("Steering Mode After Braking"),
tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.\n\n"
- "Disengage: ALC will be disengaged after the brake pedal is pressed.\n"
- "Remain Active: ALC will remain active even after the brake pedal is pressed.\n"
- "Pause Steering: ALC will be paused when the brake pedal is manually pressed."),
+ "Remain Active: ALC will remain active even after the brake pedal is pressed.\nPause Steering: ALC will be paused after the brake pedal is manually pressed."),
"",
lateral_on_brake_texts,
500);
- list->addItem(madsSteeringMode);
+ list->addItem(madsPauseLateralOnBrake);
QObject::connect(uiState(), &UIState::offroadTransition, this, &MadsSettings::updateToggles);
@@ -63,7 +61,7 @@ void MadsSettings::showEvent(QShowEvent *event) {
}
void MadsSettings::updateToggles(bool _offroad) {
- madsSteeringMode->setEnabled(_offroad);
+ madsPauseLateralOnBrake->setEnabled(_offroad);
offroad = _offroad;
}
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
index 312c4f0db0..fccb4cbb84 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
@@ -32,5 +32,5 @@ private:
ParamControl *madsMainCruiseToggle;
ParamControl *madsUnifiedEngagementModeToggle;
- ButtonParamControl *madsSteeringMode;
+ ButtonParamControl *madsPauseLateralOnBrake;
};
diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py
index 4a3f964882..831a75699e 100644
--- a/sunnypilot/mads/helpers.py
+++ b/sunnypilot/mads/helpers.py
@@ -11,30 +11,15 @@ from opendbc.safety import ALTERNATIVE_EXPERIENCE
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP, HyundaiSafetyFlagsSP
-class MadsSteeringModeOnBrake:
- DISENGAGE = 0
- REMAIN_ACTIVE = 1
- PAUSE = 2
-
-
-def read_steering_mode_param(params: Params):
- try:
- return int(params.get("MadsSteeringMode"))
- except (ValueError, TypeError):
- return f"{MadsSteeringModeOnBrake.REMAIN_ACTIVE}"
-
-
def set_alternative_experience(CP: structs.CarParams, params: Params):
enabled = params.get_bool("Mads")
- steering_mode = read_steering_mode_param(params)
+ pause_lateral_on_brake = params.get_bool("MadsPauseLateralOnBrake")
if enabled:
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS
- if steering_mode == MadsSteeringModeOnBrake.DISENGAGE:
- CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_DISENGAGE_LATERAL_ON_BRAKE
- elif steering_mode == MadsSteeringModeOnBrake.PAUSE:
- CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_PAUSE_LATERAL_ON_BRAKE
+ if pause_lateral_on_brake:
+ CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISENGAGE_LATERAL_ON_BRAKE
def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params):
diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py
index 384e48b3fa..632ca7f2a4 100644
--- a/sunnypilot/mads/mads.py
+++ b/sunnypilot/mads/mads.py
@@ -8,7 +8,6 @@ See the LICENSE.md file in the root directory for more details.
from cereal import car, log, custom
from opendbc.car.hyundai.values import HyundaiFlags
-from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake, read_steering_mode_param
from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
@@ -42,7 +41,7 @@ class ModularAssistiveDrivingSystem:
# read params on init
self.enabled_toggle = self.params.get_bool("Mads")
self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed")
- self.steering_mode_on_brake = read_steering_mode_param(self.params)
+ self.pause_lateral_on_brake_toggle = self.params.get_bool("MadsPauseLateralOnBrake")
self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode")
def read_params(self):
@@ -84,11 +83,11 @@ class ModularAssistiveDrivingSystem:
replace_event(EventName.parkBrake, EventNameSP.silentParkBrake)
transition_paused_state()
- if self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE:
+ if self.pause_lateral_on_brake_toggle:
if CS.brakePressed:
transition_paused_state()
- if not (self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE and CS.brakePressed) and \
+ if not (self.pause_lateral_on_brake_toggle and CS.brakePressed) and \
not self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT):
if self.state_machine.state == State.paused:
self.events_sp.add(EventNameSP.silentLkasEnable)
@@ -124,12 +123,6 @@ class ModularAssistiveDrivingSystem:
if self.selfdrive.CS_prev.cruiseState.available:
self.events_sp.add(EventNameSP.lkasDisable)
- if self.steering_mode_on_brake == MadsSteeringModeOnBrake.DISENGAGE:
- # Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0
- if (CS.brakePressed and (not self.selfdrive.CS_prev.brakePressed or not CS.standstill)) or \
- (CS.regenBraking and (not self.selfdrive.CS_prev.regenBraking or not CS.standstill)):
- self.events_sp.add(EventNameSP.lkasDisable)
-
self.events.remove(EventName.pcmDisable)
self.events.remove(EventName.buttonCancel)
self.events.remove(EventName.pedalPressed)
diff --git a/system/manager/manager.py b/system/manager/manager.py
index d5da672427..499ed6474b 100755
--- a/system/manager/manager.py
+++ b/system/manager/manager.py
@@ -46,7 +46,7 @@ def manager_init() -> None:
("DynamicExperimentalControl", "0"),
("Mads", "1"),
("MadsMainCruiseAllowed", "1"),
- ("MadsSteeringMode", "1"),
+ ("MadsPauseLateralOnBrake", "0"),
("MadsUnifiedEngagementMode", "1"),
("ModelManager_LastSyncTime", "0"),
("ModelManager_ModelsCache", "")
From d2846741bccb17efc458d1a8f98ef9837928eeee Mon Sep 17 00:00:00 2001
From: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com>
Date: Wed, 19 Mar 2025 14:06:42 -0700
Subject: [PATCH 65/68] ui: Error log button to Developer panel (#627)
* Add error log to developer panel
* Testing debug
* Fix condition for retry handling in action workflow
Updated the logic to properly check for 'NONE' author association instead of 'FIRST_TIME_CONTRIBUTOR' when determining retry limits. This ensures the workflow behaves as intended for contributors without prior activity.
* Clean
* Refine workflow condition logic for PR checks
Updated the `if` condition in the lfs-maintenance workflow to improve clarity and ensure proper evaluation. This change aligns the logical grouping for better readability and correctness in forked PR handling.
* Add lang files
* Update selfdrive/ui/qt/offroad/developer_panel.cc
Co-authored-by: Jason Wen
* Refactor GitHub runner control to local variable
Replaced the `enableGithubRunner` member variable with a local variable, as it is not used elsewhere in the class. Simplified visibility management for developer panel toggles in release branches.
* Updating translations
---------
Co-authored-by: DevTekVE
Co-authored-by: Jason Wen
---
selfdrive/ui/qt/offroad/developer_panel.cc | 11 +++++++++++
selfdrive/ui/qt/offroad/developer_panel.h | 1 +
selfdrive/ui/translations/main_ar.ts | 12 ++++++++++++
selfdrive/ui/translations/main_de.ts | 12 ++++++++++++
selfdrive/ui/translations/main_es.ts | 12 ++++++++++++
selfdrive/ui/translations/main_fr.ts | 12 ++++++++++++
selfdrive/ui/translations/main_ja.ts | 12 ++++++++++++
selfdrive/ui/translations/main_ko.ts | 12 ++++++++++++
selfdrive/ui/translations/main_pt-BR.ts | 12 ++++++++++++
selfdrive/ui/translations/main_th.ts | 12 ++++++++++++
selfdrive/ui/translations/main_tr.ts | 12 ++++++++++++
selfdrive/ui/translations/main_zh-CHS.ts | 12 ++++++++++++
selfdrive/ui/translations/main_zh-CHT.ts | 12 ++++++++++++
13 files changed, 144 insertions(+)
diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc
index e679720a1b..b918a9353d 100644
--- a/selfdrive/ui/qt/offroad/developer_panel.cc
+++ b/selfdrive/ui/qt/offroad/developer_panel.cc
@@ -60,6 +60,14 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) {
auto enableGithubRunner = new ParamControl("EnableGithubRunner", tr("Enable GitHub runner service"), tr("Enables or disables the github runner service."), "");
addItem(enableGithubRunner);
+ // error log button
+ errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), tr("View the error log for sunnypilot crashes."));
+ connect(errorLogBtn, &ButtonControl::clicked, [=]() {
+ std::string txt = util::read_file("/data/community/crashes/error.log");
+ ConfirmationDialog::rich(QString::fromStdString(txt), this);
+ });
+ addItem(errorLogBtn);
+
// Joystick and longitudinal maneuvers should be hidden on release branches
is_release = params.getBool("IsReleaseBranch");
@@ -112,6 +120,9 @@ void DeveloperPanel::updateToggles(bool _offroad) {
}
experimentalLongitudinalToggle->refresh();
+ // Handle specific controls visibility for release branches
+ errorLogBtn->setVisible(!is_release);
+
offroad = _offroad;
}
diff --git a/selfdrive/ui/qt/offroad/developer_panel.h b/selfdrive/ui/qt/offroad/developer_panel.h
index beecc24dee..c1783444ea 100644
--- a/selfdrive/ui/qt/offroad/developer_panel.h
+++ b/selfdrive/ui/qt/offroad/developer_panel.h
@@ -16,6 +16,7 @@ private:
Params params;
ParamControl* adbToggle;
ParamControl* joystickToggle;
+ ButtonControl* errorLogBtn;
ParamControl* longManeuverToggle;
ParamControl* experimentalLongitudinalToggle;
ParamControl* hyundaiRadarTracksToggle;
diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts
index 4cb648dc3d..643d563a78 100644
--- a/selfdrive/ui/translations/main_ar.ts
+++ b/selfdrive/ui/translations/main_ar.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ عرض
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts
index 5a994c4ad6..622a822ba9 100644
--- a/selfdrive/ui/translations/main_de.ts
+++ b/selfdrive/ui/translations/main_de.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ ANSEHEN
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts
index 77b1ecb6e4..25ed875eff 100644
--- a/selfdrive/ui/translations/main_es.ts
+++ b/selfdrive/ui/translations/main_es.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ VER
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts
index 94ebdb0a15..2da3eabafe 100644
--- a/selfdrive/ui/translations/main_fr.ts
+++ b/selfdrive/ui/translations/main_fr.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ VOIR
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts
index 1c6ab9a8b4..9d20a1e7d2 100644
--- a/selfdrive/ui/translations/main_ja.ts
+++ b/selfdrive/ui/translations/main_ja.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ 確認
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index 3286cc081f..8a7dc29fd3 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ 보기
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index 7a18c35989..5fc837513d 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ VER
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts
index abb3088dfe..ca3effbb72 100644
--- a/selfdrive/ui/translations/main_th.ts
+++ b/selfdrive/ui/translations/main_th.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ ดู
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts
index 4e0e93c708..a03a63dfb7 100644
--- a/selfdrive/ui/translations/main_tr.ts
+++ b/selfdrive/ui/translations/main_tr.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ BAK
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index c9807fa2d6..6b1bee2d9e 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ 查看
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index 1df2d934a8..17fa912d20 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -159,6 +159,18 @@
Enables or disables the github runner service.
+
+ Error Log
+
+
+
+ VIEW
+ 觀看
+
+
+ View the error log for sunnypilot crashes.
+
+ DevicePanel
From 01998f864d04489804a586a4ff0d85f77c4fd5f2 Mon Sep 17 00:00:00 2001
From: DevTekVE
Date: Wed, 19 Mar 2025 22:47:39 +0100
Subject: [PATCH 66/68] sunnylink: Improve API initialization and enhance
WebSocket security (#662)
Refactor sunnylinkd for better readability and SSL support
Reorganized variable handling for Sunnylink API initialization. Updated WebSocket connection to use Authorization header and added SSL configuration based on the connection type. This enhances code clarity and ensures secure connections when not on localhost.
Co-authored-by: Jason Wen
---
sunnypilot/sunnylink/athena/sunnylinkd.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/sunnypilot/sunnylink/athena/sunnylinkd.py b/sunnypilot/sunnylink/athena/sunnylinkd.py
index 0fb7ea6d9a..2c4b95fa46 100755
--- a/sunnypilot/sunnylink/athena/sunnylinkd.py
+++ b/sunnypilot/sunnylink/athena/sunnylinkd.py
@@ -5,6 +5,7 @@ from __future__ import annotations
import base64
import gzip
import os
+import ssl
import threading
import time
@@ -29,7 +30,8 @@ SUNNYLINK_RECONNECT_TIMEOUT_S = 70 # FYI changing this will also would require
DISALLOW_LOG_UPLOAD = threading.Event()
params = Params()
-sunnylink_api = SunnylinkApi(params.get("SunnylinkDongleId", encoding='utf-8'))
+sunnylink_dongle_id = params.get("SunnylinkDongleId", encoding='utf-8')
+sunnylink_api = SunnylinkApi(sunnylink_dongle_id)
def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None:
@@ -211,7 +213,7 @@ def main(exit_event: threading.Event = None):
UploadQueueCache.initialize(upload_queue)
- ws_uri = SUNNYLINK_ATHENA_HOST
+ ws_uri = f"{SUNNYLINK_ATHENA_HOST}"
conn_start = None
conn_retries = 0
while (exit_event is None or not exit_event.is_set()) and sunnylink_ready(params):
@@ -222,8 +224,9 @@ def main(exit_event: threading.Event = None):
cloudlog.event("sunnylinkd.main.connecting_ws", ws_uri=ws_uri, retries=conn_retries)
ws = create_connection(
ws_uri,
- cookie=f"jwt={sunnylink_api.get_token()}",
+ header={"Authorization": f"Bearer {sunnylink_api.get_token()}"},
enable_multithread=True,
+ sslopt={"cert_reqs": ssl.CERT_NONE if "localhost" in ws_uri else ssl.CERT_REQUIRED},
timeout=SUNNYLINK_RECONNECT_TIMEOUT_S,
)
cloudlog.event("sunnylinkd.main.connected_ws", ws_uri=ws_uri, retries=conn_retries,
From bf2731b4705161c5c11e1359242abed27790984f Mon Sep 17 00:00:00 2001
From: Jason Wen
Date: Wed, 19 Mar 2025 18:24:56 -0400
Subject: [PATCH 67/68] MADS: Steering Mode on Brake Pedal Press (#687)
* reimplement
* bump opendbc
* Apply suggestions from code review
Co-authored-by: DevTekVE
* update toggle positions
* translations from bot later
---------
Co-authored-by: DevTekVE
---
common/params_keys.h | 2 +-
opendbc_repo | 2 +-
selfdrive/pandad/pandad.cc | 4 ++--
.../settings/sunnypilot/mads_settings.cc | 18 +++++++++-------
.../settings/sunnypilot/mads_settings.h | 2 +-
sunnypilot/mads/helpers.py | 21 ++++++++++++++++---
sunnypilot/mads/mads.py | 13 +++++++++---
system/manager/manager.py | 2 +-
8 files changed, 44 insertions(+), 20 deletions(-)
diff --git a/common/params_keys.h b/common/params_keys.h
index 6bcf897c3e..d88189d432 100644
--- a/common/params_keys.h
+++ b/common/params_keys.h
@@ -132,7 +132,7 @@ inline static std::unordered_map keys = {
// MADS params
{"Mads", PERSISTENT | BACKUP},
{"MadsMainCruiseAllowed", PERSISTENT | BACKUP},
- {"MadsPauseLateralOnBrake", PERSISTENT | BACKUP},
+ {"MadsSteeringMode", PERSISTENT | BACKUP},
{"MadsUnifiedEngagementMode", PERSISTENT | BACKUP},
// Model Manager params
diff --git a/opendbc_repo b/opendbc_repo
index c00137782e..a26ea1e3ee 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit c00137782e6931ec09c109777199ad86d4955955
+Subproject commit a26ea1e3ee982a4b2c377f5f75fdde63cba6a007
diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc
index 3025d0091b..c0ff2db399 100644
--- a/selfdrive/pandad/pandad.cc
+++ b/selfdrive/pandad/pandad.cc
@@ -41,7 +41,7 @@
#define CUTOFF_IL 400
#define SATURATE_IL 1000
-#define ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE 2048
+#define ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE 2048
ExitHandler do_exit;
@@ -57,7 +57,7 @@ bool check_all_connected(const std::vector &pandas) {
bool process_mads_heartbeat(SubMaster *sm) {
const int &alt_exp = (*sm)["carParams"].getCarParams().getAlternativeExperience();
- const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE) != 0;
+ const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE) != 0;
const auto &mads = (*sm)["selfdriveStateSP"].getSelfdriveStateSP().getMads();
const bool heartbeat_type = disengage_lateral_on_brake ? mads.getActive() : mads.getEnabled();
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
index e6e18f262e..d178c59b07 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc
@@ -39,17 +39,19 @@ MadsSettings::MadsSettings(QWidget *parent) : QWidget(parent) {
"");
list->addItem(madsUnifiedEngagementModeToggle);
- // Pause Lateral On Brake
- std::vector lateral_on_brake_texts{tr("Remain Active"), tr("Pause Steering")};
- madsPauseLateralOnBrake = new ButtonParamControl(
- "MadsPauseLateralOnBrake",
- tr("Steering Mode After Braking"),
+ // Steering Mode On Brake
+ std::vector lateral_on_brake_texts{tr("Remain Active"), tr("Pause Steering"), tr("Disengage")};
+ madsSteeringMode = new ButtonParamControl(
+ "MadsSteeringMode",
+ tr("Steering Mode on Brake Pedal"),
tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.\n\n"
- "Remain Active: ALC will remain active even after the brake pedal is pressed.\nPause Steering: ALC will be paused after the brake pedal is manually pressed."),
+ "Remain Active: ALC will remain active even after the brake pedal is pressed.\n"
+ "Pause Steering: ALC will be paused when the brake pedal is manually pressed."),
+ "Disengage: ALC will be disengaged after the brake pedal is pressed.\n"
"",
lateral_on_brake_texts,
500);
- list->addItem(madsPauseLateralOnBrake);
+ list->addItem(madsSteeringMode);
QObject::connect(uiState(), &UIState::offroadTransition, this, &MadsSettings::updateToggles);
@@ -61,7 +63,7 @@ void MadsSettings::showEvent(QShowEvent *event) {
}
void MadsSettings::updateToggles(bool _offroad) {
- madsPauseLateralOnBrake->setEnabled(_offroad);
+ madsSteeringMode->setEnabled(_offroad);
offroad = _offroad;
}
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
index fccb4cbb84..312c4f0db0 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.h
@@ -32,5 +32,5 @@ private:
ParamControl *madsMainCruiseToggle;
ParamControl *madsUnifiedEngagementModeToggle;
- ButtonParamControl *madsPauseLateralOnBrake;
+ ButtonParamControl *madsSteeringMode;
};
diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py
index 831a75699e..36a7380e79 100644
--- a/sunnypilot/mads/helpers.py
+++ b/sunnypilot/mads/helpers.py
@@ -11,15 +11,30 @@ from opendbc.safety import ALTERNATIVE_EXPERIENCE
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP, HyundaiSafetyFlagsSP
+class MadsSteeringModeOnBrake:
+ REMAIN_ACTIVE = 0
+ PAUSE = 1
+ DISENGAGE = 2
+
+
+def read_steering_mode_param(params: Params):
+ try:
+ return int(params.get("MadsSteeringMode"))
+ except (ValueError, TypeError):
+ return f"{MadsSteeringModeOnBrake.REMAIN_ACTIVE}"
+
+
def set_alternative_experience(CP: structs.CarParams, params: Params):
enabled = params.get_bool("Mads")
- pause_lateral_on_brake = params.get_bool("MadsPauseLateralOnBrake")
+ steering_mode = read_steering_mode_param(params)
if enabled:
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS
- if pause_lateral_on_brake:
- CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISENGAGE_LATERAL_ON_BRAKE
+ if steering_mode == MadsSteeringModeOnBrake.DISENGAGE:
+ CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_DISENGAGE_LATERAL_ON_BRAKE
+ elif steering_mode == MadsSteeringModeOnBrake.PAUSE:
+ CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_PAUSE_LATERAL_ON_BRAKE
def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params):
diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py
index 632ca7f2a4..384e48b3fa 100644
--- a/sunnypilot/mads/mads.py
+++ b/sunnypilot/mads/mads.py
@@ -8,6 +8,7 @@ See the LICENSE.md file in the root directory for more details.
from cereal import car, log, custom
from opendbc.car.hyundai.values import HyundaiFlags
+from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake, read_steering_mode_param
from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
@@ -41,7 +42,7 @@ class ModularAssistiveDrivingSystem:
# read params on init
self.enabled_toggle = self.params.get_bool("Mads")
self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed")
- self.pause_lateral_on_brake_toggle = self.params.get_bool("MadsPauseLateralOnBrake")
+ self.steering_mode_on_brake = read_steering_mode_param(self.params)
self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode")
def read_params(self):
@@ -83,11 +84,11 @@ class ModularAssistiveDrivingSystem:
replace_event(EventName.parkBrake, EventNameSP.silentParkBrake)
transition_paused_state()
- if self.pause_lateral_on_brake_toggle:
+ if self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE:
if CS.brakePressed:
transition_paused_state()
- if not (self.pause_lateral_on_brake_toggle and CS.brakePressed) and \
+ if not (self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE and CS.brakePressed) and \
not self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT):
if self.state_machine.state == State.paused:
self.events_sp.add(EventNameSP.silentLkasEnable)
@@ -123,6 +124,12 @@ class ModularAssistiveDrivingSystem:
if self.selfdrive.CS_prev.cruiseState.available:
self.events_sp.add(EventNameSP.lkasDisable)
+ if self.steering_mode_on_brake == MadsSteeringModeOnBrake.DISENGAGE:
+ # Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0
+ if (CS.brakePressed and (not self.selfdrive.CS_prev.brakePressed or not CS.standstill)) or \
+ (CS.regenBraking and (not self.selfdrive.CS_prev.regenBraking or not CS.standstill)):
+ self.events_sp.add(EventNameSP.lkasDisable)
+
self.events.remove(EventName.pcmDisable)
self.events.remove(EventName.buttonCancel)
self.events.remove(EventName.pedalPressed)
diff --git a/system/manager/manager.py b/system/manager/manager.py
index 499ed6474b..d4595ec327 100755
--- a/system/manager/manager.py
+++ b/system/manager/manager.py
@@ -46,7 +46,7 @@ def manager_init() -> None:
("DynamicExperimentalControl", "0"),
("Mads", "1"),
("MadsMainCruiseAllowed", "1"),
- ("MadsPauseLateralOnBrake", "0"),
+ ("MadsSteeringMode", "0"),
("MadsUnifiedEngagementMode", "1"),
("ModelManager_LastSyncTime", "0"),
("ModelManager_ModelsCache", "")
From 47c8ac24316f3ba436bae4c0c4cff176584db12e Mon Sep 17 00:00:00 2001
From: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com>
Date: Wed, 19 Mar 2025 16:16:54 -0700
Subject: [PATCH 68/68] ui: Developer Panel Fixes (#688)
* Add error log to developer panel
* Add lang files
* Refactor GitHub runner control to local variable
Replaced the `enableGithubRunner` member variable with a local variable, as it is not used elsewhere in the class. Simplified visibility management for developer panel toggles in release branches.
* Modifying visibility rules for developer panel controls
This commit modifies the visibility settings for controls within the developer panel such as `enableGithubRunner`, `errorLogBtn`, `joystickToggle` and `longManeuverToggle`. The visibility of these controls is now dependent on whether the software is running in release mode. This update ensures that certain settings are hidden from the end user in the release version, enhancing the user interface by simplifying it and preventing potential user confusion.
* Translation files
* we already handle this in stock
---------
Co-authored-by: DevTekVE
Co-authored-by: Jason Wen
---
selfdrive/ui/qt/offroad/developer_panel.cc | 4 +++-
selfdrive/ui/qt/offroad/developer_panel.h | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc
index b918a9353d..be1c665762 100644
--- a/selfdrive/ui/qt/offroad/developer_panel.cc
+++ b/selfdrive/ui/qt/offroad/developer_panel.cc
@@ -57,7 +57,7 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) {
});
addItem(hyundaiRadarTracksToggle);
- auto enableGithubRunner = new ParamControl("EnableGithubRunner", tr("Enable GitHub runner service"), tr("Enables or disables the github runner service."), "");
+ enableGithubRunner = new ParamControl("EnableGithubRunner", tr("Enable GitHub runner service"), tr("Enables or disables the github runner service."), "");
addItem(enableGithubRunner);
// error log button
@@ -121,7 +121,9 @@ void DeveloperPanel::updateToggles(bool _offroad) {
experimentalLongitudinalToggle->refresh();
// Handle specific controls visibility for release branches
+ enableGithubRunner->setVisible(!is_release);
errorLogBtn->setVisible(!is_release);
+ joystickToggle->setVisible(!is_release);
offroad = _offroad;
}
diff --git a/selfdrive/ui/qt/offroad/developer_panel.h b/selfdrive/ui/qt/offroad/developer_panel.h
index c1783444ea..591e5f0fdf 100644
--- a/selfdrive/ui/qt/offroad/developer_panel.h
+++ b/selfdrive/ui/qt/offroad/developer_panel.h
@@ -20,6 +20,7 @@ private:
ParamControl* longManeuverToggle;
ParamControl* experimentalLongitudinalToggle;
ParamControl* hyundaiRadarTracksToggle;
+ ParamControl* enableGithubRunner;
bool is_release;
bool offroad = false;