From a1d6a062a951419d41e1837c2a312c9c490bea69 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Fri, 12 Sep 2025 12:44:11 -0700 Subject: [PATCH 001/341] add PR ref to new driving model in RELEASES.md --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 189aa7ad54..7c0b967abd 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,6 @@ Version 0.10.1 (2025-09-08) ======================== -* New driving model +* New driving model #36087 * World Model: removed global localization inputs * World Model: 2x the number of parameters * World Model: trained on 4x the number of segments From 3ca9f351a047f5a7fa93bc8a5d50d42301beca31 Mon Sep 17 00:00:00 2001 From: Armand du Parc Locmaria Date: Fri, 12 Sep 2025 12:45:52 -0700 Subject: [PATCH 002/341] =?UTF-8?q?nevada=20model=20=F0=9F=8C=B5=20(#36114?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cd29ffcf-01dd-4f1c-8808-dc197c174f1d --- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 7b87846748..cb5a6b3a47 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebb38a934d6472c061cc6010f46d9720ca132d631a47e585a893bdd41ade2419 -size 12343535 +oid sha256:5b7c686e13637733ec432d774ff5b665c4e4663982ff03deeefd6f6ffd511741 +size 12343531 From 42d9bd05162505b3975ab3eaec54d1b658db27e2 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Fri, 12 Sep 2025 11:36:50 -1000 Subject: [PATCH 003/341] jotpluggler: sync x axes and autofit y axis (#36143) * sync x axes of all timeseries plots * always autofit y-axis * fix typing --- tools/jotpluggler/pluggle.py | 31 ++++++++++++++++++ tools/jotpluggler/views.py | 63 +++++++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index 582a44454e..57ba2a245f 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -65,6 +65,10 @@ class PlaybackManager: self.current_time_s = 0.0 self.duration_s = 0.0 + self.x_axis_bounds = (0.0, 0.0) # (min_time, max_time) + self.x_axis_observers = [] # callbacks for x-axis changes + self._updating_x_axis = False + def set_route_duration(self, duration: float): self.duration_s = duration self.seek(min(self.current_time_s, duration)) @@ -87,6 +91,33 @@ class PlaybackManager: dpg.configure_item("play_pause_button", texture_tag="play_texture") return self.current_time_s + def set_x_axis_bounds(self, min_time: float, max_time: float, source_panel=None): + if self._updating_x_axis: + return + + new_bounds = (min_time, max_time) + if new_bounds == self.x_axis_bounds: + return + + self.x_axis_bounds = new_bounds + self._updating_x_axis = True # prevent recursive updates + + try: + for callback in self.x_axis_observers: + try: + callback(min_time, max_time, source_panel) + except Exception as e: + print(f"Error in x-axis sync callback: {e}") + finally: + self._updating_x_axis = False + + def add_x_axis_observer(self, callback): + if callback not in self.x_axis_observers: + self.x_axis_observers.append(callback) + + def remove_x_axis_observer(self, callback): + if callback in self.x_axis_observers: + self.x_axis_observers.remove(callback) class MainController: def __init__(self, scale: float = 1.0): diff --git a/tools/jotpluggler/views.py b/tools/jotpluggler/views.py index 4af9a102ac..f3da6e3e6f 100644 --- a/tools/jotpluggler/views.py +++ b/tools/jotpluggler/views.py @@ -51,9 +51,11 @@ class TimeSeriesPanel(ViewPanel): self._update_lock = threading.RLock() self._results_deque: deque[tuple[str, list, list]] = deque() self._new_data = False + self._last_x_limits = (0.0, 0.0) def create_ui(self, parent_tag: str): self.data_manager.add_observer(self.on_data_loaded) + self.playback_manager.add_x_axis_observer(self._on_x_axis_sync) with dpg.plot(height=-1, width=-1, tag=self.plot_tag, parent=parent_tag, drop_callback=self._on_series_drop, payload_type="TIMESERIES_PAYLOAD"): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, no_label=True, tag=self.x_axis_tag) @@ -70,8 +72,20 @@ class TimeSeriesPanel(ViewPanel): if not self._ui_created: return + current_limits = dpg.get_axis_limits(self.x_axis_tag) + # downsample if plot zoom changed significantly + plot_duration = current_limits[1] - current_limits[0] + if plot_duration > self._last_plot_duration * 2 or plot_duration < self._last_plot_duration * 0.5: + self._downsample_all_series(plot_duration) + # sync x-axis if changed by user + if self._last_x_limits != current_limits: + self.playback_manager.set_x_axis_bounds(current_limits[0], current_limits[1], source_panel=self) + self._last_x_limits = current_limits + self._fit_y_axis(current_limits[0], current_limits[1]) + if self._new_data: # handle new data in main thread self._new_data = False + dpg.set_axis_limits_constraints(self.x_axis_tag, -10, (self.playback_manager.duration_s + 10)) for series_path in list(self._series_data.keys()): self.add_series(series_path, update=True) @@ -96,10 +110,50 @@ class TimeSeriesPanel(ViewPanel): if dpg.does_item_exist(series_tag): dpg.configure_item(series_tag, label=f"{series_path}: {formatted_value}") - # downsample if plot zoom changed significantly - plot_duration = dpg.get_axis_limits(self.x_axis_tag)[1] - dpg.get_axis_limits(self.x_axis_tag)[0] - if plot_duration > self._last_plot_duration * 2 or plot_duration < self._last_plot_duration * 0.5: - self._downsample_all_series(plot_duration) + def _on_x_axis_sync(self, min_time: float, max_time: float, source_panel): + with self._update_lock: + if source_panel == self or not self._ui_created: + return + dpg.set_axis_limits(self.x_axis_tag, min_time, max_time) + dpg.render_dearpygui_frame() + dpg.set_axis_limits_auto(self.x_axis_tag) + self._last_x_limits = (min_time, max_time) + self._fit_y_axis(min_time, max_time) + + def _fit_y_axis(self, x_min: float, x_max: float): + if not self._series_data: + dpg.set_axis_limits(self.y_axis_tag, -1, 1) + return + + global_min = float('inf') + global_max = float('-inf') + found_data = False + + for time_array, value_array in self._series_data.values(): + if len(time_array) == 0: + continue + start_idx, end_idx = np.searchsorted(time_array, [x_min, x_max]) + end_idx = min(end_idx, len(time_array) - 1) + if start_idx <= end_idx: + y_slice = value_array[start_idx:end_idx + 1] + series_min, series_max = np.min(y_slice), np.max(y_slice) + global_min = min(global_min, series_min) + global_max = max(global_max, series_max) + found_data = True + + if not found_data: + dpg.set_axis_limits(self.y_axis_tag, -1, 1) + return + + if global_min == global_max: + padding = max(abs(global_min) * 0.1, 1.0) + y_min, y_max = global_min - padding, global_max + padding + else: + range_size = global_max - global_min + padding = range_size * 0.1 + y_min, y_max = global_min - padding, global_max + padding + + dpg.set_axis_limits(self.y_axis_tag, y_min, y_max) def _downsample_all_series(self, plot_duration): plot_width = dpg.get_item_rect_size(self.plot_tag)[0] @@ -145,6 +199,7 @@ class TimeSeriesPanel(ViewPanel): def destroy_ui(self): with self._update_lock: self.data_manager.remove_observer(self.on_data_loaded) + self.playback_manager.remove_x_axis_observer(self._on_x_axis_sync) if dpg.does_item_exist(self.plot_tag): dpg.delete_item(self.plot_tag) self._ui_created = False From be379e188b004643edb5d1758eaf4b28e35549b6 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Fri, 12 Sep 2025 11:37:00 -1000 Subject: [PATCH 004/341] jotpluggler: fix off by one error (#36144) fix off by one error sometimes causing missed items in datatree --- tools/jotpluggler/datatree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/jotpluggler/datatree.py b/tools/jotpluggler/datatree.py index 3390fed2e1..eb4e4ef584 100644 --- a/tools/jotpluggler/datatree.py +++ b/tools/jotpluggler/datatree.py @@ -154,7 +154,7 @@ class DataTree: for i, part in enumerate(parts): current_path_prefix = f"{current_path_prefix}/{part}" if current_path_prefix else part - if i < len(parts) - 1: + if i < len(parts): parent_nodes_to_recheck.add(current_node) # for incremental changes from new data if part not in current_node.children: current_node.children[part] = DataTreeNode(name=part, full_path=current_path_prefix, parent=current_node) From cbea5f198fbe338850ba6155fdfe7bf905ae2029 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 12 Sep 2025 15:34:28 -0700 Subject: [PATCH 005/341] op.sh: more robust switch for submodules --- tools/op.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/op.sh b/tools/op.sh index 54ff8e97e9..ae12809eb9 100755 --- a/tools/op.sh +++ b/tools/op.sh @@ -366,9 +366,11 @@ function op_switch() { BRANCH="$1" git config --replace-all remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" + git submodule deinit --all --force git fetch "$REMOTE" "$BRANCH" git checkout -f FETCH_HEAD git checkout -B "$BRANCH" --track "$REMOTE"/"$BRANCH" + git submodule deinit --all --force git reset --hard "${REMOTE}/${BRANCH}" git clean -df git submodule update --init --recursive From 347b23055d03e3af878d7abbd04ca3296f5c1cc2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 12 Sep 2025 18:59:15 -0700 Subject: [PATCH 006/341] minimal ffmpeg build (#36138) * min ffmpeg * remove avfilter * x264 * merge x264 * simpler * pin x264 * mac * rm that * lil more * move includes to lfs * try this * cleanup * larch --------- Co-authored-by: Comma Device --- .gitattributes | 1 + SConstruct | 4 + system/loggerd/SConscript | 4 +- system/loggerd/tests/test_loggerd.py | 14 +++- third_party/.gitignore | 2 + third_party/ffmpeg/Darwin/lib/libavcodec.a | 3 + third_party/ffmpeg/Darwin/lib/libavformat.a | 3 + third_party/ffmpeg/Darwin/lib/libavutil.a | 3 + third_party/ffmpeg/Darwin/lib/libx264.a | 3 + third_party/ffmpeg/build.sh | 83 +++++++++++++++++++ .../ffmpeg/include/libavcodec/ac3_parser.h | 3 + .../ffmpeg/include/libavcodec/adts_parser.h | 3 + .../ffmpeg/include/libavcodec/avcodec.h | 3 + third_party/ffmpeg/include/libavcodec/avdct.h | 3 + third_party/ffmpeg/include/libavcodec/avfft.h | 3 + third_party/ffmpeg/include/libavcodec/bsf.h | 3 + third_party/ffmpeg/include/libavcodec/codec.h | 3 + .../ffmpeg/include/libavcodec/codec_desc.h | 3 + .../ffmpeg/include/libavcodec/codec_id.h | 3 + .../ffmpeg/include/libavcodec/codec_par.h | 3 + .../ffmpeg/include/libavcodec/d3d11va.h | 3 + third_party/ffmpeg/include/libavcodec/defs.h | 3 + third_party/ffmpeg/include/libavcodec/dirac.h | 3 + .../ffmpeg/include/libavcodec/dv_profile.h | 3 + third_party/ffmpeg/include/libavcodec/dxva2.h | 3 + third_party/ffmpeg/include/libavcodec/jni.h | 3 + .../ffmpeg/include/libavcodec/mediacodec.h | 3 + .../ffmpeg/include/libavcodec/packet.h | 3 + third_party/ffmpeg/include/libavcodec/qsv.h | 3 + third_party/ffmpeg/include/libavcodec/vdpau.h | 3 + .../ffmpeg/include/libavcodec/version.h | 3 + .../ffmpeg/include/libavcodec/version_major.h | 3 + .../ffmpeg/include/libavcodec/videotoolbox.h | 3 + .../ffmpeg/include/libavcodec/vorbis_parser.h | 3 + third_party/ffmpeg/include/libavcodec/xvmc.h | 3 + .../ffmpeg/include/libavformat/avformat.h | 3 + third_party/ffmpeg/include/libavformat/avio.h | 3 + .../ffmpeg/include/libavformat/version.h | 3 + .../include/libavformat/version_major.h | 3 + .../ffmpeg/include/libavutil/adler32.h | 3 + third_party/ffmpeg/include/libavutil/aes.h | 3 + .../ffmpeg/include/libavutil/aes_ctr.h | 3 + .../libavutil/ambient_viewing_environment.h | 3 + .../ffmpeg/include/libavutil/attributes.h | 3 + .../ffmpeg/include/libavutil/audio_fifo.h | 3 + .../ffmpeg/include/libavutil/avassert.h | 3 + .../ffmpeg/include/libavutil/avconfig.h | 3 + .../ffmpeg/include/libavutil/avstring.h | 3 + third_party/ffmpeg/include/libavutil/avutil.h | 3 + third_party/ffmpeg/include/libavutil/base64.h | 3 + .../ffmpeg/include/libavutil/blowfish.h | 3 + third_party/ffmpeg/include/libavutil/bprint.h | 3 + third_party/ffmpeg/include/libavutil/bswap.h | 3 + third_party/ffmpeg/include/libavutil/buffer.h | 3 + .../ffmpeg/include/libavutil/camellia.h | 3 + third_party/ffmpeg/include/libavutil/cast5.h | 3 + .../ffmpeg/include/libavutil/channel_layout.h | 3 + third_party/ffmpeg/include/libavutil/common.h | 3 + third_party/ffmpeg/include/libavutil/cpu.h | 3 + third_party/ffmpeg/include/libavutil/crc.h | 3 + third_party/ffmpeg/include/libavutil/csp.h | 3 + third_party/ffmpeg/include/libavutil/des.h | 3 + .../ffmpeg/include/libavutil/detection_bbox.h | 3 + third_party/ffmpeg/include/libavutil/dict.h | 3 + .../ffmpeg/include/libavutil/display.h | 3 + .../ffmpeg/include/libavutil/dovi_meta.h | 3 + .../ffmpeg/include/libavutil/downmix_info.h | 3 + .../include/libavutil/encryption_info.h | 3 + third_party/ffmpeg/include/libavutil/error.h | 3 + third_party/ffmpeg/include/libavutil/eval.h | 3 + .../ffmpeg/include/libavutil/executor.h | 3 + .../ffmpeg/include/libavutil/ffversion.h | 3 + third_party/ffmpeg/include/libavutil/fifo.h | 3 + third_party/ffmpeg/include/libavutil/file.h | 3 + .../include/libavutil/film_grain_params.h | 3 + third_party/ffmpeg/include/libavutil/frame.h | 3 + third_party/ffmpeg/include/libavutil/hash.h | 3 + .../include/libavutil/hdr_dynamic_metadata.h | 3 + .../libavutil/hdr_dynamic_vivid_metadata.h | 3 + third_party/ffmpeg/include/libavutil/hmac.h | 3 + .../ffmpeg/include/libavutil/hwcontext.h | 3 + .../ffmpeg/include/libavutil/hwcontext_cuda.h | 3 + .../include/libavutil/hwcontext_d3d11va.h | 3 + .../ffmpeg/include/libavutil/hwcontext_drm.h | 3 + .../include/libavutil/hwcontext_dxva2.h | 3 + .../include/libavutil/hwcontext_mediacodec.h | 3 + .../include/libavutil/hwcontext_opencl.h | 3 + .../ffmpeg/include/libavutil/hwcontext_qsv.h | 3 + .../include/libavutil/hwcontext_vaapi.h | 3 + .../include/libavutil/hwcontext_vdpau.h | 3 + .../libavutil/hwcontext_videotoolbox.h | 3 + .../include/libavutil/hwcontext_vulkan.h | 3 + .../ffmpeg/include/libavutil/imgutils.h | 3 + .../ffmpeg/include/libavutil/intfloat.h | 3 + .../ffmpeg/include/libavutil/intreadwrite.h | 3 + third_party/ffmpeg/include/libavutil/lfg.h | 3 + third_party/ffmpeg/include/libavutil/log.h | 3 + third_party/ffmpeg/include/libavutil/lzo.h | 3 + third_party/ffmpeg/include/libavutil/macros.h | 3 + .../libavutil/mastering_display_metadata.h | 3 + .../ffmpeg/include/libavutil/mathematics.h | 3 + third_party/ffmpeg/include/libavutil/md5.h | 3 + third_party/ffmpeg/include/libavutil/mem.h | 3 + .../ffmpeg/include/libavutil/motion_vector.h | 3 + .../ffmpeg/include/libavutil/murmur3.h | 3 + third_party/ffmpeg/include/libavutil/opt.h | 3 + .../ffmpeg/include/libavutil/parseutils.h | 3 + .../ffmpeg/include/libavutil/pixdesc.h | 3 + .../ffmpeg/include/libavutil/pixelutils.h | 3 + third_party/ffmpeg/include/libavutil/pixfmt.h | 3 + .../ffmpeg/include/libavutil/random_seed.h | 3 + .../ffmpeg/include/libavutil/rational.h | 3 + third_party/ffmpeg/include/libavutil/rc4.h | 3 + .../ffmpeg/include/libavutil/replaygain.h | 3 + third_party/ffmpeg/include/libavutil/ripemd.h | 3 + .../ffmpeg/include/libavutil/samplefmt.h | 3 + third_party/ffmpeg/include/libavutil/sha.h | 3 + third_party/ffmpeg/include/libavutil/sha512.h | 3 + .../ffmpeg/include/libavutil/spherical.h | 3 + .../ffmpeg/include/libavutil/stereo3d.h | 3 + third_party/ffmpeg/include/libavutil/tea.h | 3 + .../ffmpeg/include/libavutil/threadmessage.h | 3 + third_party/ffmpeg/include/libavutil/time.h | 3 + .../ffmpeg/include/libavutil/timecode.h | 3 + .../ffmpeg/include/libavutil/timestamp.h | 3 + third_party/ffmpeg/include/libavutil/tree.h | 3 + .../ffmpeg/include/libavutil/twofish.h | 3 + third_party/ffmpeg/include/libavutil/tx.h | 3 + third_party/ffmpeg/include/libavutil/uuid.h | 3 + .../ffmpeg/include/libavutil/version.h | 3 + .../include/libavutil/video_enc_params.h | 3 + .../ffmpeg/include/libavutil/video_hint.h | 3 + third_party/ffmpeg/include/libavutil/xtea.h | 3 + third_party/ffmpeg/include/x264.h | 3 + third_party/ffmpeg/include/x264_config.h | 3 + third_party/ffmpeg/larch64/lib/libavcodec.a | 3 + third_party/ffmpeg/larch64/lib/libavformat.a | 3 + third_party/ffmpeg/larch64/lib/libavutil.a | 3 + third_party/ffmpeg/larch64/lib/libx264.a | 3 + third_party/ffmpeg/x86_64/lib/libavcodec.a | 3 + third_party/ffmpeg/x86_64/lib/libavformat.a | 3 + third_party/ffmpeg/x86_64/lib/libavutil.a | 3 + third_party/ffmpeg/x86_64/lib/libx264.a | 3 + tools/cabana/SConscript | 2 +- tools/install_ubuntu_dependencies.sh | 6 -- tools/mac_setup.sh | 1 - tools/replay/SConscript | 2 +- 147 files changed, 517 insertions(+), 13 deletions(-) create mode 100644 third_party/ffmpeg/Darwin/lib/libavcodec.a create mode 100644 third_party/ffmpeg/Darwin/lib/libavformat.a create mode 100644 third_party/ffmpeg/Darwin/lib/libavutil.a create mode 100644 third_party/ffmpeg/Darwin/lib/libx264.a create mode 100755 third_party/ffmpeg/build.sh create mode 100644 third_party/ffmpeg/include/libavcodec/ac3_parser.h create mode 100644 third_party/ffmpeg/include/libavcodec/adts_parser.h create mode 100644 third_party/ffmpeg/include/libavcodec/avcodec.h create mode 100644 third_party/ffmpeg/include/libavcodec/avdct.h create mode 100644 third_party/ffmpeg/include/libavcodec/avfft.h create mode 100644 third_party/ffmpeg/include/libavcodec/bsf.h create mode 100644 third_party/ffmpeg/include/libavcodec/codec.h create mode 100644 third_party/ffmpeg/include/libavcodec/codec_desc.h create mode 100644 third_party/ffmpeg/include/libavcodec/codec_id.h create mode 100644 third_party/ffmpeg/include/libavcodec/codec_par.h create mode 100644 third_party/ffmpeg/include/libavcodec/d3d11va.h create mode 100644 third_party/ffmpeg/include/libavcodec/defs.h create mode 100644 third_party/ffmpeg/include/libavcodec/dirac.h create mode 100644 third_party/ffmpeg/include/libavcodec/dv_profile.h create mode 100644 third_party/ffmpeg/include/libavcodec/dxva2.h create mode 100644 third_party/ffmpeg/include/libavcodec/jni.h create mode 100644 third_party/ffmpeg/include/libavcodec/mediacodec.h create mode 100644 third_party/ffmpeg/include/libavcodec/packet.h create mode 100644 third_party/ffmpeg/include/libavcodec/qsv.h create mode 100644 third_party/ffmpeg/include/libavcodec/vdpau.h create mode 100644 third_party/ffmpeg/include/libavcodec/version.h create mode 100644 third_party/ffmpeg/include/libavcodec/version_major.h create mode 100644 third_party/ffmpeg/include/libavcodec/videotoolbox.h create mode 100644 third_party/ffmpeg/include/libavcodec/vorbis_parser.h create mode 100644 third_party/ffmpeg/include/libavcodec/xvmc.h create mode 100644 third_party/ffmpeg/include/libavformat/avformat.h create mode 100644 third_party/ffmpeg/include/libavformat/avio.h create mode 100644 third_party/ffmpeg/include/libavformat/version.h create mode 100644 third_party/ffmpeg/include/libavformat/version_major.h create mode 100644 third_party/ffmpeg/include/libavutil/adler32.h create mode 100644 third_party/ffmpeg/include/libavutil/aes.h create mode 100644 third_party/ffmpeg/include/libavutil/aes_ctr.h create mode 100644 third_party/ffmpeg/include/libavutil/ambient_viewing_environment.h create mode 100644 third_party/ffmpeg/include/libavutil/attributes.h create mode 100644 third_party/ffmpeg/include/libavutil/audio_fifo.h create mode 100644 third_party/ffmpeg/include/libavutil/avassert.h create mode 100644 third_party/ffmpeg/include/libavutil/avconfig.h create mode 100644 third_party/ffmpeg/include/libavutil/avstring.h create mode 100644 third_party/ffmpeg/include/libavutil/avutil.h create mode 100644 third_party/ffmpeg/include/libavutil/base64.h create mode 100644 third_party/ffmpeg/include/libavutil/blowfish.h create mode 100644 third_party/ffmpeg/include/libavutil/bprint.h create mode 100644 third_party/ffmpeg/include/libavutil/bswap.h create mode 100644 third_party/ffmpeg/include/libavutil/buffer.h create mode 100644 third_party/ffmpeg/include/libavutil/camellia.h create mode 100644 third_party/ffmpeg/include/libavutil/cast5.h create mode 100644 third_party/ffmpeg/include/libavutil/channel_layout.h create mode 100644 third_party/ffmpeg/include/libavutil/common.h create mode 100644 third_party/ffmpeg/include/libavutil/cpu.h create mode 100644 third_party/ffmpeg/include/libavutil/crc.h create mode 100644 third_party/ffmpeg/include/libavutil/csp.h create mode 100644 third_party/ffmpeg/include/libavutil/des.h create mode 100644 third_party/ffmpeg/include/libavutil/detection_bbox.h create mode 100644 third_party/ffmpeg/include/libavutil/dict.h create mode 100644 third_party/ffmpeg/include/libavutil/display.h create mode 100644 third_party/ffmpeg/include/libavutil/dovi_meta.h create mode 100644 third_party/ffmpeg/include/libavutil/downmix_info.h create mode 100644 third_party/ffmpeg/include/libavutil/encryption_info.h create mode 100644 third_party/ffmpeg/include/libavutil/error.h create mode 100644 third_party/ffmpeg/include/libavutil/eval.h create mode 100644 third_party/ffmpeg/include/libavutil/executor.h create mode 100644 third_party/ffmpeg/include/libavutil/ffversion.h create mode 100644 third_party/ffmpeg/include/libavutil/fifo.h create mode 100644 third_party/ffmpeg/include/libavutil/file.h create mode 100644 third_party/ffmpeg/include/libavutil/film_grain_params.h create mode 100644 third_party/ffmpeg/include/libavutil/frame.h create mode 100644 third_party/ffmpeg/include/libavutil/hash.h create mode 100644 third_party/ffmpeg/include/libavutil/hdr_dynamic_metadata.h create mode 100644 third_party/ffmpeg/include/libavutil/hdr_dynamic_vivid_metadata.h create mode 100644 third_party/ffmpeg/include/libavutil/hmac.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_cuda.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_d3d11va.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_drm.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_dxva2.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_mediacodec.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_opencl.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_qsv.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_vaapi.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_vdpau.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_videotoolbox.h create mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_vulkan.h create mode 100644 third_party/ffmpeg/include/libavutil/imgutils.h create mode 100644 third_party/ffmpeg/include/libavutil/intfloat.h create mode 100644 third_party/ffmpeg/include/libavutil/intreadwrite.h create mode 100644 third_party/ffmpeg/include/libavutil/lfg.h create mode 100644 third_party/ffmpeg/include/libavutil/log.h create mode 100644 third_party/ffmpeg/include/libavutil/lzo.h create mode 100644 third_party/ffmpeg/include/libavutil/macros.h create mode 100644 third_party/ffmpeg/include/libavutil/mastering_display_metadata.h create mode 100644 third_party/ffmpeg/include/libavutil/mathematics.h create mode 100644 third_party/ffmpeg/include/libavutil/md5.h create mode 100644 third_party/ffmpeg/include/libavutil/mem.h create mode 100644 third_party/ffmpeg/include/libavutil/motion_vector.h create mode 100644 third_party/ffmpeg/include/libavutil/murmur3.h create mode 100644 third_party/ffmpeg/include/libavutil/opt.h create mode 100644 third_party/ffmpeg/include/libavutil/parseutils.h create mode 100644 third_party/ffmpeg/include/libavutil/pixdesc.h create mode 100644 third_party/ffmpeg/include/libavutil/pixelutils.h create mode 100644 third_party/ffmpeg/include/libavutil/pixfmt.h create mode 100644 third_party/ffmpeg/include/libavutil/random_seed.h create mode 100644 third_party/ffmpeg/include/libavutil/rational.h create mode 100644 third_party/ffmpeg/include/libavutil/rc4.h create mode 100644 third_party/ffmpeg/include/libavutil/replaygain.h create mode 100644 third_party/ffmpeg/include/libavutil/ripemd.h create mode 100644 third_party/ffmpeg/include/libavutil/samplefmt.h create mode 100644 third_party/ffmpeg/include/libavutil/sha.h create mode 100644 third_party/ffmpeg/include/libavutil/sha512.h create mode 100644 third_party/ffmpeg/include/libavutil/spherical.h create mode 100644 third_party/ffmpeg/include/libavutil/stereo3d.h create mode 100644 third_party/ffmpeg/include/libavutil/tea.h create mode 100644 third_party/ffmpeg/include/libavutil/threadmessage.h create mode 100644 third_party/ffmpeg/include/libavutil/time.h create mode 100644 third_party/ffmpeg/include/libavutil/timecode.h create mode 100644 third_party/ffmpeg/include/libavutil/timestamp.h create mode 100644 third_party/ffmpeg/include/libavutil/tree.h create mode 100644 third_party/ffmpeg/include/libavutil/twofish.h create mode 100644 third_party/ffmpeg/include/libavutil/tx.h create mode 100644 third_party/ffmpeg/include/libavutil/uuid.h create mode 100644 third_party/ffmpeg/include/libavutil/version.h create mode 100644 third_party/ffmpeg/include/libavutil/video_enc_params.h create mode 100644 third_party/ffmpeg/include/libavutil/video_hint.h create mode 100644 third_party/ffmpeg/include/libavutil/xtea.h create mode 100644 third_party/ffmpeg/include/x264.h create mode 100644 third_party/ffmpeg/include/x264_config.h create mode 100644 third_party/ffmpeg/larch64/lib/libavcodec.a create mode 100644 third_party/ffmpeg/larch64/lib/libavformat.a create mode 100644 third_party/ffmpeg/larch64/lib/libavutil.a create mode 100644 third_party/ffmpeg/larch64/lib/libx264.a create mode 100644 third_party/ffmpeg/x86_64/lib/libavcodec.a create mode 100644 third_party/ffmpeg/x86_64/lib/libavformat.a create mode 100644 third_party/ffmpeg/x86_64/lib/libavutil.a create mode 100644 third_party/ffmpeg/x86_64/lib/libx264.a diff --git a/.gitattributes b/.gitattributes index cc1605a132..152dbfe456 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,6 +15,7 @@ third_party/**/*.a filter=lfs diff=lfs merge=lfs -text third_party/**/*.so filter=lfs diff=lfs merge=lfs -text third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text third_party/**/*.dylib filter=lfs diff=lfs merge=lfs -text +third_party/ffmpeg/include/**/*.h filter=lfs diff=lfs merge=lfs -text third_party/acados/*/t_renderer filter=lfs diff=lfs merge=lfs -text third_party/qt5/larch64/bin/lrelease filter=lfs diff=lfs merge=lfs -text third_party/qt5/larch64/bin/lupdate filter=lfs diff=lfs merge=lfs -text diff --git a/SConstruct b/SConstruct index 5b13bd635a..a788b9a290 100644 --- a/SConstruct +++ b/SConstruct @@ -91,6 +91,7 @@ if arch == "larch64": ] libpath = [ + f"#third_party/ffmpeg/{arch}/lib", "/usr/local/lib", "/system/vendor/lib64", f"#third_party/acados/{arch}/lib", @@ -114,6 +115,7 @@ else: libpath = [ f"#third_party/libyuv/{arch}/lib", f"#third_party/acados/{arch}/lib", + f"#third_party/ffmpeg/{arch}/lib", f"{brew_prefix}/lib", f"{brew_prefix}/opt/openssl@3.0/lib", "/System/Library/Frameworks/OpenGL.framework/Libraries", @@ -130,6 +132,7 @@ else: libpath = [ f"#third_party/acados/{arch}/lib", f"#third_party/libyuv/{arch}/lib", + f"#third_party/ffmpeg/{arch}/lib", "/usr/lib", "/usr/local/lib", ] @@ -177,6 +180,7 @@ env = Environment( "#third_party/libyuv/include", "#third_party/json11", "#third_party/linux/include", + "#third_party/ffmpeg/include", "#third_party", "#msgq", ], diff --git a/system/loggerd/SConscript b/system/loggerd/SConscript index cf169f4dc6..c87db4232b 100644 --- a/system/loggerd/SConscript +++ b/system/loggerd/SConscript @@ -1,8 +1,8 @@ Import('env', 'arch', 'messaging', 'common', 'visionipc') libs = [common, messaging, visionipc, - 'avformat', 'avcodec', 'avutil', - 'yuv', 'OpenCL', 'pthread', 'zstd'] + 'yuv', 'OpenCL', 'pthread', 'zstd', + 'avformat', 'avcodec', 'avutil', 'x264'] src = ['logger.cc', 'zstd_writer.cc', 'video_writer.cc', 'encoder/encoder.cc', 'encoder/v4l_encoder.cc', 'encoder/jpeg_encoder.cc'] if arch != "larch64": diff --git a/system/loggerd/tests/test_loggerd.py b/system/loggerd/tests/test_loggerd.py index c6a4b12e63..ae7196684d 100644 --- a/system/loggerd/tests/test_loggerd.py +++ b/system/loggerd/tests/test_loggerd.py @@ -316,8 +316,18 @@ class TestLoggerd: self._publish_camera_and_audio_messages() qcamera_ts_path = os.path.join(self._get_latest_log_dir(), 'qcamera.ts') - ffprobe_cmd = f"ffprobe -i {qcamera_ts_path} -show_streams -select_streams a -loglevel error" - has_audio_stream = subprocess.run(ffprobe_cmd, shell=True, capture_output=True).stdout.strip() != b'' + + # simplest heuristic: look for AAC ADTS syncwords in the TS file + def ts_has_audio_stream(ts_path: str) -> bool: + try: + with open(ts_path, 'rb') as f: + data = f.read() + # ADTS headers typically start with 0xFFF1 or 0xFFF9 + return (b"\xFF\xF1" in data) or (b"\xFF\xF9" in data) + except Exception: + return False + + has_audio_stream = ts_has_audio_stream(qcamera_ts_path) assert has_audio_stream == record_audio raw_audio_in_rlog = any(m.which() == 'rawAudioData' for m in LogReader(os.path.join(self._get_latest_log_dir(), 'rlog.zst'))) diff --git a/third_party/.gitignore b/third_party/.gitignore index 0d20b6487c..0d5b3d2213 100644 --- a/third_party/.gitignore +++ b/third_party/.gitignore @@ -1 +1,3 @@ *.pyc +src/ +build/ diff --git a/third_party/ffmpeg/Darwin/lib/libavcodec.a b/third_party/ffmpeg/Darwin/lib/libavcodec.a new file mode 100644 index 0000000000..a87d3323ce --- /dev/null +++ b/third_party/ffmpeg/Darwin/lib/libavcodec.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2dcb729a6833558fc0e01abe5e728eba2b3c215abc7dece74d5ce1bc1d279e7d +size 2328832 diff --git a/third_party/ffmpeg/Darwin/lib/libavformat.a b/third_party/ffmpeg/Darwin/lib/libavformat.a new file mode 100644 index 0000000000..b7c48f7961 --- /dev/null +++ b/third_party/ffmpeg/Darwin/lib/libavformat.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:825f145f7168226deae199538113f063e6b71a02999b297c75254dfa466b73cb +size 863544 diff --git a/third_party/ffmpeg/Darwin/lib/libavutil.a b/third_party/ffmpeg/Darwin/lib/libavutil.a new file mode 100644 index 0000000000..17985d73b6 --- /dev/null +++ b/third_party/ffmpeg/Darwin/lib/libavutil.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c13c341440029d06a1044479876843bc93615849554dc636e386f31f6776850 +size 789024 diff --git a/third_party/ffmpeg/Darwin/lib/libx264.a b/third_party/ffmpeg/Darwin/lib/libx264.a new file mode 100644 index 0000000000..27375793a9 --- /dev/null +++ b/third_party/ffmpeg/Darwin/lib/libx264.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bedf59051187092bd9f282acb7f22f7885ed41b2f89692e0478252329bd7e4b +size 1941808 diff --git a/third_party/ffmpeg/build.sh b/third_party/ffmpeg/build.sh new file mode 100755 index 0000000000..f8812d6f17 --- /dev/null +++ b/third_party/ffmpeg/build.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +# Detect arch +ARCHNAME="x86_64" +if [ -f /TICI ]; then + ARCHNAME="larch64" +fi +if [[ "$OSTYPE" == "darwin"* ]]; then + ARCHNAME="Darwin" +fi + +VERSION="6.1.1" # LTS +PREFIX="$DIR/$ARCHNAME" +BUILD_DIR="$DIR/build/" + +mkdir -p "$BUILD_DIR" +rm -rf include/ && mkdir -p include/ +rm -rf "$PREFIX" && mkdir -p "$PREFIX" + +# *** build x264 *** +if [[ ! -d "$DIR/src/x264/" ]]; then + # TODO: pin to a commit + git clone --depth=1 --branch "stable" https://code.videolan.org/videolan/x264.git "$DIR/src/x264/" +fi +cd $DIR/src/x264 +git fetch origin b35605ace3ddf7c1a5d67a2eb553f034aef41d55 +git checkout -f FETCH_HEAD +./configure --prefix="$PREFIX" --enable-static --disable-opencl --enable-pic --disable-cli +make -j8 +make install +cp -a "$PREFIX/include/." "$DIR/include/" + +# *** build ffmpeg *** +mkdir -p "$DIR/src" +if [[ ! -d "$DIR/src/ffmpeg-$VERSION" ]]; then + echo "Downloading FFmpeg $VERSION ..." + curl -L "https://ffmpeg.org/releases/ffmpeg-${VERSION}.tar.xz" -o "$DIR/src/ffmpeg-${VERSION}.tar.xz" + tar -C "$DIR/src" -xf "$DIR/src/ffmpeg-${VERSION}.tar.xz" +fi + +cd $BUILD_DIR + +export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" +export EXTRA_CFLAGS="-I$PREFIX/include ${EXTRA_CFLAGS:-}" +export EXTRA_LDFLAGS="-L$PREFIX/lib ${EXTRA_LDFLAGS:-}" +# Configure minimal static FFmpeg for desktop Linux tools +"$DIR/src/ffmpeg-$VERSION/configure" \ + --prefix="$PREFIX" \ + --datadir="$PREFIX" \ + --docdir="$PREFIX" \ + --mandir="$PREFIX" \ + --enable-static --disable-shared \ + --disable-programs --disable-doc --disable-debug \ + --disable-network \ + --disable-avdevice --disable-swscale --disable-swresample --disable-postproc --disable-avfilter \ + --disable-autodetect --disable-iconv \ + --enable-avcodec --enable-avformat --enable-avutil \ + --enable-protocol=file \ + --pkg-config-flags=--static \ + --enable-gpl --enable-libx264 \ + --disable-decoders --enable-decoder=h264,hevc,aac \ + --disable-encoders --enable-encoder=libx264,ffvhuff,aac \ + --disable-demuxers --enable-demuxer=mpegts,hevc,h264,matroska,mov \ + --disable-muxers --enable-muxer=matroska,mpegts \ + --disable-parsers --enable-parser=h264,hevc,aac,vorbis \ + --disable-bsfs \ + --enable-small \ + --extra-cflags="${EXTRA_CFLAGS:-}" \ + --extra-ldflags="${EXTRA_LDFLAGS:-}" + + +make -j$(nproc) +make install +cp -a "$PREFIX/include/." "$DIR/include/" + +# *** cleanup *** +cd $PREFIX +rm -rf share/ doc/ man/ examples/ examples/ include/ lib/pkgconfig/ +rm -f lib/libavfilter* "$DIR/include/libavfilter*" diff --git a/third_party/ffmpeg/include/libavcodec/ac3_parser.h b/third_party/ffmpeg/include/libavcodec/ac3_parser.h new file mode 100644 index 0000000000..c8acedae83 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/ac3_parser.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:200c6d2e96975196e8ba5f5716223dc9dda999d51578dabce2fca93175a05252 +size 1207 diff --git a/third_party/ffmpeg/include/libavcodec/adts_parser.h b/third_party/ffmpeg/include/libavcodec/adts_parser.h new file mode 100644 index 0000000000..031f10320a --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/adts_parser.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d466583a8dc1260b015e588adbe3abd45f3f8ca0e43722f3088b472e80492a15 +size 1354 diff --git a/third_party/ffmpeg/include/libavcodec/avcodec.h b/third_party/ffmpeg/include/libavcodec/avcodec.h new file mode 100644 index 0000000000..82fc7d1bd1 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/avcodec.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc38e85633a2c6c44fc60b319d7e046a6e4206456565aa61240dd93ee2e81188 +size 114251 diff --git a/third_party/ffmpeg/include/libavcodec/avdct.h b/third_party/ffmpeg/include/libavcodec/avdct.h new file mode 100644 index 0000000000..1a57c43007 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/avdct.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c125edc1985ec078f99abb1c9044c4fc76b06a03eb02c026cb968fb9d41fcca +size 2726 diff --git a/third_party/ffmpeg/include/libavcodec/avfft.h b/third_party/ffmpeg/include/libavcodec/avfft.h new file mode 100644 index 0000000000..f54c365c1c --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/avfft.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81bb6c5359f57de53b0e39766591404b632d15e700d313c97ded234d63d4e393 +size 4081 diff --git a/third_party/ffmpeg/include/libavcodec/bsf.h b/third_party/ffmpeg/include/libavcodec/bsf.h new file mode 100644 index 0000000000..54fc91b112 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/bsf.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc26676aa44638fa5cbef953d967e19e2084b46aaa100f0a35ba2d34b302301c +size 11540 diff --git a/third_party/ffmpeg/include/libavcodec/codec.h b/third_party/ffmpeg/include/libavcodec/codec.h new file mode 100644 index 0000000000..b8c3841727 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/codec.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8b75f2e4128ce59db3172e2bdcbf64540424e6242952dc0912002bee5314254 +size 13460 diff --git a/third_party/ffmpeg/include/libavcodec/codec_desc.h b/third_party/ffmpeg/include/libavcodec/codec_desc.h new file mode 100644 index 0000000000..108aa120d9 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/codec_desc.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c1a8d02398cc82c1913487405c483f4d74ec71ed3e666ed7e7570f581c8f479 +size 3974 diff --git a/third_party/ffmpeg/include/libavcodec/codec_id.h b/third_party/ffmpeg/include/libavcodec/codec_id.h new file mode 100644 index 0000000000..72002e6892 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/codec_id.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:754a846797be20c7c1ca30358d2555873df3bb19b6b2c8011c85697440f600df +size 18020 diff --git a/third_party/ffmpeg/include/libavcodec/codec_par.h b/third_party/ffmpeg/include/libavcodec/codec_par.h new file mode 100644 index 0000000000..2c2f42e885 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/codec_par.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f2bef7f23782ac9c0b5769eeb54704103efa5074ca5b657d7f01890795baf08 +size 8065 diff --git a/third_party/ffmpeg/include/libavcodec/d3d11va.h b/third_party/ffmpeg/include/libavcodec/d3d11va.h new file mode 100644 index 0000000000..aa372312b4 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/d3d11va.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74a55a2e3f19ce797e99624a224302f25efa89a115b9bf2e932c8fa179b0cc66 +size 2853 diff --git a/third_party/ffmpeg/include/libavcodec/defs.h b/third_party/ffmpeg/include/libavcodec/defs.h new file mode 100644 index 0000000000..6467ef6639 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/defs.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bb9d7049499d252c75f1fe15fced5022ebb30d1966d47217848236eb021604a +size 12358 diff --git a/third_party/ffmpeg/include/libavcodec/dirac.h b/third_party/ffmpeg/include/libavcodec/dirac.h new file mode 100644 index 0000000000..8bf7c4a0d3 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/dirac.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09fd1f670422ee61713568abd90fdf371b30e6fe35bc6fb8213f1ad32b45cf56 +size 4126 diff --git a/third_party/ffmpeg/include/libavcodec/dv_profile.h b/third_party/ffmpeg/include/libavcodec/dv_profile.h new file mode 100644 index 0000000000..738746f3fc --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/dv_profile.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19f59e0b20ac583de4bfd76d18889d334bf0b6cdf7b5356723a33f3874738466 +size 3694 diff --git a/third_party/ffmpeg/include/libavcodec/dxva2.h b/third_party/ffmpeg/include/libavcodec/dxva2.h new file mode 100644 index 0000000000..aa41fbba83 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/dxva2.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e69dc45a7d5a9206b3bfead10caf3117117005cd532c4fc599d9976637c1b9e3 +size 2361 diff --git a/third_party/ffmpeg/include/libavcodec/jni.h b/third_party/ffmpeg/include/libavcodec/jni.h new file mode 100644 index 0000000000..942690da84 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/jni.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18ca8eae5bce081b4eef1b1f61a62aefed1d4e6e3cde41487c81fb96ee709e51 +size 1650 diff --git a/third_party/ffmpeg/include/libavcodec/mediacodec.h b/third_party/ffmpeg/include/libavcodec/mediacodec.h new file mode 100644 index 0000000000..2afacbd1dc --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/mediacodec.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f64544000dd2f2ec94604d5fcf7c2a6d26d32d085264356084a4b53a7f0c3f0 +size 3570 diff --git a/third_party/ffmpeg/include/libavcodec/packet.h b/third_party/ffmpeg/include/libavcodec/packet.h new file mode 100644 index 0000000000..7c11f362ec --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/packet.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18eb41b9aa9ec55b3ee3932279cc54ead813710741f004c5ea5a1c2957896f59 +size 28678 diff --git a/third_party/ffmpeg/include/libavcodec/qsv.h b/third_party/ffmpeg/include/libavcodec/qsv.h new file mode 100644 index 0000000000..02c4c961f3 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/qsv.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e45780237ab0e9ea9ec11aab9f62dfb8b059138f846632ecacdc024db16dfb1b +size 3844 diff --git a/third_party/ffmpeg/include/libavcodec/vdpau.h b/third_party/ffmpeg/include/libavcodec/vdpau.h new file mode 100644 index 0000000000..6ccba34dbc --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/vdpau.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e0c963348495936556724cfb4d2d35eb4c14007ba72d51be94dd26c83d93fb8 +size 5104 diff --git a/third_party/ffmpeg/include/libavcodec/version.h b/third_party/ffmpeg/include/libavcodec/version.h new file mode 100644 index 0000000000..9f2069af2d --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/version.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6938a71f9eb6b1ef2a201499f47ef1131869b90755932a2ee70a272b2ee0784 +size 1619 diff --git a/third_party/ffmpeg/include/libavcodec/version_major.h b/third_party/ffmpeg/include/libavcodec/version_major.h new file mode 100644 index 0000000000..7781faab45 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/version_major.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9562c933e8eebb020eb143d21c4b2b529e2dc38d2d7365bc13adb8d0ef8227b +size 2494 diff --git a/third_party/ffmpeg/include/libavcodec/videotoolbox.h b/third_party/ffmpeg/include/libavcodec/videotoolbox.h new file mode 100644 index 0000000000..59f788e025 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/videotoolbox.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19b6ba05f4e9f600044f45f1a5dc04d0da9276dc9a1cb5b07826df5cab5012c9 +size 4677 diff --git a/third_party/ffmpeg/include/libavcodec/vorbis_parser.h b/third_party/ffmpeg/include/libavcodec/vorbis_parser.h new file mode 100644 index 0000000000..d578829d44 --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/vorbis_parser.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57077b2e1d28d42636cab0f69e4b92b1ad64ac2eaa2843c270a6afaf308a76ae +size 2285 diff --git a/third_party/ffmpeg/include/libavcodec/xvmc.h b/third_party/ffmpeg/include/libavcodec/xvmc.h new file mode 100644 index 0000000000..abe162a93b --- /dev/null +++ b/third_party/ffmpeg/include/libavcodec/xvmc.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca59c5caaaa32368f7c84c714bbf2ad1cba61ec9ca5b5cb1a14cf6975affe8a2 +size 6136 diff --git a/third_party/ffmpeg/include/libavformat/avformat.h b/third_party/ffmpeg/include/libavformat/avformat.h new file mode 100644 index 0000000000..a4244e9137 --- /dev/null +++ b/third_party/ffmpeg/include/libavformat/avformat.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1ca6edf1d5eab00126fa7320f01c21ed8cea09dc2d931f84a4015b9e50f8d2a +size 110803 diff --git a/third_party/ffmpeg/include/libavformat/avio.h b/third_party/ffmpeg/include/libavformat/avio.h new file mode 100644 index 0000000000..af9666e186 --- /dev/null +++ b/third_party/ffmpeg/include/libavformat/avio.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de6fe4be374481b1ee5ba8722882af7e7698038fd744375de153d1c78f528836 +size 31681 diff --git a/third_party/ffmpeg/include/libavformat/version.h b/third_party/ffmpeg/include/libavformat/version.h new file mode 100644 index 0000000000..07fe55f955 --- /dev/null +++ b/third_party/ffmpeg/include/libavformat/version.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08b64bb52109a07ae3ee1a1dafd12ad336d0a646c23aea075c5024b9437f3b29 +size 1652 diff --git a/third_party/ffmpeg/include/libavformat/version_major.h b/third_party/ffmpeg/include/libavformat/version_major.h new file mode 100644 index 0000000000..0efde349bb --- /dev/null +++ b/third_party/ffmpeg/include/libavformat/version_major.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d440097abe2039c3c831d3b91d315857829d04e462415ee632c9f36321d520d +size 2240 diff --git a/third_party/ffmpeg/include/libavutil/adler32.h b/third_party/ffmpeg/include/libavutil/adler32.h new file mode 100644 index 0000000000..19f01b3441 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/adler32.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f21a861957bf4b1812ed67fdc528890f9cff1bb483facf0f5109e4a51932fe3b +size 1696 diff --git a/third_party/ffmpeg/include/libavutil/aes.h b/third_party/ffmpeg/include/libavutil/aes.h new file mode 100644 index 0000000000..6d81ffa43e --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/aes.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a86ebeaf9ed33548bf0f92359f7047e521d4d80fe6b0ef1c8ef9505e38b6d28 +size 1912 diff --git a/third_party/ffmpeg/include/libavutil/aes_ctr.h b/third_party/ffmpeg/include/libavutil/aes_ctr.h new file mode 100644 index 0000000000..1a156aed36 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/aes_ctr.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbbb94888bfab2ea7141e7e1afa5872de099e7ee57baea17632c3d4fdd2cba3f +size 2443 diff --git a/third_party/ffmpeg/include/libavutil/ambient_viewing_environment.h b/third_party/ffmpeg/include/libavutil/ambient_viewing_environment.h new file mode 100644 index 0000000000..42a94d1941 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/ambient_viewing_environment.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00135de08089f8c711bb113588f80575a1d26e86d50b6dac5928a27a0ff9a8c7 +size 2585 diff --git a/third_party/ffmpeg/include/libavutil/attributes.h b/third_party/ffmpeg/include/libavutil/attributes.h new file mode 100644 index 0000000000..2a47842614 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/attributes.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f51258ba39861e08cbc8d407ec37a8d0fd28669aa7306c1365cb9c675fa1d76 +size 4850 diff --git a/third_party/ffmpeg/include/libavutil/audio_fifo.h b/third_party/ffmpeg/include/libavutil/audio_fifo.h new file mode 100644 index 0000000000..28a3364eee --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/audio_fifo.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c513ca46927346c0673cd9db302afe62dbbf976844f36da0307fd10f7f47bfd +size 5966 diff --git a/third_party/ffmpeg/include/libavutil/avassert.h b/third_party/ffmpeg/include/libavutil/avassert.h new file mode 100644 index 0000000000..40ecca50e9 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/avassert.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39dfe16f0790daa5d6b9b1bad3167c3c9e638c72452e0acdaf2d163c9c75ea40 +size 2408 diff --git a/third_party/ffmpeg/include/libavutil/avconfig.h b/third_party/ffmpeg/include/libavutil/avconfig.h new file mode 100644 index 0000000000..4e1eedcf99 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/avconfig.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:975611ad5eba15212d9e1d5fca9d4fdf0daec6d2269b2fcab8e29af8667164bc +size 180 diff --git a/third_party/ffmpeg/include/libavutil/avstring.h b/third_party/ffmpeg/include/libavutil/avstring.h new file mode 100644 index 0000000000..ce048414e3 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/avstring.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac51462bf55ff62da39db4b15c7a6b9b783387709f06879196a065691b34edbf +size 14940 diff --git a/third_party/ffmpeg/include/libavutil/avutil.h b/third_party/ffmpeg/include/libavutil/avutil.h new file mode 100644 index 0000000000..7f3e2654eb --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/avutil.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7640d72de6a72eb9589ed2c623b4ec0924a241097846086c62dc3e05ab70ee9e +size 9968 diff --git a/third_party/ffmpeg/include/libavutil/base64.h b/third_party/ffmpeg/include/libavutil/base64.h new file mode 100644 index 0000000000..db8221772a --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/base64.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81ac13d23f3744fe85ea2651ce903e201cd55fc63fcdd899d2cfe5560d50ef3d +size 2285 diff --git a/third_party/ffmpeg/include/libavutil/blowfish.h b/third_party/ffmpeg/include/libavutil/blowfish.h new file mode 100644 index 0000000000..c16f03bfd2 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/blowfish.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b955a63c60c8b3be0203ec6c3973f9084d848cf884fe56cd56088301aeef7992 +size 2394 diff --git a/third_party/ffmpeg/include/libavutil/bprint.h b/third_party/ffmpeg/include/libavutil/bprint.h new file mode 100644 index 0000000000..9a55285676 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/bprint.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c19f6dc2c06e55d47bc1fbe9fc7658acc7a0d506b6baf16839a6119a03e873c1 +size 8812 diff --git a/third_party/ffmpeg/include/libavutil/bswap.h b/third_party/ffmpeg/include/libavutil/bswap.h new file mode 100644 index 0000000000..b8e017531e --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/bswap.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:855e138f6d8c7947553b1c63a7bab06bcb71362f03c72b761fd9ed61820d71aa +size 2903 diff --git a/third_party/ffmpeg/include/libavutil/buffer.h b/third_party/ffmpeg/include/libavutil/buffer.h new file mode 100644 index 0000000000..fd462ec6b8 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/buffer.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f16742d574216434580573a2b09f56fc5b66b7dda1960d4f02ba59e3269ba548 +size 11998 diff --git a/third_party/ffmpeg/include/libavutil/camellia.h b/third_party/ffmpeg/include/libavutil/camellia.h new file mode 100644 index 0000000000..8d6fdaca6c --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/camellia.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1db30753e71c73f1937e807850069e8215cdf37a1bc3ff89d3a6370a719c1fde +size 2139 diff --git a/third_party/ffmpeg/include/libavutil/cast5.h b/third_party/ffmpeg/include/libavutil/cast5.h new file mode 100644 index 0000000000..2dfac6f5b2 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/cast5.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05b2e13aecaa0adbb470081a689f45baffb8e03a71997c31f37a22ea4e383a60 +size 2561 diff --git a/third_party/ffmpeg/include/libavutil/channel_layout.h b/third_party/ffmpeg/include/libavutil/channel_layout.h new file mode 100644 index 0000000000..02549bd9a9 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/channel_layout.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5df118692f2b7043820f0e641f1968cadb66c39f0ba85fab7435391322a223f5 +size 33727 diff --git a/third_party/ffmpeg/include/libavutil/common.h b/third_party/ffmpeg/include/libavutil/common.h new file mode 100644 index 0000000000..8f2dfcaebb --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/common.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d861dac3eaecc326fa8fb717d90f195d961cc8ac7898daccfefd10bd5ea42398 +size 17166 diff --git a/third_party/ffmpeg/include/libavutil/cpu.h b/third_party/ffmpeg/include/libavutil/cpu.h new file mode 100644 index 0000000000..3181cd0896 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/cpu.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37c4ccea939e19b463a2a3e4c101ff8dd03c98847fc045395d28ba0c9db8b662 +size 6320 diff --git a/third_party/ffmpeg/include/libavutil/crc.h b/third_party/ffmpeg/include/libavutil/crc.h new file mode 100644 index 0000000000..9cc23bb941 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/crc.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5728cf65705a46723ea28b4f6c8361aad82b76a90e859943efe8af0edb79ec86 +size 3259 diff --git a/third_party/ffmpeg/include/libavutil/csp.h b/third_party/ffmpeg/include/libavutil/csp.h new file mode 100644 index 0000000000..f4c8c5652c --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/csp.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7969fd662f31cf3180403510a6784a14af60d7f9bf3a569dde84585a696dff09 +size 4927 diff --git a/third_party/ffmpeg/include/libavutil/des.h b/third_party/ffmpeg/include/libavutil/des.h new file mode 100644 index 0000000000..5df3354f97 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/des.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15ebdda1af65d91c4607a3444c5f749d5e9757ff5d7f4b04213b3194603f74d9 +size 2514 diff --git a/third_party/ffmpeg/include/libavutil/detection_bbox.h b/third_party/ffmpeg/include/libavutil/detection_bbox.h new file mode 100644 index 0000000000..f81ef0a64a --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/detection_bbox.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f5817d77af243a52e905947aa5ae73c218d68dba909040b2f63bd2ca6f93922 +size 3524 diff --git a/third_party/ffmpeg/include/libavutil/dict.h b/third_party/ffmpeg/include/libavutil/dict.h new file mode 100644 index 0000000000..e01fbf8bb3 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/dict.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c95cba1829e7886e31469f4e058bb9af3c8f215619c7ffddbc2045c9039f0554 +size 9374 diff --git a/third_party/ffmpeg/include/libavutil/display.h b/third_party/ffmpeg/include/libavutil/display.h new file mode 100644 index 0000000000..6bca44f9f8 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/display.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9c78c80aa9331b945802b6bcd1db4ecc9ec4f9fad41993cc82b880c0dec2576 +size 3472 diff --git a/third_party/ffmpeg/include/libavutil/dovi_meta.h b/third_party/ffmpeg/include/libavutil/dovi_meta.h new file mode 100644 index 0000000000..0d545564b2 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/dovi_meta.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a67a422676c5b6e2dfd59b3e6a8e3f816b0602f5d73332c0b1d8e6b43644077 +size 7641 diff --git a/third_party/ffmpeg/include/libavutil/downmix_info.h b/third_party/ffmpeg/include/libavutil/downmix_info.h new file mode 100644 index 0000000000..cfebdff5be --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/downmix_info.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fc23ad8f0750d82fcd6aa3b653998e2ea9721f9d1664df7b6cb80e93d7fa3aa +size 3235 diff --git a/third_party/ffmpeg/include/libavutil/encryption_info.h b/third_party/ffmpeg/include/libavutil/encryption_info.h new file mode 100644 index 0000000000..490da787ff --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/encryption_info.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccc3a4a889b8a3c5aaf37b9fb2407bcdf23a065487c7cba718518a517c463b18 +size 7056 diff --git a/third_party/ffmpeg/include/libavutil/error.h b/third_party/ffmpeg/include/libavutil/error.h new file mode 100644 index 0000000000..30e492756d --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/error.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f90feec75a317e491618e06ce14d19218e5b0257c885155613b704002a9d5bda +size 5489 diff --git a/third_party/ffmpeg/include/libavutil/eval.h b/third_party/ffmpeg/include/libavutil/eval.h new file mode 100644 index 0000000000..49448a05c7 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/eval.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e92af6be91c610c3bcad3a344a2df5e3516153fc89045464447bb0ee44b1f56 +size 6599 diff --git a/third_party/ffmpeg/include/libavutil/executor.h b/third_party/ffmpeg/include/libavutil/executor.h new file mode 100644 index 0000000000..91edf0541f --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/executor.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c949cf5269ad933ed1a200ece06a3998db10147e1403629a398b7be52459cdc +size 1885 diff --git a/third_party/ffmpeg/include/libavutil/ffversion.h b/third_party/ffmpeg/include/libavutil/ffversion.h new file mode 100644 index 0000000000..7fe4e44534 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/ffversion.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1defa7afe4fab2090455bf74435ce185bce721d1b795f94c56994a031ae20080 +size 184 diff --git a/third_party/ffmpeg/include/libavutil/fifo.h b/third_party/ffmpeg/include/libavutil/fifo.h new file mode 100644 index 0000000000..8e4cff8600 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/fifo.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c77e489715a83e1dc1ce3187b5e23b1b0cd52762638919e955d43afae316f9b +size 15452 diff --git a/third_party/ffmpeg/include/libavutil/file.h b/third_party/ffmpeg/include/libavutil/file.h new file mode 100644 index 0000000000..44cae78190 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/file.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f91c41bc9a14a206bc86c87b9edd7215b5be63b8aade2bda40297dc6752de36 +size 3039 diff --git a/third_party/ffmpeg/include/libavutil/film_grain_params.h b/third_party/ffmpeg/include/libavutil/film_grain_params.h new file mode 100644 index 0000000000..8d052ff6d2 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/film_grain_params.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1bda2934dce25cb0bb9fa6eaae230cd21aca79428905b66417f74fc8f0fa72a +size 8499 diff --git a/third_party/ffmpeg/include/libavutil/frame.h b/third_party/ffmpeg/include/libavutil/frame.h new file mode 100644 index 0000000000..013f70247f --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/frame.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0512cdd2a4479faac17b556f6311a3307ace2a5278217c4d738bd752c07dc37e +size 35894 diff --git a/third_party/ffmpeg/include/libavutil/hash.h b/third_party/ffmpeg/include/libavutil/hash.h new file mode 100644 index 0000000000..db71ca59f5 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hash.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0896571267220736679eea28c454783795a02a0f1aef008ebe7c40489a75fdd +size 8457 diff --git a/third_party/ffmpeg/include/libavutil/hdr_dynamic_metadata.h b/third_party/ffmpeg/include/libavutil/hdr_dynamic_metadata.h new file mode 100644 index 0000000000..f920e30d6c --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hdr_dynamic_metadata.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:457274c2eda1fe91e83ae76b9d1ab8682e6144adfacbd2c86f5fb4a03ede421f +size 14385 diff --git a/third_party/ffmpeg/include/libavutil/hdr_dynamic_vivid_metadata.h b/third_party/ffmpeg/include/libavutil/hdr_dynamic_vivid_metadata.h new file mode 100644 index 0000000000..64e00109fe --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hdr_dynamic_vivid_metadata.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f50e471376bc9e8ca504a338cfe74ef634b8f2c242ccefed2e4da67197112de +size 10004 diff --git a/third_party/ffmpeg/include/libavutil/hmac.h b/third_party/ffmpeg/include/libavutil/hmac.h new file mode 100644 index 0000000000..919dc1ec2a --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hmac.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d14d625a897d6bba0668acdf33dc597bb0050237c5c1a5f7e568fe36822782e7 +size 2865 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext.h b/third_party/ffmpeg/include/libavutil/hwcontext.h new file mode 100644 index 0000000000..5ccfbbe916 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e05c09b0a2f51c800aa57dc21277b5b4534e6783ab3df52dbf81c63e37fe8323 +size 24341 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_cuda.h b/third_party/ffmpeg/include/libavutil/hwcontext_cuda.h new file mode 100644 index 0000000000..c596a2781d --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_cuda.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4878f46347271bc7a9ff26bb1573449a99cc81447684e1034a3edd4b0ff91d9a +size 1843 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_d3d11va.h b/third_party/ffmpeg/include/libavutil/hwcontext_d3d11va.h new file mode 100644 index 0000000000..efdaa88006 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_d3d11va.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d99c1b5c5dab94c23709d4ce6cf489c2a0b4f4bd57bb04e2196371277dca5af7 +size 6669 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_drm.h b/third_party/ffmpeg/include/libavutil/hwcontext_drm.h new file mode 100644 index 0000000000..fa6732af71 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_drm.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b598f37f40cf1342f923c0b97784a6f2830b543868eccee046375e096fbd5f24 +size 4673 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_dxva2.h b/third_party/ffmpeg/include/libavutil/hwcontext_dxva2.h new file mode 100644 index 0000000000..d871979e67 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_dxva2.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73a0333b65e99675834dcb1b63a5e9339638ccc619f1a2fcba85cdd0e179ade0 +size 2411 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_mediacodec.h b/third_party/ffmpeg/include/libavutil/hwcontext_mediacodec.h new file mode 100644 index 0000000000..efee54fea8 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_mediacodec.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c602859ebca906ba6e43ea548ff28821cf2886b4500b2be1deaaf2d552496d4 +size 1988 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_opencl.h b/third_party/ffmpeg/include/libavutil/hwcontext_opencl.h new file mode 100644 index 0000000000..812b29caaf --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_opencl.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad521fa2fd015cb1aba962468ca4ac176f5fc6b2c4b7be28f05e1c03d89e1b31 +size 3097 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_qsv.h b/third_party/ffmpeg/include/libavutil/hwcontext_qsv.h new file mode 100644 index 0000000000..a5053d51de --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_qsv.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4986f86340b8162ef2724f0e77a380b5133aaa0612749cfb836c38b4c166d20d +size 1960 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_vaapi.h b/third_party/ffmpeg/include/libavutil/hwcontext_vaapi.h new file mode 100644 index 0000000000..b205bc5fcf --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_vaapi.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f6c6a5250dd0f901cdc7de8b9b3db26102719b7e056cd17500009096bfd9b39 +size 3787 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_vdpau.h b/third_party/ffmpeg/include/libavutil/hwcontext_vdpau.h new file mode 100644 index 0000000000..8cd234bf7b --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_vdpau.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c96373d9e5deb2c500004f3f55ee1d2cea0f76cdfaeabaf5a3ad3e4938e8252 +size 1360 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_videotoolbox.h b/third_party/ffmpeg/include/libavutil/hwcontext_videotoolbox.h new file mode 100644 index 0000000000..db959d5fc1 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_videotoolbox.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f74c83778a0df9738abcae823412436cbbeaff8e9083281fb6f38116cb401c2 +size 3431 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_vulkan.h b/third_party/ffmpeg/include/libavutil/hwcontext_vulkan.h new file mode 100644 index 0000000000..fce1192137 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/hwcontext_vulkan.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74067b6224e8a9d60969f192fdc17f97a12ff073274f5047c380543647026760 +size 11412 diff --git a/third_party/ffmpeg/include/libavutil/imgutils.h b/third_party/ffmpeg/include/libavutil/imgutils.h new file mode 100644 index 0000000000..9636ebc81b --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/imgutils.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4cceea6c7b3017dea61a9fd514d7460ab13592907addb69a2e92870872207f1 +size 14819 diff --git a/third_party/ffmpeg/include/libavutil/intfloat.h b/third_party/ffmpeg/include/libavutil/intfloat.h new file mode 100644 index 0000000000..278a2a6311 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/intfloat.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a29e4eebc8c269cfd867b96de91d8231773d392c12a8820e46eaba96d2b4ca1 +size 1726 diff --git a/third_party/ffmpeg/include/libavutil/intreadwrite.h b/third_party/ffmpeg/include/libavutil/intreadwrite.h new file mode 100644 index 0000000000..e6ae02efbf --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/intreadwrite.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f815f4edb0fb58c0b2c7b4f4763a27ee223672fa43ef4c5c0b99fb4575b66bf7 +size 18735 diff --git a/third_party/ffmpeg/include/libavutil/lfg.h b/third_party/ffmpeg/include/libavutil/lfg.h new file mode 100644 index 0000000000..4e9488eaa0 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/lfg.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3bc1533172fe74870df8e37f168f28b0ff2e21bdb6a519b6555ec72710c910f +size 2541 diff --git a/third_party/ffmpeg/include/libavutil/log.h b/third_party/ffmpeg/include/libavutil/log.h new file mode 100644 index 0000000000..a3dd2c72af --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/log.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8555ad5d41f5f2fa1c7dd0dc65f2145b0517c0a414654575aade8b3968e4c408 +size 12766 diff --git a/third_party/ffmpeg/include/libavutil/lzo.h b/third_party/ffmpeg/include/libavutil/lzo.h new file mode 100644 index 0000000000..2e7db3c19d --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/lzo.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61e89928dee9d83030adececac06aa6c1ae2aada06c5682fde52c52015c53556 +size 2048 diff --git a/third_party/ffmpeg/include/libavutil/macros.h b/third_party/ffmpeg/include/libavutil/macros.h new file mode 100644 index 0000000000..57932eaeb0 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/macros.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b63b3a268b096f0eed1e91b821714cff334e5dc5bb34365148704393ae15321e +size 2304 diff --git a/third_party/ffmpeg/include/libavutil/mastering_display_metadata.h b/third_party/ffmpeg/include/libavutil/mastering_display_metadata.h new file mode 100644 index 0000000000..90983df170 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/mastering_display_metadata.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d5743a42306ac0158e26248a1281ae8be9ebfeb02e67f14e0e6ae770a543a65 +size 3944 diff --git a/third_party/ffmpeg/include/libavutil/mathematics.h b/third_party/ffmpeg/include/libavutil/mathematics.h new file mode 100644 index 0000000000..e707131c8f --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/mathematics.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64fac2eb3a42fd3788f5585ac8e65c7d5cd82711730d1f030042ba0a62fe1a62 +size 9563 diff --git a/third_party/ffmpeg/include/libavutil/md5.h b/third_party/ffmpeg/include/libavutil/md5.h new file mode 100644 index 0000000000..33a63e05cb --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/md5.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b42de1758d289f78b4d20c47686f443e4ea8a5a6411c0deb357f709d2ef34d7 +size 2092 diff --git a/third_party/ffmpeg/include/libavutil/mem.h b/third_party/ffmpeg/include/libavutil/mem.h new file mode 100644 index 0000000000..5a81cd674a --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/mem.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf9a1d8c218c38b333efb16be076c5778d31d66a0691bdd6175f171c08283d7c +size 20457 diff --git a/third_party/ffmpeg/include/libavutil/motion_vector.h b/third_party/ffmpeg/include/libavutil/motion_vector.h new file mode 100644 index 0000000000..6c61741e5e --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/motion_vector.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc0b0a15a638c8b91df95a418c5951ee5e787d518f22b6e3d70094922536e8bb +size 1770 diff --git a/third_party/ffmpeg/include/libavutil/murmur3.h b/third_party/ffmpeg/include/libavutil/murmur3.h new file mode 100644 index 0000000000..353b5a763f --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/murmur3.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:649258a51c4737fa19a025a489e2ac9e9b06a96eafa802f2765178c684382887 +size 3507 diff --git a/third_party/ffmpeg/include/libavutil/opt.h b/third_party/ffmpeg/include/libavutil/opt.h new file mode 100644 index 0000000000..ef2b874b26 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/opt.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a1679c21453e4337cc7d377efaec6610340a59204c0b58fc102ab3a9da51a39 +size 37201 diff --git a/third_party/ffmpeg/include/libavutil/parseutils.h b/third_party/ffmpeg/include/libavutil/parseutils.h new file mode 100644 index 0000000000..de2f938c60 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/parseutils.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8efed69396851f429a8258d50e9c4f0431f921687a7c31bf6db13d14f7482c3 +size 7888 diff --git a/third_party/ffmpeg/include/libavutil/pixdesc.h b/third_party/ffmpeg/include/libavutil/pixdesc.h new file mode 100644 index 0000000000..ade8e6906c --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/pixdesc.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:253359c22bc051754a9ded9497a850e5296cbfd058a9b73957c23b53d7314d96 +size 16031 diff --git a/third_party/ffmpeg/include/libavutil/pixelutils.h b/third_party/ffmpeg/include/libavutil/pixelutils.h new file mode 100644 index 0000000000..2734d292c9 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/pixelutils.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:339cd6ffb6460d06401801c5dfb91ca66b9bdc028e1acc9ff4a0f447cfd3785c +size 2051 diff --git a/third_party/ffmpeg/include/libavutil/pixfmt.h b/third_party/ffmpeg/include/libavutil/pixfmt.h new file mode 100644 index 0000000000..459bb057c9 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/pixfmt.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69104b943b1c44a54e46635be1e5da0ff6be7cd3f1af6ebc9924d4d965d0e420 +size 41556 diff --git a/third_party/ffmpeg/include/libavutil/random_seed.h b/third_party/ffmpeg/include/libavutil/random_seed.h new file mode 100644 index 0000000000..a4e8933e50 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/random_seed.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4490fd79919aadb18f765caac0c210d22cafa4d63cddcf9275e6f5bf66e2fdea +size 1889 diff --git a/third_party/ffmpeg/include/libavutil/rational.h b/third_party/ffmpeg/include/libavutil/rational.h new file mode 100644 index 0000000000..32a39febc7 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/rational.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:700422cc782acde6898522ffe1b74658b12ca22e2cfbb8f44986266216de7f21 +size 6100 diff --git a/third_party/ffmpeg/include/libavutil/rc4.h b/third_party/ffmpeg/include/libavutil/rc4.h new file mode 100644 index 0000000000..a79ce5a1b4 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/rc4.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ea0fbee43677721ad0d846f703a785aaf9881794d1cca0bcb210241b260fc26 +size 2003 diff --git a/third_party/ffmpeg/include/libavutil/replaygain.h b/third_party/ffmpeg/include/libavutil/replaygain.h new file mode 100644 index 0000000000..760b289ee1 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/replaygain.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ec82edbdc4e5493fba3cae6a27566f0f15d1399ccf16e25073ffd50ba8187ea +size 1607 diff --git a/third_party/ffmpeg/include/libavutil/ripemd.h b/third_party/ffmpeg/include/libavutil/ripemd.h new file mode 100644 index 0000000000..34cd2714a6 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/ripemd.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df9ef8c29ee31e5bd8ea299b03d51bd25fe937583793a994db53d1df2b316620 +size 2158 diff --git a/third_party/ffmpeg/include/libavutil/samplefmt.h b/third_party/ffmpeg/include/libavutil/samplefmt.h new file mode 100644 index 0000000000..c37f4ace9d --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/samplefmt.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e844d8b4a691256238d10de762a6a536ffe52995705166aee0d7f01ce79778a9 +size 10301 diff --git a/third_party/ffmpeg/include/libavutil/sha.h b/third_party/ffmpeg/include/libavutil/sha.h new file mode 100644 index 0000000000..58b5ea658e --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/sha.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91280db6995b1b99b9e5aad0aa211a3177dc4d2841da2fea097f54964b7891fd +size 2368 diff --git a/third_party/ffmpeg/include/libavutil/sha512.h b/third_party/ffmpeg/include/libavutil/sha512.h new file mode 100644 index 0000000000..4e62f7fb63 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/sha512.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da265152798b221706d7fe95293a0e8cd18fa2b5087bf32504a8120f10e7658f +size 2413 diff --git a/third_party/ffmpeg/include/libavutil/spherical.h b/third_party/ffmpeg/include/libavutil/spherical.h new file mode 100644 index 0000000000..2e14e4f293 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/spherical.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18b8e559f69a9c93b251c9d3956b2aba7f47ba46ef5a7fa01f8ae858967b73da +size 7997 diff --git a/third_party/ffmpeg/include/libavutil/stereo3d.h b/third_party/ffmpeg/include/libavutil/stereo3d.h new file mode 100644 index 0000000000..952eb6cf79 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/stereo3d.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ece3ffb6baafd288f9f2d8747cae92b1b3985e6693fa2de6bc200173a1981b6 +size 5224 diff --git a/third_party/ffmpeg/include/libavutil/tea.h b/third_party/ffmpeg/include/libavutil/tea.h new file mode 100644 index 0000000000..5b3094167b --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/tea.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c1e93c566630bb4eeedad3ef3c8719bd6050081ac1c764b1fde81aba4969076 +size 2035 diff --git a/third_party/ffmpeg/include/libavutil/threadmessage.h b/third_party/ffmpeg/include/libavutil/threadmessage.h new file mode 100644 index 0000000000..70c8112bad --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/threadmessage.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bb242d7adc48662b947726843108aff7c34547d7a4a0d0e6f58f54a00fc4c9f +size 3910 diff --git a/third_party/ffmpeg/include/libavutil/time.h b/third_party/ffmpeg/include/libavutil/time.h new file mode 100644 index 0000000000..ef77ac592e --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/time.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40e11fa242e0585996753affb054443e78be25919b7c3063042d0aaff1656760 +size 1800 diff --git a/third_party/ffmpeg/include/libavutil/timecode.h b/third_party/ffmpeg/include/libavutil/timecode.h new file mode 100644 index 0000000000..5b54b01bcf --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/timecode.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:afd0a634b1abcb282c694ae6c43f0412fe2ccb37fb3f08433af33779ea9e572e +size 7843 diff --git a/third_party/ffmpeg/include/libavutil/timestamp.h b/third_party/ffmpeg/include/libavutil/timestamp.h new file mode 100644 index 0000000000..828043fe81 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/timestamp.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7cfbb6640449104c473cc1bc6f78244ce97636fca294da2867cedaab868c1b8c +size 2617 diff --git a/third_party/ffmpeg/include/libavutil/tree.h b/third_party/ffmpeg/include/libavutil/tree.h new file mode 100644 index 0000000000..ca3f72fff0 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/tree.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f8e906917612a05c138036dea7ed9f8faee5899413a523fdad4eb51711bc1e5 +size 5408 diff --git a/third_party/ffmpeg/include/libavutil/twofish.h b/third_party/ffmpeg/include/libavutil/twofish.h new file mode 100644 index 0000000000..96cf965ab3 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/twofish.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b71714336821e1c606b65620ba4b1ea47e431666be41f3174facbc51047fd814 +size 2245 diff --git a/third_party/ffmpeg/include/libavutil/tx.h b/third_party/ffmpeg/include/libavutil/tx.h new file mode 100644 index 0000000000..223ffc4951 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/tx.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34f83ae31d074847e46579f25e3367210fbbfe1832b5315883cf4a28980ef101 +size 7140 diff --git a/third_party/ffmpeg/include/libavutil/uuid.h b/third_party/ffmpeg/include/libavutil/uuid.h new file mode 100644 index 0000000000..3bc380c31a --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/uuid.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e669ce76a6b987e189b4d7ff62d0fd9ad6e334fa4967076cc6d912976574b646 +size 4895 diff --git a/third_party/ffmpeg/include/libavutil/version.h b/third_party/ffmpeg/include/libavutil/version.h new file mode 100644 index 0000000000..1f0fa95e08 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/version.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fed7c6f02a873de2c7e2f7e6d24e48e747b3f5204e36d50e19b5e82358b76470 +size 4832 diff --git a/third_party/ffmpeg/include/libavutil/video_enc_params.h b/third_party/ffmpeg/include/libavutil/video_enc_params.h new file mode 100644 index 0000000000..7ce1b57024 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/video_enc_params.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f287486c4f828f82e579f93ea98fccb98749129544f660decfa56da6f818fd57 +size 5991 diff --git a/third_party/ffmpeg/include/libavutil/video_hint.h b/third_party/ffmpeg/include/libavutil/video_hint.h new file mode 100644 index 0000000000..46ad47b113 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/video_hint.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c8b7768ffd03af5034a13274d1ca0cc6206b9f2c0c478400aba8a6c79ad5359 +size 3586 diff --git a/third_party/ffmpeg/include/libavutil/xtea.h b/third_party/ffmpeg/include/libavutil/xtea.h new file mode 100644 index 0000000000..99bdc08b36 --- /dev/null +++ b/third_party/ffmpeg/include/libavutil/xtea.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2eb91f780cc4ad86095e4ebbce453475d40f4e9b8737d52bdf20a068dfafcdf0 +size 2834 diff --git a/third_party/ffmpeg/include/x264.h b/third_party/ffmpeg/include/x264.h new file mode 100644 index 0000000000..17c5741777 --- /dev/null +++ b/third_party/ffmpeg/include/x264.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cec084bdb3cd2330800d0726bdbd99d91112df3fdb6d79f94e15ba67808b715c +size 49056 diff --git a/third_party/ffmpeg/include/x264_config.h b/third_party/ffmpeg/include/x264_config.h new file mode 100644 index 0000000000..0b4ef0a07f --- /dev/null +++ b/third_party/ffmpeg/include/x264_config.h @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81acf190604a6c03d8e44dd3a9751ea12dc4823cdff1c9f0b376faaeafe79ed2 +size 172 diff --git a/third_party/ffmpeg/larch64/lib/libavcodec.a b/third_party/ffmpeg/larch64/lib/libavcodec.a new file mode 100644 index 0000000000..7e094e1813 --- /dev/null +++ b/third_party/ffmpeg/larch64/lib/libavcodec.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cbbf4f95193267b3799652b8c86975642a22ad6c91b1df40e24ec602d315368 +size 2368942 diff --git a/third_party/ffmpeg/larch64/lib/libavformat.a b/third_party/ffmpeg/larch64/lib/libavformat.a new file mode 100644 index 0000000000..0cfd4d2df4 --- /dev/null +++ b/third_party/ffmpeg/larch64/lib/libavformat.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f12e0f2a24ff87f328b4045b91d6453133588c2972a96980a05fc6339ff27eec +size 946966 diff --git a/third_party/ffmpeg/larch64/lib/libavutil.a b/third_party/ffmpeg/larch64/lib/libavutil.a new file mode 100644 index 0000000000..0270811548 --- /dev/null +++ b/third_party/ffmpeg/larch64/lib/libavutil.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0944c03c21379aa6c264e0e1b59538789e0a15c6c77e5ac0ef8d571cb6f5b01 +size 891284 diff --git a/third_party/ffmpeg/larch64/lib/libx264.a b/third_party/ffmpeg/larch64/lib/libx264.a new file mode 100644 index 0000000000..5a95720c7f --- /dev/null +++ b/third_party/ffmpeg/larch64/lib/libx264.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ceeac8654bb25feea79f64e91197e20a26fee4c2c36af6eae9f13943d8acf2c1 +size 2222366 diff --git a/third_party/ffmpeg/x86_64/lib/libavcodec.a b/third_party/ffmpeg/x86_64/lib/libavcodec.a new file mode 100644 index 0000000000..00a4f4b48f --- /dev/null +++ b/third_party/ffmpeg/x86_64/lib/libavcodec.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32ac488b287137c0537da1a81b3fa81e074bb79d9bf5af58185779f3dfe70070 +size 3267672 diff --git a/third_party/ffmpeg/x86_64/lib/libavformat.a b/third_party/ffmpeg/x86_64/lib/libavformat.a new file mode 100644 index 0000000000..497fc2a34f --- /dev/null +++ b/third_party/ffmpeg/x86_64/lib/libavformat.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53ba309c4adffc993671f4700e316fd5886f28aead91f051261ae1ffe8dbb03e +size 919414 diff --git a/third_party/ffmpeg/x86_64/lib/libavutil.a b/third_party/ffmpeg/x86_64/lib/libavutil.a new file mode 100644 index 0000000000..0fc4523171 --- /dev/null +++ b/third_party/ffmpeg/x86_64/lib/libavutil.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4fed041aafbd9672bfff2b8988256ebd3fd77892c4ecdef705c7b81173a3c56 +size 1022522 diff --git a/third_party/ffmpeg/x86_64/lib/libx264.a b/third_party/ffmpeg/x86_64/lib/libx264.a new file mode 100644 index 0000000000..84b9d47425 --- /dev/null +++ b/third_party/ffmpeg/x86_64/lib/libx264.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e17625b8355736b3d5a8bf8ef49d10b8a531783b2c74d41246be7a35fdce3dff +size 3065330 diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 89be3cceb2..eb6f15817d 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -16,7 +16,7 @@ qt_libs = ['qt_util'] + base_libs cabana_env = qt_env.Clone() -cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'zstd', 'curl', 'yuv', 'usb-1.0'] + qt_libs +cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, 'avformat', 'avcodec', 'avutil', 'x264', 'bz2', 'zstd', 'curl', 'yuv', 'usb-1.0'] + qt_libs opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../opendbc/dbc").abspath) cabana_env['CXXFLAGS'] += [opendbc_path] diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index f33569704a..6abc10c8e8 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -32,12 +32,6 @@ function install_ubuntu_common_requirements() { libcurl4-openssl-dev \ git \ git-lfs \ - ffmpeg \ - libavformat-dev \ - libavcodec-dev \ - libavdevice-dev \ - libavutil-dev \ - libavfilter-dev \ libbz2-dev \ libeigen3-dev \ libffi-dev \ diff --git a/tools/mac_setup.sh b/tools/mac_setup.sh index 19ef77e01c..b5ee988cd6 100755 --- a/tools/mac_setup.sh +++ b/tools/mac_setup.sh @@ -38,7 +38,6 @@ brew "zlib" brew "capnp" brew "coreutils" brew "eigen" -brew "ffmpeg" brew "glfw" brew "libarchive" brew "libusb" diff --git a/tools/replay/SConscript b/tools/replay/SConscript index 136c4119f6..013cf76584 100644 --- a/tools/replay/SConscript +++ b/tools/replay/SConscript @@ -17,7 +17,7 @@ if arch != "Darwin": replay_lib_src.append("qcom_decoder.cc") replay_lib = replay_env.Library("replay", replay_lib_src, LIBS=base_libs, FRAMEWORKS=base_frameworks) Export('replay_lib') -replay_libs = [replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'zstd', 'curl', 'yuv', 'ncurses'] + base_libs +replay_libs = [replay_lib, 'avformat', 'avcodec', 'avutil', 'bz2', 'zstd', 'curl', 'yuv', 'ncurses', 'x264'] + base_libs replay_env.Program("replay", ["main.cc"], LIBS=replay_libs, FRAMEWORKS=base_frameworks) if GetOption('extras'): From 1870d4905b99f65f3eadaf3c4c5923cc43bc12f7 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Fri, 12 Sep 2025 21:52:01 -0700 Subject: [PATCH 007/341] jotpluggler: add tabs to layout (#36146) * queue syncs in main thread to avoid Glfw Error/segfault * tabs --- tools/jotpluggler/assets/plus.png | 3 + tools/jotpluggler/layout.py | 163 ++++++++++++++++++++++++++---- tools/jotpluggler/pluggle.py | 12 +-- tools/jotpluggler/views.py | 27 +++-- 4 files changed, 171 insertions(+), 34 deletions(-) create mode 100644 tools/jotpluggler/assets/plus.png diff --git a/tools/jotpluggler/assets/plus.png b/tools/jotpluggler/assets/plus.png new file mode 100644 index 0000000000..6f8388b24d --- /dev/null +++ b/tools/jotpluggler/assets/plus.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:248b71eafd1b42b0861da92114da3d625221cd88121fff01e0514bf3d79ff3b1 +size 1364 diff --git a/tools/jotpluggler/layout.py b/tools/jotpluggler/layout.py index 917c156f9f..b73b7467a8 100644 --- a/tools/jotpluggler/layout.py +++ b/tools/jotpluggler/layout.py @@ -5,15 +5,135 @@ from openpilot.tools.jotpluggler.views import TimeSeriesPanel GRIP_SIZE = 4 MIN_PANE_SIZE = 60 - -class PlotLayoutManager: - def __init__(self, data_manager: DataManager, playback_manager, worker_manager, scale: float = 1.0): +class LayoutManager: + def __init__(self, data_manager, playback_manager, worker_manager, scale: float = 1.0): self.data_manager = data_manager self.playback_manager = playback_manager self.worker_manager = worker_manager self.scale = scale self.container_tag = "plot_layout_container" + self.tab_bar_tag = "tab_bar_container" + self.tab_content_tag = "tab_content_area" + + self.active_tab = 0 + initial_panel_layout = PanelLayoutManager(data_manager, playback_manager, worker_manager, scale) + self.tabs: dict = {0: {"name": "Tab 1", "panel_layout": initial_panel_layout}} + self._next_tab_id = self.active_tab + 1 + + self._create_tab_themes() + + def _create_tab_themes(self): + for tag, color in (("active_tab_theme", (37, 37, 38, 255)), ("inactive_tab_theme", (70, 70, 75, 255))): + with dpg.theme(tag=tag): + for cmp, target in ((dpg.mvChildWindow, dpg.mvThemeCol_ChildBg), (dpg.mvInputText, dpg.mvThemeCol_FrameBg), (dpg.mvImageButton, dpg.mvThemeCol_Button)): + with dpg.theme_component(cmp): + dpg.add_theme_color(target, color) + with dpg.theme(tag="tab_bar_theme"): + with dpg.theme_component(dpg.mvChildWindow): + dpg.add_theme_color(dpg.mvThemeCol_ChildBg, (51, 51, 55, 255)) + + def create_ui(self, parent_tag: str): + if dpg.does_item_exist(self.container_tag): + dpg.delete_item(self.container_tag) + + with dpg.child_window(tag=self.container_tag, parent=parent_tag, border=False, width=-1, height=-1, no_scrollbar=True, no_scroll_with_mouse=True): + self._create_tab_bar() + self._create_tab_content() + dpg.bind_item_theme(self.tab_bar_tag, "tab_bar_theme") + + def _create_tab_bar(self): + text_size = int(13 * self.scale) + with dpg.child_window(tag=self.tab_bar_tag, parent=self.container_tag, height=(text_size + 8), border=False, horizontal_scrollbar=True): + with dpg.group(horizontal=True, tag="tab_bar_group"): + for tab_id, tab_data in self.tabs.items(): + self._create_tab_ui(tab_id, tab_data["name"]) + dpg.add_image_button(texture_tag="plus_texture", callback=self.add_tab, width=text_size, height=text_size, tag="add_tab_button") + dpg.bind_item_theme("add_tab_button", "inactive_tab_theme") + + def _create_tab_ui(self, tab_id: int, tab_name: str): + text_size = int(13 * self.scale) + tab_width = int(120 * self.scale) + with dpg.child_window(width=tab_width, height=-1, border=False, no_scrollbar=True, tag=f"tab_window_{tab_id}", parent="tab_bar_group"): + with dpg.group(horizontal=True, tag=f"tab_group_{tab_id}"): + dpg.add_input_text( + default_value=tab_name, width=tab_width - text_size - 16, callback=lambda s, v, u: self.rename_tab(u, v), user_data=tab_id, tag=f"tab_input_{tab_id}" + ) + dpg.add_image_button( + texture_tag="x_texture", callback=lambda s, a, u: self.close_tab(u), user_data=tab_id, width=text_size, height=text_size, tag=f"tab_close_{tab_id}" + ) + with dpg.item_handler_registry(tag=f"tab_handler_{tab_id}"): + dpg.add_item_clicked_handler(callback=lambda s, a, u: self.switch_tab(u), user_data=tab_id) + dpg.bind_item_handler_registry(f"tab_group_{tab_id}", f"tab_handler_{tab_id}") + + theme_tag = "active_tab_theme" if tab_id == self.active_tab else "inactive_tab_theme" + dpg.bind_item_theme(f"tab_window_{tab_id}", theme_tag) + + def _create_tab_content(self): + with dpg.child_window(tag=self.tab_content_tag, parent=self.container_tag, border=False, width=-1, height=-1, no_scrollbar=True, no_scroll_with_mouse=True): + active_panel_layout = self.tabs[self.active_tab]["panel_layout"] + active_panel_layout.create_ui() + + def add_tab(self): + new_panel_layout = PanelLayoutManager(self.data_manager, self.playback_manager, self.worker_manager, self.scale) + new_tab = {"name": f"Tab {self._next_tab_id + 1}", "panel_layout": new_panel_layout} + self.tabs[ self._next_tab_id] = new_tab + self._create_tab_ui( self._next_tab_id, new_tab["name"]) + dpg.move_item("add_tab_button", parent="tab_bar_group") # move plus button to end + self.switch_tab( self._next_tab_id) + self._next_tab_id += 1 + + def close_tab(self, tab_id: int): + if len(self.tabs) <= 1: + return # don't allow closing the last tab + + tab_to_close = self.tabs[tab_id] + tab_to_close["panel_layout"].destroy_ui() + for suffix in ["window", "group", "input", "close", "handler"]: + tag = f"tab_{suffix}_{tab_id}" + if dpg.does_item_exist(tag): + dpg.delete_item(tag) + del self.tabs[tab_id] + + if self.active_tab == tab_id: # switch to another tab if we closed the active one + self.active_tab = next(iter(self.tabs.keys())) + self._switch_tab_content() + dpg.bind_item_theme(f"tab_window_{self.active_tab}", "active_tab_theme") + + def switch_tab(self, tab_id: int): + if tab_id == self.active_tab or tab_id not in self.tabs: + return + + current_panel_layout = self.tabs[self.active_tab]["panel_layout"] + current_panel_layout.destroy_ui() + dpg.bind_item_theme(f"tab_window_{self.active_tab}", "inactive_tab_theme") # deactivate old tab + self.active_tab = tab_id + dpg.bind_item_theme(f"tab_window_{tab_id}", "active_tab_theme") # activate new tab + self._switch_tab_content() + + def _switch_tab_content(self): + dpg.delete_item(self.tab_content_tag, children_only=True) + active_panel_layout = self.tabs[self.active_tab]["panel_layout"] + active_panel_layout.create_ui() + active_panel_layout.update_all_panels() + + def rename_tab(self, tab_id: int, new_name: str): + if tab_id in self.tabs: + self.tabs[tab_id]["name"] = new_name + + def update_all_panels(self): + self.tabs[self.active_tab]["panel_layout"].update_all_panels() + + def on_viewport_resize(self): + self.tabs[self.active_tab]["panel_layout"].on_viewport_resize() + +class PanelLayoutManager: + def __init__(self, data_manager: DataManager, playback_manager, worker_manager, scale: float = 1.0): + self.data_manager = data_manager + self.playback_manager = playback_manager + self.worker_manager = worker_manager + self.scale = scale self.active_panels: list = [] + self.parent_tag = "tab_content_area" self.grip_size = int(GRIP_SIZE * self.scale) self.min_pane_size = int(MIN_PANE_SIZE * self.scale) @@ -21,13 +141,18 @@ class PlotLayoutManager: initial_panel = TimeSeriesPanel(data_manager, playback_manager, worker_manager) self.layout: dict = {"type": "panel", "panel": initial_panel} - def create_ui(self, parent_tag: str): - if dpg.does_item_exist(self.container_tag): - dpg.delete_item(self.container_tag) + def create_ui(self): + self.active_panels.clear() - with dpg.child_window(tag=self.container_tag, parent=parent_tag, border=False, width=-1, height=-1, no_scrollbar=True, no_scroll_with_mouse=True): - container_width, container_height = dpg.get_item_rect_size(self.container_tag) - self._create_ui_recursive(self.layout, self.container_tag, [], container_width, container_height) + if dpg.does_item_exist(self.parent_tag): + dpg.delete_item(self.parent_tag, children_only=True) + + container_width, container_height = dpg.get_item_rect_size(self.parent_tag) + self._create_ui_recursive(self.layout, self.parent_tag, [], container_width, container_height) + + def destroy_ui(self): + self._cleanup_ui_recursive(self.layout, []) + self.active_panels.clear() def _create_ui_recursive(self, layout: dict, parent_tag: str, path: list[int], width: int, height: int): if layout["type"] == "panel": @@ -35,14 +160,14 @@ class PlotLayoutManager: else: self._create_split_ui(layout, parent_tag, path, width, height) - def _create_panel_ui(self, layout: dict, parent_tag: str, path: list[int], width: int, height:int): + def _create_panel_ui(self, layout: dict, parent_tag: str, path: list[int], width: int, height: int): panel_tag = self._path_to_tag(path, "panel") panel = layout["panel"] self.active_panels.append(panel) text_size = int(13 * self.scale) - bar_height = (text_size+24) if width < int(279 * self.scale + 80) else (text_size+8) # adjust height to allow for scrollbar + bar_height = (text_size + 24) if width < int(279 * self.scale + 64) else (text_size + 8) # adjust height to allow for scrollbar - with dpg.child_window(parent=parent_tag, border=True, width=-1, height=-1, no_scrollbar=True): + with dpg.child_window(parent=parent_tag, border=False, width=-1, height=-1, no_scrollbar=True): with dpg.group(horizontal=True): with dpg.child_window(tag=panel_tag, width=-(text_size + 16), height=bar_height, horizontal_scrollbar=True, no_scroll_with_mouse=True, border=False): with dpg.group(horizontal=True): @@ -67,7 +192,7 @@ class PlotLayoutManager: for i, child_layout in enumerate(layout["children"]): child_path = path + [i] container_tag = self._path_to_tag(child_path, "container") - pane_width, pane_height = [(pane_sizes[i], -1), (-1, pane_sizes[i])][orientation] # fill 2nd dim up to the border + pane_width, pane_height = [(pane_sizes[i], -1), (-1, pane_sizes[i])][orientation] # fill 2nd dim up to the border with dpg.child_window(tag=container_tag, width=pane_width, height=pane_height, border=False, no_scrollbar=True): child_width, child_height = [(pane_sizes[i], height), (width, pane_sizes[i])][orientation] self._create_ui_recursive(child_layout, container_tag, child_path, child_width, child_height) @@ -137,7 +262,7 @@ class PlotLayoutManager: if path: container_tag = self._path_to_tag(path, "container") else: # Root update - container_tag = self.container_tag + container_tag = self.parent_tag self._cleanup_ui_recursive(layout, path) dpg.delete_item(container_tag, children_only=True) @@ -181,17 +306,17 @@ class PlotLayoutManager: dpg.configure_item(container_tag, **{size_properties[orientation]: pane_sizes[i]}) child_width, child_height = [(pane_sizes[i], available_sizes[1]), (available_sizes[0], pane_sizes[i])][orientation] self._resize_splits_recursive(child_layout, child_path, child_width, child_height) - else: # leaf node/panel - adjust bar height to allow for scrollbar + else: # leaf node/panel - adjust bar height to allow for scrollbar panel_tag = self._path_to_tag(path, "panel") - if width is not None and width < int(279 * self.scale + 80): # scaled widths of the elements in top bar + fixed 8 padding on left and right of each item - dpg.configure_item(panel_tag, height=(int(13*self.scale) + 24)) + if width is not None and width < int(279 * self.scale + 64): # scaled widths of the elements in top bar + fixed 8 padding on left and right of each item + dpg.configure_item(panel_tag, height=(int(13 * self.scale) + 24)) else: - dpg.configure_item(panel_tag, height=(int(13*self.scale) + 8)) + dpg.configure_item(panel_tag, height=(int(13 * self.scale) + 8)) def _get_split_geometry(self, layout: dict, available_size: tuple[int, int]) -> tuple[int, int, list[int]]: orientation = layout["orientation"] num_grips = len(layout["children"]) - 1 - usable_size = max(self.min_pane_size, available_size[orientation] - (num_grips * (self.grip_size + 8 * (2-orientation)))) # approximate, scaling is weird + usable_size = max(self.min_pane_size, available_size[orientation] - (num_grips * (self.grip_size + 8 * (2 - orientation)))) # approximate, scaling is weird pane_sizes = [max(self.min_pane_size, int(usable_size * prop)) for prop in layout["proportions"]] return orientation, usable_size, pane_sizes diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index 57ba2a245f..41bf520cd9 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -10,7 +10,7 @@ import signal from openpilot.common.basedir import BASEDIR from openpilot.tools.jotpluggler.data import DataManager from openpilot.tools.jotpluggler.datatree import DataTree -from openpilot.tools.jotpluggler.layout import PlotLayoutManager +from openpilot.tools.jotpluggler.layout import LayoutManager DEMO_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19" @@ -127,7 +127,7 @@ class MainController: self.worker_manager = WorkerManager() self._create_global_themes() self.data_tree = DataTree(self.data_manager, self.playback_manager) - self.plot_layout_manager = PlotLayoutManager(self.data_manager, self.playback_manager, self.worker_manager, scale=self.scale) + self.layout_manager = LayoutManager(self.data_manager, self.playback_manager, self.worker_manager, scale=self.scale) self.data_manager.add_observer(self.on_data_loaded) def _create_global_themes(self): @@ -168,7 +168,7 @@ class MainController: def setup_ui(self): with dpg.texture_registry(): script_dir = os.path.dirname(os.path.realpath(__file__)) - for image in ["play", "pause", "x", "split_h", "split_v"]: + for image in ["play", "pause", "x", "split_h", "split_v", "plus"]: texture = dpg.load_image(os.path.join(script_dir, "assets", f"{image}.png")) dpg.add_static_texture(width=texture[0], height=texture[1], default_value=texture[3], tag=f"{image}_texture") @@ -186,7 +186,7 @@ class MainController: # Right panel - Plots and timeline with dpg.group(tag="right_panel"): with dpg.child_window(label="Plot Window", border=True, height=-(32 + 13 * self.scale), tag="main_plot_area"): - self.plot_layout_manager.create_ui("main_plot_area") + self.layout_manager.create_ui("main_plot_area") with dpg.child_window(label="Timeline", border=True): with dpg.table(header_row=False, borders_innerH=False, borders_innerV=False, borders_outerH=False, borders_outerV=False): @@ -205,7 +205,7 @@ class MainController: dpg.set_primary_window("Primary Window", True) def on_plot_resize(self, sender, app_data, user_data): - self.plot_layout_manager.on_viewport_resize() + self.layout_manager.on_viewport_resize() def load_route(self): route_name = dpg.get_value("route_input").strip() @@ -227,7 +227,7 @@ class MainController: if not dpg.is_item_active("timeline_slider"): dpg.set_value("timeline_slider", new_time) - self.plot_layout_manager.update_all_panels() + self.layout_manager.update_all_panels() dpg.set_value("fps_counter", f"{dpg.get_frame_rate():.1f} FPS") diff --git a/tools/jotpluggler/views.py b/tools/jotpluggler/views.py index f3da6e3e6f..cb993acf0d 100644 --- a/tools/jotpluggler/views.py +++ b/tools/jotpluggler/views.py @@ -52,6 +52,8 @@ class TimeSeriesPanel(ViewPanel): self._results_deque: deque[tuple[str, list, list]] = deque() self._new_data = False self._last_x_limits = (0.0, 0.0) + self._queued_x_sync: tuple | None = None + self._queued_reallow_x_zoom = False def create_ui(self, parent_tag: str): self.data_manager.add_observer(self.on_data_loaded) @@ -63,8 +65,7 @@ class TimeSeriesPanel(ViewPanel): timeline_series_tag = dpg.add_inf_line_series(x=[0], label="Timeline", parent=self.y_axis_tag, tag=self.timeline_indicator_tag) dpg.bind_item_theme(timeline_series_tag, "global_timeline_theme") - for series_path in list(self._series_data.keys()): - self.add_series(series_path) + self._new_data = True self._ui_created = True def update(self): @@ -72,6 +73,19 @@ class TimeSeriesPanel(ViewPanel): if not self._ui_created: return + if self._queued_x_sync: + min_time, max_time = self._queued_x_sync + self._queued_x_sync = None + dpg.set_axis_limits(self.x_axis_tag, min_time, max_time) + self._last_x_limits = (min_time, max_time) + self._fit_y_axis(min_time, max_time) + self._queued_reallow_x_zoom = True # must wait a frame before allowing user changes so that axis limits take effect + return + + if self._queued_reallow_x_zoom: + self._queued_reallow_x_zoom = False + dpg.set_axis_limits_auto(self.x_axis_tag) + current_limits = dpg.get_axis_limits(self.x_axis_tag) # downsample if plot zoom changed significantly plot_duration = current_limits[1] - current_limits[0] @@ -112,13 +126,8 @@ class TimeSeriesPanel(ViewPanel): def _on_x_axis_sync(self, min_time: float, max_time: float, source_panel): with self._update_lock: - if source_panel == self or not self._ui_created: - return - dpg.set_axis_limits(self.x_axis_tag, min_time, max_time) - dpg.render_dearpygui_frame() - dpg.set_axis_limits_auto(self.x_axis_tag) - self._last_x_limits = (min_time, max_time) - self._fit_y_axis(min_time, max_time) + if source_panel != self: + self._queued_x_sync = (min_time, max_time) def _fit_y_axis(self, x_min: float, x_max: float): if not self._series_data: From 826c5e96a1a71b5650c82793e2723f2000437135 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Fri, 12 Sep 2025 22:15:56 -0700 Subject: [PATCH 008/341] jotpluggler: migrate logs (#36147) migrate logs --- tools/jotpluggler/data.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/jotpluggler/data.py b/tools/jotpluggler/data.py index 100dfe544d..14d54e6bea 100644 --- a/tools/jotpluggler/data.py +++ b/tools/jotpluggler/data.py @@ -5,6 +5,7 @@ import bisect from collections import defaultdict from tqdm import tqdm from openpilot.common.swaglog import cloudlog +from openpilot.selfdrive.test.process_replay.migration import migrate_all from openpilot.tools.lib.logreader import _LogFileReader, LogReader @@ -199,7 +200,8 @@ def msgs_to_time_series(msgs): def _process_segment(segment_identifier: str): try: lr = _LogFileReader(segment_identifier, sort_by_time=True) - return msgs_to_time_series(lr) + migrated_msgs = migrate_all(lr) + return msgs_to_time_series(migrated_msgs) except Exception as e: cloudlog.warning(f"Warning: Failed to process segment {segment_identifier}: {e}") return {}, 0.0, 0.0 From 63df46bf22a05ac2118a7f5d35b5d1bbab5bcae7 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:20:12 -0700 Subject: [PATCH 009/341] jotpluggler: store and load layouts (#36148) * store and load layouts * torque controller layout * ignore missing yaml stubs for mypy --- tools/jotpluggler/layout.py | 130 ++++++++++++++---- .../layouts/torque-controller.yaml | 128 +++++++++++++++++ tools/jotpluggler/pluggle.py | 83 ++++++++++- tools/jotpluggler/views.py | 27 +++- 4 files changed, 335 insertions(+), 33 deletions(-) create mode 100644 tools/jotpluggler/layouts/torque-controller.yaml diff --git a/tools/jotpluggler/layout.py b/tools/jotpluggler/layout.py index b73b7467a8..13fbee54e2 100644 --- a/tools/jotpluggler/layout.py +++ b/tools/jotpluggler/layout.py @@ -20,17 +20,35 @@ class LayoutManager: self.tabs: dict = {0: {"name": "Tab 1", "panel_layout": initial_panel_layout}} self._next_tab_id = self.active_tab + 1 - self._create_tab_themes() + def to_dict(self) -> dict: + return { + "tabs": { + str(tab_id): { + "name": tab_data["name"], + "panel_layout": tab_data["panel_layout"].to_dict() + } + for tab_id, tab_data in self.tabs.items() + } + } - def _create_tab_themes(self): - for tag, color in (("active_tab_theme", (37, 37, 38, 255)), ("inactive_tab_theme", (70, 70, 75, 255))): - with dpg.theme(tag=tag): - for cmp, target in ((dpg.mvChildWindow, dpg.mvThemeCol_ChildBg), (dpg.mvInputText, dpg.mvThemeCol_FrameBg), (dpg.mvImageButton, dpg.mvThemeCol_Button)): - with dpg.theme_component(cmp): - dpg.add_theme_color(target, color) - with dpg.theme(tag="tab_bar_theme"): - with dpg.theme_component(dpg.mvChildWindow): - dpg.add_theme_color(dpg.mvThemeCol_ChildBg, (51, 51, 55, 255)) + def clear_and_load_from_dict(self, data: dict): + tab_ids_to_close = list(self.tabs.keys()) + for tab_id in tab_ids_to_close: + self.close_tab(tab_id, force=True) + + for tab_id_str, tab_data in data["tabs"].items(): + tab_id = int(tab_id_str) + panel_layout = PanelLayoutManager.load_from_dict( + tab_data["panel_layout"], self.data_manager, self.playback_manager, + self.worker_manager, self.scale + ) + self.tabs[tab_id] = { + "name": tab_data["name"], + "panel_layout": panel_layout + } + + self.active_tab = min(self.tabs.keys()) if self.tabs else 0 + self._next_tab_id = max(self.tabs.keys()) + 1 if self.tabs else 1 def create_ui(self, parent_tag: str): if dpg.does_item_exist(self.container_tag): @@ -52,7 +70,7 @@ class LayoutManager: def _create_tab_ui(self, tab_id: int, tab_name: str): text_size = int(13 * self.scale) - tab_width = int(120 * self.scale) + tab_width = int(140 * self.scale) with dpg.child_window(width=tab_width, height=-1, border=False, no_scrollbar=True, tag=f"tab_window_{tab_id}", parent="tab_bar_group"): with dpg.group(horizontal=True, tag=f"tab_group_{tab_id}"): dpg.add_input_text( @@ -70,20 +88,21 @@ class LayoutManager: def _create_tab_content(self): with dpg.child_window(tag=self.tab_content_tag, parent=self.container_tag, border=False, width=-1, height=-1, no_scrollbar=True, no_scroll_with_mouse=True): - active_panel_layout = self.tabs[self.active_tab]["panel_layout"] - active_panel_layout.create_ui() + if self.active_tab in self.tabs: + active_panel_layout = self.tabs[self.active_tab]["panel_layout"] + active_panel_layout.create_ui() def add_tab(self): new_panel_layout = PanelLayoutManager(self.data_manager, self.playback_manager, self.worker_manager, self.scale) new_tab = {"name": f"Tab {self._next_tab_id + 1}", "panel_layout": new_panel_layout} - self.tabs[ self._next_tab_id] = new_tab - self._create_tab_ui( self._next_tab_id, new_tab["name"]) + self.tabs[self._next_tab_id] = new_tab + self._create_tab_ui(self._next_tab_id, new_tab["name"]) dpg.move_item("add_tab_button", parent="tab_bar_group") # move plus button to end - self.switch_tab( self._next_tab_id) + self.switch_tab(self._next_tab_id) self._next_tab_id += 1 - def close_tab(self, tab_id: int): - if len(self.tabs) <= 1: + def close_tab(self, tab_id: int, force = False): + if len(self.tabs) <= 1 and not force: return # don't allow closing the last tab tab_to_close = self.tabs[tab_id] @@ -94,7 +113,7 @@ class LayoutManager: dpg.delete_item(tag) del self.tabs[tab_id] - if self.active_tab == tab_id: # switch to another tab if we closed the active one + if self.active_tab == tab_id and self.tabs: # switch to another tab if we closed the active one self.active_tab = next(iter(self.tabs.keys())) self._switch_tab_content() dpg.bind_item_theme(f"tab_window_{self.active_tab}", "active_tab_theme") @@ -134,6 +153,8 @@ class PanelLayoutManager: self.scale = scale self.active_panels: list = [] self.parent_tag = "tab_content_area" + self._queue_resize = False + self._created_handler_tags: set[str] = set() self.grip_size = int(GRIP_SIZE * self.scale) self.min_pane_size = int(MIN_PANE_SIZE * self.scale) @@ -141,19 +162,70 @@ class PanelLayoutManager: initial_panel = TimeSeriesPanel(data_manager, playback_manager, worker_manager) self.layout: dict = {"type": "panel", "panel": initial_panel} + def to_dict(self) -> dict: + return self._layout_to_dict(self.layout) + + def _layout_to_dict(self, layout: dict) -> dict: + if layout["type"] == "panel": + return { + "type": "panel", + "panel": layout["panel"].to_dict() + } + else: # split + return { + "type": "split", + "orientation": layout["orientation"], + "proportions": layout["proportions"], + "children": [self._layout_to_dict(child) for child in layout["children"]] + } + + @classmethod + def load_from_dict(cls, data: dict, data_manager, playback_manager, worker_manager, scale: float = 1.0): + manager = cls(data_manager, playback_manager, worker_manager, scale) + manager.layout = manager._dict_to_layout(data) + return manager + + def _dict_to_layout(self, data: dict) -> dict: + if data["type"] == "panel": + panel_data = data["panel"] + if panel_data["type"] == "timeseries": + panel = TimeSeriesPanel.load_from_dict( + panel_data, self.data_manager, self.playback_manager, self.worker_manager + ) + return {"type": "panel", "panel": panel} + else: + # Handle future panel types here or make a general mapping + raise ValueError(f"Unknown panel type: {panel_data['type']}") + else: # split + return { + "type": "split", + "orientation": data["orientation"], + "proportions": data["proportions"], + "children": [self._dict_to_layout(child) for child in data["children"]] + } + def create_ui(self): self.active_panels.clear() - if dpg.does_item_exist(self.parent_tag): dpg.delete_item(self.parent_tag, children_only=True) + self._cleanup_all_handlers() container_width, container_height = dpg.get_item_rect_size(self.parent_tag) + if container_width == 0 and container_height == 0: + self._queue_resize = True self._create_ui_recursive(self.layout, self.parent_tag, [], container_width, container_height) def destroy_ui(self): self._cleanup_ui_recursive(self.layout, []) + self._cleanup_all_handlers() self.active_panels.clear() + def _cleanup_all_handlers(self): + for handler_tag in list(self._created_handler_tags): + if dpg.does_item_exist(handler_tag): + dpg.delete_item(handler_tag) + self._created_handler_tags.clear() + def _create_ui_recursive(self, layout: dict, parent_tag: str, path: list[int], width: int, height: int): if layout["type"] == "panel": self._create_panel_ui(layout, parent_tag, path, width, height) @@ -165,13 +237,14 @@ class PanelLayoutManager: panel = layout["panel"] self.active_panels.append(panel) text_size = int(13 * self.scale) - bar_height = (text_size + 24) if width < int(279 * self.scale + 64) else (text_size + 8) # adjust height to allow for scrollbar + bar_height = (text_size + 24) if width < int(329 * self.scale + 64) else (text_size + 8) # adjust height to allow for scrollbar with dpg.child_window(parent=parent_tag, border=False, width=-1, height=-1, no_scrollbar=True): with dpg.group(horizontal=True): with dpg.child_window(tag=panel_tag, width=-(text_size + 16), height=bar_height, horizontal_scrollbar=True, no_scroll_with_mouse=True, border=False): with dpg.group(horizontal=True): - dpg.add_input_text(default_value=panel.title, width=int(100 * self.scale), callback=lambda s, v: setattr(panel, "title", v)) + # if you change the widths make sure to change the sum of widths (currently 329 * scale) + dpg.add_input_text(default_value=panel.title, width=int(150 * self.scale), callback=lambda s, v: setattr(panel, "title", v)) dpg.add_combo(items=["Time Series"], default_value="Time Series", width=int(100 * self.scale)) dpg.add_button(label="Clear", callback=lambda: self.clear_panel(panel), width=int(40 * self.scale)) dpg.add_image_button(texture_tag="split_h_texture", callback=lambda: self.split_panel(path, 0), width=text_size, height=text_size) @@ -280,11 +353,16 @@ class PanelLayoutManager: handler_tag = f"{self._path_to_tag(path, f'grip_{i}')}_handler" if dpg.does_item_exist(handler_tag): dpg.delete_item(handler_tag) + self._created_handler_tags.discard(handler_tag) for i, child in enumerate(layout["children"]): self._cleanup_ui_recursive(child, path + [i]) def update_all_panels(self): + if self._queue_resize: + if (size := dpg.get_item_rect_size(self.parent_tag)) != [0, 0]: + self._queue_resize = False + self._resize_splits_recursive(self.layout, [], *size) for panel in self.active_panels: panel.update() @@ -308,7 +386,7 @@ class PanelLayoutManager: self._resize_splits_recursive(child_layout, child_path, child_width, child_height) else: # leaf node/panel - adjust bar height to allow for scrollbar panel_tag = self._path_to_tag(path, "panel") - if width is not None and width < int(279 * self.scale + 64): # scaled widths of the elements in top bar + fixed 8 padding on left and right of each item + if width is not None and width < int(329 * self.scale + 64): # scaled widths of the elements in top bar + fixed 8 padding on left and right of each item dpg.configure_item(panel_tag, height=(int(13 * self.scale) + 24)) else: dpg.configure_item(panel_tag, height=(int(13 * self.scale) + 8)) @@ -342,16 +420,18 @@ class PanelLayoutManager: def _create_grip(self, parent_tag: str, path: list[int], grip_index: int, orientation: int): grip_tag = self._path_to_tag(path, f"grip_{grip_index}") + handler_tag = f"{grip_tag}_handler" width, height = [(self.grip_size, -1), (-1, self.grip_size)][orientation] with dpg.child_window(tag=grip_tag, parent=parent_tag, width=width, height=height, no_scrollbar=True, border=False): button_tag = dpg.add_button(label="", width=-1, height=-1) - with dpg.item_handler_registry(tag=f"{grip_tag}_handler"): + with dpg.item_handler_registry(tag=handler_tag): user_data = (path, grip_index, orientation) dpg.add_item_active_handler(callback=self._on_grip_drag, user_data=user_data) dpg.add_item_deactivated_handler(callback=self._on_grip_end, user_data=user_data) - dpg.bind_item_handler_registry(button_tag, f"{grip_tag}_handler") + dpg.bind_item_handler_registry(button_tag, handler_tag) + self._created_handler_tags.add(handler_tag) def _on_grip_drag(self, sender, app_data, user_data): path, grip_index, orientation = user_data diff --git a/tools/jotpluggler/layouts/torque-controller.yaml b/tools/jotpluggler/layouts/torque-controller.yaml new file mode 100644 index 0000000000..5503be9e64 --- /dev/null +++ b/tools/jotpluggler/layouts/torque-controller.yaml @@ -0,0 +1,128 @@ +tabs: + '0': + name: Lateral Plan Conformance + panel_layout: + type: split + orientation: 1 + proportions: + - 0.3333333333333333 + - 0.3333333333333333 + - 0.3333333333333333 + children: + - type: panel + panel: + type: timeseries + title: desired vs actual + series_paths: + - controlsState/lateralControlState/torqueState/desiredLateralAccel + - controlsState/lateralControlState/torqueState/actualLateralAccel + - type: panel + panel: + type: timeseries + title: ff vs output + series_paths: + - controlsState/lateralControlState/torqueState/f + - carState/steeringPressed + - carControl/actuators/torque + - type: panel + panel: + type: timeseries + title: vehicle speed + series_paths: + - carState/vEgo + '1': + name: Actuator Performance + panel_layout: + type: split + orientation: 1 + proportions: + - 0.3333333333333333 + - 0.3333333333333333 + - 0.3333333333333333 + children: + - type: panel + panel: + type: timeseries + title: calc vs learned latAccelFactor + series_paths: + - liveTorqueParameters/latAccelFactorFiltered + - liveTorqueParameters/latAccelFactorRaw + - carParams/lateralTuning/torque/latAccelFactor + - type: panel + panel: + type: timeseries + title: learned latAccelOffset + series_paths: + - liveTorqueParameters/latAccelOffsetRaw + - liveTorqueParameters/latAccelOffsetFiltered + - type: panel + panel: + type: timeseries + title: calc vs learned friction + series_paths: + - liveTorqueParameters/frictionCoefficientFiltered + - liveTorqueParameters/frictionCoefficientRaw + - carParams/lateralTuning/torque/friction + '2': + name: Vehicle Dynamics + panel_layout: + type: split + orientation: 1 + proportions: + - 0.3333333333333333 + - 0.3333333333333333 + - 0.3333333333333333 + children: + - type: panel + panel: + type: timeseries + title: initial vs learned steerRatio + series_paths: + - carParams/steerRatio + - liveParameters/steerRatio + - type: panel + panel: + type: timeseries + title: initial vs learned tireStiffnessFactor + series_paths: + - carParams/tireStiffnessFactor + - liveParameters/stiffnessFactor + - type: panel + panel: + type: timeseries + title: live steering angle offsets + series_paths: + - liveParameters/angleOffsetDeg + - liveParameters/angleOffsetAverageDeg + '3': + name: Controller PIF Terms + panel_layout: + type: split + orientation: 1 + proportions: + - 0.3333333333333333 + - 0.3333333333333333 + - 0.3333333333333333 + children: + - type: panel + panel: + type: timeseries + title: ff vs output + series_paths: + - carControl/actuators/torque + - controlsState/lateralControlState/torqueState/f + - carState/steeringPressed + - type: panel + panel: + type: timeseries + title: PIF terms + series_paths: + - controlsState/lateralControlState/torqueState/f + - controlsState/lateralControlState/torqueState/p + - controlsState/lateralControlState/torqueState/i + - type: panel + panel: + type: timeseries + title: road roll angle + series_paths: + - liveParameters/roll diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index 41bf520cd9..61b0a09718 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -7,6 +7,8 @@ import dearpygui.dearpygui as dpg import multiprocessing import uuid import signal +import yaml # type: ignore +from openpilot.common.swaglog import cloudlog from openpilot.common.basedir import BASEDIR from openpilot.tools.jotpluggler.data import DataManager from openpilot.tools.jotpluggler.datatree import DataTree @@ -131,17 +133,27 @@ class MainController: self.data_manager.add_observer(self.on_data_loaded) def _create_global_themes(self): - with dpg.theme(tag="global_line_theme"): + with dpg.theme(tag="line_theme"): with dpg.theme_component(dpg.mvLineSeries): scaled_thickness = max(1.0, self.scale) dpg.add_theme_style(dpg.mvPlotStyleVar_LineWeight, scaled_thickness, category=dpg.mvThemeCat_Plots) - with dpg.theme(tag="global_timeline_theme"): + with dpg.theme(tag="timeline_theme"): with dpg.theme_component(dpg.mvInfLineSeries): scaled_thickness = max(1.0, self.scale) dpg.add_theme_style(dpg.mvPlotStyleVar_LineWeight, scaled_thickness, category=dpg.mvThemeCat_Plots) dpg.add_theme_color(dpg.mvPlotCol_Line, (255, 0, 0, 128), category=dpg.mvThemeCat_Plots) + for tag, color in (("active_tab_theme", (37, 37, 38, 255)), ("inactive_tab_theme", (70, 70, 75, 255))): + with dpg.theme(tag=tag): + for cmp, target in ((dpg.mvChildWindow, dpg.mvThemeCol_ChildBg), (dpg.mvInputText, dpg.mvThemeCol_FrameBg), (dpg.mvImageButton, dpg.mvThemeCol_Button)): + with dpg.theme_component(cmp): + dpg.add_theme_color(target, color) + + with dpg.theme(tag="tab_bar_theme"): + with dpg.theme_component(dpg.mvChildWindow): + dpg.add_theme_color(dpg.mvThemeCol_ChildBg, (51, 51, 55, 255)) + def on_data_loaded(self, data: dict): duration = data.get('duration', 0.0) self.playback_manager.set_route_duration(duration) @@ -165,6 +177,56 @@ class MainController: dpg.configure_item("timeline_slider", max_value=duration) + def save_layout_to_yaml(self, filepath: str): + layout_dict = self.layout_manager.to_dict() + with open(filepath, 'w') as f: + yaml.dump(layout_dict, f, default_flow_style=False, sort_keys=False) + + def load_layout_from_yaml(self, filepath: str): + with open(filepath) as f: + layout_dict = yaml.safe_load(f) + self.layout_manager.clear_and_load_from_dict(layout_dict) + self.layout_manager.create_ui("main_plot_area") + + def save_layout_dialog(self): + if dpg.does_item_exist("save_layout_dialog"): + dpg.delete_item("save_layout_dialog") + with dpg.file_dialog( + directory_selector=False, show=True, callback=self._save_layout_callback, + tag="save_layout_dialog", width=int(700 * self.scale), height=int(400 * self.scale), + default_filename="layout", default_path="layouts" + ): + dpg.add_file_extension(".yaml") + + def load_layout_dialog(self): + if dpg.does_item_exist("load_layout_dialog"): + dpg.delete_item("load_layout_dialog") + with dpg.file_dialog( + directory_selector=False, show=True, callback=self._load_layout_callback, + tag="load_layout_dialog", width=int(700 * self.scale), height=int(400 * self.scale), default_path="layouts" + ): + dpg.add_file_extension(".yaml") + + def _save_layout_callback(self, sender, app_data): + filepath = app_data['file_path_name'] + try: + self.save_layout_to_yaml(filepath) + dpg.set_value("load_status", f"Layout saved to {os.path.basename(filepath)}") + except Exception: + dpg.set_value("load_status", "Error saving layout") + cloudlog.exception(f"Error saving layout to {filepath}") + dpg.delete_item("save_layout_dialog") + + def _load_layout_callback(self, sender, app_data): + filepath = app_data['file_path_name'] + try: + self.load_layout_from_yaml(filepath) + dpg.set_value("load_status", f"Layout loaded from {os.path.basename(filepath)}") + except Exception: + dpg.set_value("load_status", "Error loading layout") + cloudlog.exception(f"Error loading layout from {filepath}:") + dpg.delete_item("load_layout_dialog") + def setup_ui(self): with dpg.texture_registry(): script_dir = os.path.dirname(os.path.realpath(__file__)) @@ -175,21 +237,30 @@ class MainController: with dpg.window(tag="Primary Window"): with dpg.group(horizontal=True): # Left panel - Data tree - with dpg.child_window(label="Sidebar", width=300 * self.scale, tag="sidebar_window", border=True, resizable_x=True): + with dpg.child_window(label="Sidebar", width=int(300 * self.scale), tag="sidebar_window", border=True, resizable_x=True): with dpg.group(horizontal=True): - dpg.add_input_text(tag="route_input", width=-75 * self.scale, hint="Enter route name...") + dpg.add_input_text(tag="route_input", width=int(-75 * self.scale), hint="Enter route name...") dpg.add_button(label="Load", callback=self.load_route, tag="load_button", width=-1) dpg.add_text("Ready to load route", tag="load_status") dpg.add_separator() + + with dpg.table(header_row=False, policy=dpg.mvTable_SizingStretchProp): + dpg.add_table_column(init_width_or_weight=0.5) + dpg.add_table_column(init_width_or_weight=0.5) + with dpg.table_row(): + dpg.add_button(label="Save Layout", callback=self.save_layout_dialog, width=-1) + dpg.add_button(label="Load Layout", callback=self.load_layout_dialog, width=-1) + dpg.add_separator() + self.data_tree.create_ui("sidebar_window") # Right panel - Plots and timeline with dpg.group(tag="right_panel"): - with dpg.child_window(label="Plot Window", border=True, height=-(32 + 13 * self.scale), tag="main_plot_area"): + with dpg.child_window(label="Plot Window", border=True, height=int(-(32 + 13 * self.scale)), tag="main_plot_area"): self.layout_manager.create_ui("main_plot_area") with dpg.child_window(label="Timeline", border=True): - with dpg.table(header_row=False, borders_innerH=False, borders_innerV=False, borders_outerH=False, borders_outerV=False): + with dpg.table(header_row=False): btn_size = int(13 * self.scale) dpg.add_table_column(width_fixed=True, init_width_or_weight=(btn_size + 8)) # Play button dpg.add_table_column(width_stretch=True) # Timeline slider diff --git a/tools/jotpluggler/views.py b/tools/jotpluggler/views.py index cb993acf0d..09bba74930 100644 --- a/tools/jotpluggler/views.py +++ b/tools/jotpluggler/views.py @@ -33,6 +33,15 @@ class ViewPanel(ABC): def update(self): pass + @abstractmethod + def to_dict(self) -> dict: + pass + + @classmethod + @abstractmethod + def load_from_dict(cls, data: dict, data_manager, playback_manager, worker_manager): + pass + class TimeSeriesPanel(ViewPanel): def __init__(self, data_manager, playback_manager, worker_manager, panel_id: str | None = None): @@ -55,6 +64,20 @@ class TimeSeriesPanel(ViewPanel): self._queued_x_sync: tuple | None = None self._queued_reallow_x_zoom = False + def to_dict(self) -> dict: + return { + "type": "timeseries", + "title": self.title, + "series_paths": list(self._series_data.keys()) + } + + @classmethod + def load_from_dict(cls, data: dict, data_manager, playback_manager, worker_manager): + panel = cls(data_manager, playback_manager, worker_manager) + panel.title = data.get("title", "Time Series Plot") + panel._series_data = {path: (np.array([]), np.array([])) for path in data.get("series_paths", [])} + return panel + def create_ui(self, parent_tag: str): self.data_manager.add_observer(self.on_data_loaded) self.playback_manager.add_x_axis_observer(self._on_x_axis_sync) @@ -63,7 +86,7 @@ class TimeSeriesPanel(ViewPanel): dpg.add_plot_axis(dpg.mvXAxis, no_label=True, tag=self.x_axis_tag) dpg.add_plot_axis(dpg.mvYAxis, no_label=True, tag=self.y_axis_tag) timeline_series_tag = dpg.add_inf_line_series(x=[0], label="Timeline", parent=self.y_axis_tag, tag=self.timeline_indicator_tag) - dpg.bind_item_theme(timeline_series_tag, "global_timeline_theme") + dpg.bind_item_theme(timeline_series_tag, "timeline_theme") self._new_data = True self._ui_created = True @@ -199,7 +222,7 @@ class TimeSeriesPanel(ViewPanel): dpg.set_value(series_tag, (time_array, value_array.astype(float))) else: line_series_tag = dpg.add_line_series(x=time_array, y=value_array.astype(float), label=series_path, parent=self.y_axis_tag, tag=series_tag) - dpg.bind_item_theme(line_series_tag, "global_line_theme") + dpg.bind_item_theme(line_series_tag, "line_theme") dpg.fit_axis_data(self.x_axis_tag) dpg.fit_axis_data(self.y_axis_tag) plot_duration = dpg.get_axis_limits(self.x_axis_tag)[1] - dpg.get_axis_limits(self.x_axis_tag)[0] From 8d3b919ef68206bc4a123d986407f798e1f4e990 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:28:23 -0700 Subject: [PATCH 010/341] jotpluggler: better defaults for zooming/fitting (#36149) better defaults for zooming/fitting --- tools/jotpluggler/pluggle.py | 11 +++++++++-- tools/jotpluggler/views.py | 32 ++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index 61b0a09718..b831a2f154 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -66,6 +66,7 @@ class PlaybackManager: self.is_playing = False self.current_time_s = 0.0 self.duration_s = 0.0 + self.num_segments = 0 self.x_axis_bounds = (0.0, 0.0) # (min_time, max_time) self.x_axis_observers = [] # callbacks for x-axis changes @@ -131,6 +132,7 @@ class MainController: self.data_tree = DataTree(self.data_manager, self.playback_manager) self.layout_manager = LayoutManager(self.data_manager, self.playback_manager, self.worker_manager, scale=self.scale) self.data_manager.add_observer(self.on_data_loaded) + self._total_segments = 0 def _create_global_themes(self): with dpg.theme(tag="line_theme"): @@ -158,10 +160,15 @@ class MainController: duration = data.get('duration', 0.0) self.playback_manager.set_route_duration(duration) - if data.get('reset'): + if data.get('metadata_loaded'): + self.playback_manager.num_segments = data.get('total_segments', 0) + self._total_segments = data.get('total_segments', 0) + dpg.set_value("load_status", f"Loading... 0/{self._total_segments} segments processed") + elif data.get('reset'): self.playback_manager.current_time_s = 0.0 self.playback_manager.duration_s = 0.0 self.playback_manager.is_playing = False + self._total_segments = 0 dpg.set_value("load_status", "Loading...") dpg.set_value("timeline_slider", 0.0) dpg.configure_item("timeline_slider", max_value=0.0) @@ -173,7 +180,7 @@ class MainController: dpg.configure_item("load_button", enabled=True) elif data.get('segment_added'): segment_count = data.get('segment_count', 0) - dpg.set_value("load_status", f"Loading... {segment_count} segments processed") + dpg.set_value("load_status", f"Loading... {segment_count}/{self._total_segments} segments processed") dpg.configure_item("timeline_slider", max_value=duration) diff --git a/tools/jotpluggler/views.py b/tools/jotpluggler/views.py index 09bba74930..1c4d9a8f3c 100644 --- a/tools/jotpluggler/views.py +++ b/tools/jotpluggler/views.py @@ -63,6 +63,7 @@ class TimeSeriesPanel(ViewPanel): self._last_x_limits = (0.0, 0.0) self._queued_x_sync: tuple | None = None self._queued_reallow_x_zoom = False + self._total_segments = self.playback_manager.num_segments def to_dict(self) -> dict: return { @@ -89,6 +90,7 @@ class TimeSeriesPanel(ViewPanel): dpg.bind_item_theme(timeline_series_tag, "timeline_theme") self._new_data = True + self._queued_x_sync = self.playback_manager.x_axis_bounds self._ui_created = True def update(self): @@ -107,7 +109,19 @@ class TimeSeriesPanel(ViewPanel): if self._queued_reallow_x_zoom: self._queued_reallow_x_zoom = False - dpg.set_axis_limits_auto(self.x_axis_tag) + if tuple(dpg.get_axis_limits(self.x_axis_tag)) == self._last_x_limits: + dpg.set_axis_limits_auto(self.x_axis_tag) + else: + self._queued_x_sync = self._last_x_limits # retry, likely too early + return + + if self._new_data: # handle new data in main thread + self._new_data = False + if self._total_segments > 0: + dpg.set_axis_limits_constraints(self.x_axis_tag, -10, self._total_segments * 60 + 10) + self._fit_y_axis(*dpg.get_axis_limits(self.x_axis_tag)) + for series_path in list(self._series_data.keys()): + self.add_series(series_path, update=True) current_limits = dpg.get_axis_limits(self.x_axis_tag) # downsample if plot zoom changed significantly @@ -120,12 +134,6 @@ class TimeSeriesPanel(ViewPanel): self._last_x_limits = current_limits self._fit_y_axis(current_limits[0], current_limits[1]) - if self._new_data: # handle new data in main thread - self._new_data = False - dpg.set_axis_limits_constraints(self.x_axis_tag, -10, (self.playback_manager.duration_s + 10)) - for series_path in list(self._series_data.keys()): - self.add_series(series_path, update=True) - while self._results_deque: # handle downsampled results in main thread results = self._results_deque.popleft() for series_path, downsampled_time, downsampled_values in results: @@ -223,8 +231,7 @@ class TimeSeriesPanel(ViewPanel): else: line_series_tag = dpg.add_line_series(x=time_array, y=value_array.astype(float), label=series_path, parent=self.y_axis_tag, tag=series_tag) dpg.bind_item_theme(line_series_tag, "line_theme") - dpg.fit_axis_data(self.x_axis_tag) - dpg.fit_axis_data(self.y_axis_tag) + self._fit_y_axis(*dpg.get_axis_limits(self.x_axis_tag)) plot_duration = dpg.get_axis_limits(self.x_axis_tag)[1] - dpg.get_axis_limits(self.x_axis_tag)[0] self._downsample_all_series(plot_duration) @@ -252,7 +259,12 @@ class TimeSeriesPanel(ViewPanel): del self._series_data[series_path] def on_data_loaded(self, data: dict): - self._new_data = True + with self._update_lock: + self._new_data = True + if data.get('metadata_loaded'): + self._total_segments = data.get('total_segments', 0) + limits = (-10, self._total_segments * 60 + 10) + self._queued_x_sync = limits def _on_series_drop(self, sender, app_data, user_data): self.add_series(app_data) From c812c3192d6d24578e4037bd7c9f0d7c446c8c54 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:53:41 -0700 Subject: [PATCH 011/341] jotpluggler: fix hidpi/mac font scaling (#36150) fix hidpi/mac font scaling --- tools/jotpluggler/pluggle.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index b831a2f154..b9cafa60d7 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -324,8 +324,9 @@ def main(route_to_load=None): scale = 1 with dpg.font_registry(): - default_font = dpg.add_font(os.path.join(BASEDIR, "selfdrive/assets/fonts/JetBrainsMono-Medium.ttf"), int(13 * scale)) + default_font = dpg.add_font(os.path.join(BASEDIR, "selfdrive/assets/fonts/JetBrainsMono-Medium.ttf"), int(13 * scale * 2)) # 2x then scale for hidpi dpg.bind_font(default_font) + dpg.set_global_font_scale(0.5) viewport_width, viewport_height = int(1200 * scale), int(800 * scale) mouse_x, mouse_y = pyautogui.position() # TODO: find better way of creating the window where the user is (default dpg behavior annoying on multiple displays) From f18828228ae9ba7fc1aaa807af519f1c920d38d2 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:13:27 -0700 Subject: [PATCH 012/341] jotpluggler: fix layout folder path loading and total segment (#36151) * forgot to commit this earlier with total segments * look in correct directory --- tools/jotpluggler/data.py | 6 ++++++ tools/jotpluggler/pluggle.py | 9 ++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/jotpluggler/data.py b/tools/jotpluggler/data.py index 14d54e6bea..cf27857d1f 100644 --- a/tools/jotpluggler/data.py +++ b/tools/jotpluggler/data.py @@ -314,6 +314,12 @@ class DataManager: cloudlog.warning(f"Warning: No log segments found for route: {route}") return + total_segments = len(lr.logreader_identifiers) + with self._lock: + observers = self._observers.copy() + for callback in observers: + callback({'metadata_loaded': True, 'total_segments': total_segments}) + num_processes = max(1, multiprocessing.cpu_count() // 2) with multiprocessing.Pool(processes=num_processes) as pool, tqdm(total=len(lr.logreader_identifiers), desc="Processing Segments") as pbar: for segment_result, start_time, end_time in pool.imap(_process_segment, lr.logreader_identifiers): diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index b9cafa60d7..986df3d7bf 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -199,9 +199,8 @@ class MainController: if dpg.does_item_exist("save_layout_dialog"): dpg.delete_item("save_layout_dialog") with dpg.file_dialog( - directory_selector=False, show=True, callback=self._save_layout_callback, - tag="save_layout_dialog", width=int(700 * self.scale), height=int(400 * self.scale), - default_filename="layout", default_path="layouts" + callback=self._save_layout_callback, tag="save_layout_dialog", width=int(700 * self.scale), height=int(400 * self.scale), + default_filename="layout", default_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "layouts") ): dpg.add_file_extension(".yaml") @@ -209,8 +208,8 @@ class MainController: if dpg.does_item_exist("load_layout_dialog"): dpg.delete_item("load_layout_dialog") with dpg.file_dialog( - directory_selector=False, show=True, callback=self._load_layout_callback, - tag="load_layout_dialog", width=int(700 * self.scale), height=int(400 * self.scale), default_path="layouts" + callback=self._load_layout_callback, tag="load_layout_dialog", width=int(700 * self.scale), height=int(400 * self.scale), + default_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "layouts") ): dpg.add_file_extension(".yaml") From 3e0dd06374b86c237d7a5e12f6eec600b980d748 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:20:53 -0700 Subject: [PATCH 013/341] jotpluggler: accept --layout argument to pluggle (#36152) accept layouts as arg to pluggle --- tools/jotpluggler/pluggle.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index 986df3d7bf..879b677514 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -312,7 +312,7 @@ class MainController: self.worker_manager.shutdown() -def main(route_to_load=None): +def main(route_to_load=None, layout_to_load=None): dpg.create_context() # TODO: find better way of calculating display scaling @@ -337,6 +337,14 @@ def main(route_to_load=None): controller = MainController(scale=scale) controller.setup_ui() + if layout_to_load: + try: + controller.load_layout_from_yaml(layout_to_load) + print(f"Loaded layout from {layout_to_load}") + except Exception as e: + print(f"Failed to load layout from {layout_to_load}: {e}") + cloudlog.exception(f"Error loading layout from {layout_to_load}") + if route_to_load: dpg.set_value("route_input", route_to_load) controller.load_route() @@ -355,7 +363,8 @@ def main(route_to_load=None): if __name__ == "__main__": parser = argparse.ArgumentParser(description="A tool for visualizing openpilot logs.") parser.add_argument("--demo", action="store_true", help="Use the demo route instead of providing one") + parser.add_argument("--layout", type=str, help="Path to YAML layout file to load on startup") parser.add_argument("route", nargs='?', default=None, help="Optional route name to load on startup.") args = parser.parse_args() route = DEMO_ROUTE if args.demo else args.route - main(route_to_load=route) + main(route_to_load=route, layout_to_load=args.layout) From 04a26ada69c3546bbd850e62b207a81aeb0530fe Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:35:12 -0700 Subject: [PATCH 014/341] jotpluggler: fix bug with char width after scaling text (#36154) fix bug with char width after scaling text --- tools/jotpluggler/datatree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/jotpluggler/datatree.py b/tools/jotpluggler/datatree.py index eb4e4ef584..4f3219dc1b 100644 --- a/tools/jotpluggler/datatree.py +++ b/tools/jotpluggler/datatree.py @@ -67,7 +67,7 @@ class DataTree: with self._ui_lock: if self._char_width is None: if size := dpg.get_text_size(" ", font=font): - self._char_width = size[0] + self._char_width = size[0] / 2 # we scale font 2x and downscale to fix hidpi bug if self._new_data: self._process_path_change() From 98d61982f933b3ef83b81c985e2dcf7d34d89028 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:35:35 -0700 Subject: [PATCH 015/341] jotpluggler: add README (#36153) * add README * fix typo --- tools/jotpluggler/README.md | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tools/jotpluggler/README.md diff --git a/tools/jotpluggler/README.md b/tools/jotpluggler/README.md new file mode 100644 index 0000000000..c5b43dbd3a --- /dev/null +++ b/tools/jotpluggler/README.md @@ -0,0 +1,67 @@ +# JotPluggler + +JotPluggler is a tool to quickly visualize openpilot logs. + +## Usage + +``` +$ ./jotpluggler/pluggle.py -h +usage: pluggle.py [-h] [--demo] [--layout LAYOUT] [route] + +A tool for visualizing openpilot logs. + +positional arguments: + route Optional route name to load on startup. + +options: + -h, --help show this help message and exit + --demo Use the demo route instead of providing one + --layout LAYOUT Path to YAML layout file to load on startup +``` + +Example using route name: + +`./pluggle.py "a2a0ccea32023010/2023-07-27--13-01-19"` + +Examples using segment: + +`./pluggle.py "a2a0ccea32023010/2023-07-27--13-01-19/1"` + +`./pluggle.py "a2a0ccea32023010/2023-07-27--13-01-19/1/q" # use qlogs` + +Example using segment range: + +`./pluggle.py "a2a0ccea32023010/2023-07-27--13-01-19/0:1"` + +## Demo + +For a quick demo, run this command: + +`./pluggle.py --demo --layout=layouts/torque-controller.yaml` + + +## Basic Usage/Features: +- The text box to load a route is a the top left of the page, accepts standard openpilot format routes (e.g. `a2a0ccea32023010/2023-07-27--13-01-19/0:1`, `https://connect.comma.ai/a2a0ccea32023010/2023-07-27--13-01-19/`) +- The Play/Pause button is at the bottom of the screen, you can drag the bottom slider to seek. The timeline in timeseries plots are synced with the slider. +- The Timeseries List sidebar has several dropdowns, the fields each show the field name and value, synced with the timeline (will show N/A until the time of the first message in that field is reached). +- There is a search bar for the timeseries list, you can search for structs or fields, or both by separating with a "/" +- You can drag and drop any numeric/boolean field from the timeseries list into a timeseries panel. +- You can create more panels with the split buttons (buttons with two rectangles, either horizontal or vertical). You can resize the panels by dragging the grip in between any panel. +- You can load and save layouts with the corresponding buttons. Layouts will save all tabs, panels, titles, timeseries, etc. + +## Layouts + +If you create a layout that's useful for others, consider upstreaming it. + +## Plot Interaction Controls + +- **Left click and drag within the plot area** to pan X + - Left click and drag on an axis to pan an individual axis (disabled for Y-axis) +- **Scroll in the plot area** to zoom in X axes, Y-axis is autofit + - Scroll on an axis to zoom an individual axis +- **Right click and drag** to select data and zoom into the selected data + - Left click while box selecting to cancel the selection +- **Double left click** to fit all visible data + - Double left click on an axis to fit the individual axis (disabled for Y-axis, always autofit) +- **Double right click** to open the plot context menu +- **Click legend label icons** to show/hide plot items From eb821ceb5c6835a560b51ab7850a87f40c5fb22a Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Sat, 13 Sep 2025 11:48:35 -0700 Subject: [PATCH 016/341] [bot] Update Python packages (#36118) * Update Python packages * revert tinygrad * can cnt * bump panda * bump panda * update panda test * revert that --------- Co-authored-by: Vehicle Researcher Co-authored-by: Adeeb Shihadeh --- docs/CARS.md | 8 ++++---- msgq_repo | 2 +- opendbc_repo | 2 +- panda | 2 +- selfdrive/pandad/pandad.cc | 2 +- selfdrive/pandad/tests/test_pandad.py | 27 ++++++++------------------- 6 files changed, 16 insertions(+), 27 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index bd6a9c920c..7d3f7a9442 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -246,10 +246,10 @@ A supported vehicle is one that just works when you install a comma device. All |Škoda|Octavia Scout 2017-19[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Škoda|Scala 2020-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)||| |Škoda|Superb 2015-22[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Tesla[11](#footnotes)|Model 3 (with HW3) 2019-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Tesla[11](#footnotes)|Model 3 (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Tesla[11](#footnotes)|Model Y (with HW3) 2020-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Tesla[11](#footnotes)|Model Y (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Tesla[11](#footnotes)|Model 3 (with HW3) 2019-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Tesla[11](#footnotes)|Model 3 (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Tesla[11](#footnotes)|Model Y (with HW3) 2020-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Tesla[11](#footnotes)|Model Y (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/msgq_repo b/msgq_repo index 5483a02de3..89096d90d2 160000 --- a/msgq_repo +++ b/msgq_repo @@ -1 +1 @@ -Subproject commit 5483a02de303d40cb2632d59f3f3a54dabfb5965 +Subproject commit 89096d90d2f0f71be63a4af0152fe3b2aa55cf9d diff --git a/opendbc_repo b/opendbc_repo index 4170d7d876..c2ba07083c 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 4170d7d876a87904dab4b351aa8139ec3d400430 +Subproject commit c2ba07083c1cfbb0c5b86469bd7b87090291a0d3 diff --git a/panda b/panda index 819fa5854e..a2064b86f3 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 819fa5854e2e75da7f982f7d06be69c61793d6e1 +Subproject commit a2064b86f3c9908883033a953503f150cedacbc7 diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc index 8289df5491..2931eb4acd 100644 --- a/selfdrive/pandad/pandad.cc +++ b/selfdrive/pandad/pandad.cc @@ -66,7 +66,7 @@ Panda *connect(std::string serial="", uint32_t index=0) { } //panda->enable_deepsleep(); - for (int i = 0; i < PANDA_BUS_CNT; i++) { + for (int i = 0; i < PANDA_CAN_CNT; i++) { panda->set_can_fd_auto(i, true); } diff --git a/selfdrive/pandad/tests/test_pandad.py b/selfdrive/pandad/tests/test_pandad.py index 6a7359fd85..88d3939a6a 100644 --- a/selfdrive/pandad/tests/test_pandad.py +++ b/selfdrive/pandad/tests/test_pandad.py @@ -5,8 +5,7 @@ import time import cereal.messaging as messaging from cereal import log from openpilot.common.gpio import gpio_set, gpio_init -from panda import Panda, PandaDFU, PandaProtocolMismatch -from openpilot.common.retry import retry +from panda import Panda, PandaDFU from openpilot.system.manager.process_config import managed_processes from openpilot.system.hardware import HARDWARE from openpilot.system.hardware.tici.pins import GPIO @@ -50,8 +49,7 @@ class TestPandad: assert not Panda.wait_for_dfu(None, 3) assert not Panda.wait_for_panda(None, 3) - @retry(attempts=3) - def _flash_bootstub_and_test(self, fn, expect_mismatch=False): + def _flash_bootstub(self, fn): self._go_to_dfu() pd = PandaDFU(None) if fn is None: @@ -61,16 +59,6 @@ class TestPandad: pd.reset() HARDWARE.reset_internal_panda() - assert Panda.wait_for_panda(None, 10) - if expect_mismatch: - with pytest.raises(PandaProtocolMismatch): - Panda() - else: - with Panda() as p: - assert p.bootstub - - self._run_test(45) - def test_in_dfu(self): HARDWARE.recover_internal_panda() self._run_test(60) @@ -106,13 +94,14 @@ class TestPandad: print("startup times", ts, sum(ts) / len(ts)) assert 0.1 < (sum(ts)/len(ts)) < 0.7 - def test_protocol_version_check(self): - # flash old fw - fn = os.path.join(HERE, "bootstub.panda_h7_spiv0.bin") - self._flash_bootstub_and_test(fn, expect_mismatch=True) + def test_old_spi_protocol(self): + # flash firmware with old SPI protocol + self._flash_bootstub(os.path.join(HERE, "bootstub.panda_h7_spiv0.bin")) + self._run_test(45) def test_release_to_devel_bootstub(self): - self._flash_bootstub_and_test(None) + self._flash_bootstub(None) + self._run_test(45) def test_recover_from_bad_bootstub(self): self._go_to_dfu() From a6adedf6e0ad6c33374ca4d64e25935566d763a3 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 13 Sep 2025 11:58:49 -0700 Subject: [PATCH 017/341] prep for python pandad (#36155) --- system/hardware/base.py | 9 +++++++++ system/hardware/tici/hardware.py | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/system/hardware/base.py b/system/hardware/base.py index ce97bf294d..17d0ec1614 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -232,3 +232,12 @@ class HardwareBase(ABC): def get_modem_data_usage(self): return -1, -1 + + def get_voltage(self) -> float: + return 0. + + def get_current(self) -> float: + return 0. + + def set_ir_power(self, percent: int): + pass diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 0f50acdc38..36e65ad91c 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -116,6 +116,26 @@ class Tici(HardwareBase): def get_serial(self): return self.get_cmdline()['androidboot.serialno'] + def get_voltage(self): + with open("/sys/class/hwmon/hwmon1/in1_input") as f: + return int(f.read()) + + def get_current(self): + with open("/sys/class/hwmon/hwmon1/curr1_input") as f: + return int(f.read()) + + def set_ir_power(self, percent: int): + if self.get_device_type() in ("tici", "tizi"): + return + + value = int((percent / 100) * 300) + with open("/sys/class/leds/led:switch_2/brightness", "w") as f: + f.write("0\n") + with open("/sys/class/leds/led:torch_2/brightness", "w") as f: + f.write(f"{value}\n") + with open("/sys/class/leds/led:switch_2/brightness", "w") as f: + f.write(f"{value}\n") + def get_network_type(self): try: primary_connection = self.nm.Get(NM, 'PrimaryConnection', dbus_interface=DBUS_PROPS, timeout=TIMEOUT) From 96c00271e335a5c451f44185a38976c2f5695fed Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:37:52 -0400 Subject: [PATCH 018/341] pin pycapnp (#36160) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1655b565e2..489876f78c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ # core "cffi", "scons", - "pycapnp", + "pycapnp==2.1.0", "Cython", "setuptools", "numpy >=2.0", From 889ce4c4fb6ac189817786f5ec4b2584397f25df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Mon, 15 Sep 2025 23:04:33 +0200 Subject: [PATCH 019/341] torqued: add DEBUG flag (#36161) Add a debug flag to torqued --- selfdrive/locationd/torqued.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 71b5291dfb..3f9b846e82 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import os import numpy as np from collections import deque, defaultdict @@ -242,6 +243,8 @@ class TorqueEstimator(ParameterEstimator): def main(demo=False): config_realtime_process([0, 1, 2, 3], 5) + DEBUG = bool(int(os.getenv("DEBUG", "0"))) + pm = messaging.PubMaster(['liveTorqueParameters']) sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose', 'liveDelay'], poll='livePose') @@ -258,7 +261,7 @@ def main(demo=False): # 4Hz driven by livePose if sm.frame % 5 == 0: - pm.send('liveTorqueParameters', estimator.get_msg(valid=sm.all_checks())) + pm.send('liveTorqueParameters', estimator.get_msg(valid=sm.all_checks(), with_points=DEBUG)) # Cache points every 60 seconds while onroad if sm.frame % 240 == 0: From 086e33dd6e7047da3af092fd3c322e8c63b86571 Mon Sep 17 00:00:00 2001 From: Mitchell Goff Date: Tue, 16 Sep 2025 14:25:18 -0700 Subject: [PATCH 020/341] Revert "minimal ffmpeg build (#36138)" This reverts commit 347b23055d03e3af878d7abbd04ca3296f5c1cc2. --- .gitattributes | 1 - SConstruct | 4 - system/loggerd/SConscript | 4 +- system/loggerd/tests/test_loggerd.py | 14 +--- third_party/.gitignore | 2 - third_party/ffmpeg/Darwin/lib/libavcodec.a | 3 - third_party/ffmpeg/Darwin/lib/libavformat.a | 3 - third_party/ffmpeg/Darwin/lib/libavutil.a | 3 - third_party/ffmpeg/Darwin/lib/libx264.a | 3 - third_party/ffmpeg/build.sh | 83 ------------------- .../ffmpeg/include/libavcodec/ac3_parser.h | 3 - .../ffmpeg/include/libavcodec/adts_parser.h | 3 - .../ffmpeg/include/libavcodec/avcodec.h | 3 - third_party/ffmpeg/include/libavcodec/avdct.h | 3 - third_party/ffmpeg/include/libavcodec/avfft.h | 3 - third_party/ffmpeg/include/libavcodec/bsf.h | 3 - third_party/ffmpeg/include/libavcodec/codec.h | 3 - .../ffmpeg/include/libavcodec/codec_desc.h | 3 - .../ffmpeg/include/libavcodec/codec_id.h | 3 - .../ffmpeg/include/libavcodec/codec_par.h | 3 - .../ffmpeg/include/libavcodec/d3d11va.h | 3 - third_party/ffmpeg/include/libavcodec/defs.h | 3 - third_party/ffmpeg/include/libavcodec/dirac.h | 3 - .../ffmpeg/include/libavcodec/dv_profile.h | 3 - third_party/ffmpeg/include/libavcodec/dxva2.h | 3 - third_party/ffmpeg/include/libavcodec/jni.h | 3 - .../ffmpeg/include/libavcodec/mediacodec.h | 3 - .../ffmpeg/include/libavcodec/packet.h | 3 - third_party/ffmpeg/include/libavcodec/qsv.h | 3 - third_party/ffmpeg/include/libavcodec/vdpau.h | 3 - .../ffmpeg/include/libavcodec/version.h | 3 - .../ffmpeg/include/libavcodec/version_major.h | 3 - .../ffmpeg/include/libavcodec/videotoolbox.h | 3 - .../ffmpeg/include/libavcodec/vorbis_parser.h | 3 - third_party/ffmpeg/include/libavcodec/xvmc.h | 3 - .../ffmpeg/include/libavformat/avformat.h | 3 - third_party/ffmpeg/include/libavformat/avio.h | 3 - .../ffmpeg/include/libavformat/version.h | 3 - .../include/libavformat/version_major.h | 3 - .../ffmpeg/include/libavutil/adler32.h | 3 - third_party/ffmpeg/include/libavutil/aes.h | 3 - .../ffmpeg/include/libavutil/aes_ctr.h | 3 - .../libavutil/ambient_viewing_environment.h | 3 - .../ffmpeg/include/libavutil/attributes.h | 3 - .../ffmpeg/include/libavutil/audio_fifo.h | 3 - .../ffmpeg/include/libavutil/avassert.h | 3 - .../ffmpeg/include/libavutil/avconfig.h | 3 - .../ffmpeg/include/libavutil/avstring.h | 3 - third_party/ffmpeg/include/libavutil/avutil.h | 3 - third_party/ffmpeg/include/libavutil/base64.h | 3 - .../ffmpeg/include/libavutil/blowfish.h | 3 - third_party/ffmpeg/include/libavutil/bprint.h | 3 - third_party/ffmpeg/include/libavutil/bswap.h | 3 - third_party/ffmpeg/include/libavutil/buffer.h | 3 - .../ffmpeg/include/libavutil/camellia.h | 3 - third_party/ffmpeg/include/libavutil/cast5.h | 3 - .../ffmpeg/include/libavutil/channel_layout.h | 3 - third_party/ffmpeg/include/libavutil/common.h | 3 - third_party/ffmpeg/include/libavutil/cpu.h | 3 - third_party/ffmpeg/include/libavutil/crc.h | 3 - third_party/ffmpeg/include/libavutil/csp.h | 3 - third_party/ffmpeg/include/libavutil/des.h | 3 - .../ffmpeg/include/libavutil/detection_bbox.h | 3 - third_party/ffmpeg/include/libavutil/dict.h | 3 - .../ffmpeg/include/libavutil/display.h | 3 - .../ffmpeg/include/libavutil/dovi_meta.h | 3 - .../ffmpeg/include/libavutil/downmix_info.h | 3 - .../include/libavutil/encryption_info.h | 3 - third_party/ffmpeg/include/libavutil/error.h | 3 - third_party/ffmpeg/include/libavutil/eval.h | 3 - .../ffmpeg/include/libavutil/executor.h | 3 - .../ffmpeg/include/libavutil/ffversion.h | 3 - third_party/ffmpeg/include/libavutil/fifo.h | 3 - third_party/ffmpeg/include/libavutil/file.h | 3 - .../include/libavutil/film_grain_params.h | 3 - third_party/ffmpeg/include/libavutil/frame.h | 3 - third_party/ffmpeg/include/libavutil/hash.h | 3 - .../include/libavutil/hdr_dynamic_metadata.h | 3 - .../libavutil/hdr_dynamic_vivid_metadata.h | 3 - third_party/ffmpeg/include/libavutil/hmac.h | 3 - .../ffmpeg/include/libavutil/hwcontext.h | 3 - .../ffmpeg/include/libavutil/hwcontext_cuda.h | 3 - .../include/libavutil/hwcontext_d3d11va.h | 3 - .../ffmpeg/include/libavutil/hwcontext_drm.h | 3 - .../include/libavutil/hwcontext_dxva2.h | 3 - .../include/libavutil/hwcontext_mediacodec.h | 3 - .../include/libavutil/hwcontext_opencl.h | 3 - .../ffmpeg/include/libavutil/hwcontext_qsv.h | 3 - .../include/libavutil/hwcontext_vaapi.h | 3 - .../include/libavutil/hwcontext_vdpau.h | 3 - .../libavutil/hwcontext_videotoolbox.h | 3 - .../include/libavutil/hwcontext_vulkan.h | 3 - .../ffmpeg/include/libavutil/imgutils.h | 3 - .../ffmpeg/include/libavutil/intfloat.h | 3 - .../ffmpeg/include/libavutil/intreadwrite.h | 3 - third_party/ffmpeg/include/libavutil/lfg.h | 3 - third_party/ffmpeg/include/libavutil/log.h | 3 - third_party/ffmpeg/include/libavutil/lzo.h | 3 - third_party/ffmpeg/include/libavutil/macros.h | 3 - .../libavutil/mastering_display_metadata.h | 3 - .../ffmpeg/include/libavutil/mathematics.h | 3 - third_party/ffmpeg/include/libavutil/md5.h | 3 - third_party/ffmpeg/include/libavutil/mem.h | 3 - .../ffmpeg/include/libavutil/motion_vector.h | 3 - .../ffmpeg/include/libavutil/murmur3.h | 3 - third_party/ffmpeg/include/libavutil/opt.h | 3 - .../ffmpeg/include/libavutil/parseutils.h | 3 - .../ffmpeg/include/libavutil/pixdesc.h | 3 - .../ffmpeg/include/libavutil/pixelutils.h | 3 - third_party/ffmpeg/include/libavutil/pixfmt.h | 3 - .../ffmpeg/include/libavutil/random_seed.h | 3 - .../ffmpeg/include/libavutil/rational.h | 3 - third_party/ffmpeg/include/libavutil/rc4.h | 3 - .../ffmpeg/include/libavutil/replaygain.h | 3 - third_party/ffmpeg/include/libavutil/ripemd.h | 3 - .../ffmpeg/include/libavutil/samplefmt.h | 3 - third_party/ffmpeg/include/libavutil/sha.h | 3 - third_party/ffmpeg/include/libavutil/sha512.h | 3 - .../ffmpeg/include/libavutil/spherical.h | 3 - .../ffmpeg/include/libavutil/stereo3d.h | 3 - third_party/ffmpeg/include/libavutil/tea.h | 3 - .../ffmpeg/include/libavutil/threadmessage.h | 3 - third_party/ffmpeg/include/libavutil/time.h | 3 - .../ffmpeg/include/libavutil/timecode.h | 3 - .../ffmpeg/include/libavutil/timestamp.h | 3 - third_party/ffmpeg/include/libavutil/tree.h | 3 - .../ffmpeg/include/libavutil/twofish.h | 3 - third_party/ffmpeg/include/libavutil/tx.h | 3 - third_party/ffmpeg/include/libavutil/uuid.h | 3 - .../ffmpeg/include/libavutil/version.h | 3 - .../include/libavutil/video_enc_params.h | 3 - .../ffmpeg/include/libavutil/video_hint.h | 3 - third_party/ffmpeg/include/libavutil/xtea.h | 3 - third_party/ffmpeg/include/x264.h | 3 - third_party/ffmpeg/include/x264_config.h | 3 - third_party/ffmpeg/larch64/lib/libavcodec.a | 3 - third_party/ffmpeg/larch64/lib/libavformat.a | 3 - third_party/ffmpeg/larch64/lib/libavutil.a | 3 - third_party/ffmpeg/larch64/lib/libx264.a | 3 - third_party/ffmpeg/x86_64/lib/libavcodec.a | 3 - third_party/ffmpeg/x86_64/lib/libavformat.a | 3 - third_party/ffmpeg/x86_64/lib/libavutil.a | 3 - third_party/ffmpeg/x86_64/lib/libx264.a | 3 - tools/cabana/SConscript | 2 +- tools/install_ubuntu_dependencies.sh | 6 ++ tools/mac_setup.sh | 1 + tools/replay/SConscript | 2 +- 147 files changed, 13 insertions(+), 517 deletions(-) delete mode 100644 third_party/ffmpeg/Darwin/lib/libavcodec.a delete mode 100644 third_party/ffmpeg/Darwin/lib/libavformat.a delete mode 100644 third_party/ffmpeg/Darwin/lib/libavutil.a delete mode 100644 third_party/ffmpeg/Darwin/lib/libx264.a delete mode 100755 third_party/ffmpeg/build.sh delete mode 100644 third_party/ffmpeg/include/libavcodec/ac3_parser.h delete mode 100644 third_party/ffmpeg/include/libavcodec/adts_parser.h delete mode 100644 third_party/ffmpeg/include/libavcodec/avcodec.h delete mode 100644 third_party/ffmpeg/include/libavcodec/avdct.h delete mode 100644 third_party/ffmpeg/include/libavcodec/avfft.h delete mode 100644 third_party/ffmpeg/include/libavcodec/bsf.h delete mode 100644 third_party/ffmpeg/include/libavcodec/codec.h delete mode 100644 third_party/ffmpeg/include/libavcodec/codec_desc.h delete mode 100644 third_party/ffmpeg/include/libavcodec/codec_id.h delete mode 100644 third_party/ffmpeg/include/libavcodec/codec_par.h delete mode 100644 third_party/ffmpeg/include/libavcodec/d3d11va.h delete mode 100644 third_party/ffmpeg/include/libavcodec/defs.h delete mode 100644 third_party/ffmpeg/include/libavcodec/dirac.h delete mode 100644 third_party/ffmpeg/include/libavcodec/dv_profile.h delete mode 100644 third_party/ffmpeg/include/libavcodec/dxva2.h delete mode 100644 third_party/ffmpeg/include/libavcodec/jni.h delete mode 100644 third_party/ffmpeg/include/libavcodec/mediacodec.h delete mode 100644 third_party/ffmpeg/include/libavcodec/packet.h delete mode 100644 third_party/ffmpeg/include/libavcodec/qsv.h delete mode 100644 third_party/ffmpeg/include/libavcodec/vdpau.h delete mode 100644 third_party/ffmpeg/include/libavcodec/version.h delete mode 100644 third_party/ffmpeg/include/libavcodec/version_major.h delete mode 100644 third_party/ffmpeg/include/libavcodec/videotoolbox.h delete mode 100644 third_party/ffmpeg/include/libavcodec/vorbis_parser.h delete mode 100644 third_party/ffmpeg/include/libavcodec/xvmc.h delete mode 100644 third_party/ffmpeg/include/libavformat/avformat.h delete mode 100644 third_party/ffmpeg/include/libavformat/avio.h delete mode 100644 third_party/ffmpeg/include/libavformat/version.h delete mode 100644 third_party/ffmpeg/include/libavformat/version_major.h delete mode 100644 third_party/ffmpeg/include/libavutil/adler32.h delete mode 100644 third_party/ffmpeg/include/libavutil/aes.h delete mode 100644 third_party/ffmpeg/include/libavutil/aes_ctr.h delete mode 100644 third_party/ffmpeg/include/libavutil/ambient_viewing_environment.h delete mode 100644 third_party/ffmpeg/include/libavutil/attributes.h delete mode 100644 third_party/ffmpeg/include/libavutil/audio_fifo.h delete mode 100644 third_party/ffmpeg/include/libavutil/avassert.h delete mode 100644 third_party/ffmpeg/include/libavutil/avconfig.h delete mode 100644 third_party/ffmpeg/include/libavutil/avstring.h delete mode 100644 third_party/ffmpeg/include/libavutil/avutil.h delete mode 100644 third_party/ffmpeg/include/libavutil/base64.h delete mode 100644 third_party/ffmpeg/include/libavutil/blowfish.h delete mode 100644 third_party/ffmpeg/include/libavutil/bprint.h delete mode 100644 third_party/ffmpeg/include/libavutil/bswap.h delete mode 100644 third_party/ffmpeg/include/libavutil/buffer.h delete mode 100644 third_party/ffmpeg/include/libavutil/camellia.h delete mode 100644 third_party/ffmpeg/include/libavutil/cast5.h delete mode 100644 third_party/ffmpeg/include/libavutil/channel_layout.h delete mode 100644 third_party/ffmpeg/include/libavutil/common.h delete mode 100644 third_party/ffmpeg/include/libavutil/cpu.h delete mode 100644 third_party/ffmpeg/include/libavutil/crc.h delete mode 100644 third_party/ffmpeg/include/libavutil/csp.h delete mode 100644 third_party/ffmpeg/include/libavutil/des.h delete mode 100644 third_party/ffmpeg/include/libavutil/detection_bbox.h delete mode 100644 third_party/ffmpeg/include/libavutil/dict.h delete mode 100644 third_party/ffmpeg/include/libavutil/display.h delete mode 100644 third_party/ffmpeg/include/libavutil/dovi_meta.h delete mode 100644 third_party/ffmpeg/include/libavutil/downmix_info.h delete mode 100644 third_party/ffmpeg/include/libavutil/encryption_info.h delete mode 100644 third_party/ffmpeg/include/libavutil/error.h delete mode 100644 third_party/ffmpeg/include/libavutil/eval.h delete mode 100644 third_party/ffmpeg/include/libavutil/executor.h delete mode 100644 third_party/ffmpeg/include/libavutil/ffversion.h delete mode 100644 third_party/ffmpeg/include/libavutil/fifo.h delete mode 100644 third_party/ffmpeg/include/libavutil/file.h delete mode 100644 third_party/ffmpeg/include/libavutil/film_grain_params.h delete mode 100644 third_party/ffmpeg/include/libavutil/frame.h delete mode 100644 third_party/ffmpeg/include/libavutil/hash.h delete mode 100644 third_party/ffmpeg/include/libavutil/hdr_dynamic_metadata.h delete mode 100644 third_party/ffmpeg/include/libavutil/hdr_dynamic_vivid_metadata.h delete mode 100644 third_party/ffmpeg/include/libavutil/hmac.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_cuda.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_d3d11va.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_drm.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_dxva2.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_mediacodec.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_opencl.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_qsv.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_vaapi.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_vdpau.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_videotoolbox.h delete mode 100644 third_party/ffmpeg/include/libavutil/hwcontext_vulkan.h delete mode 100644 third_party/ffmpeg/include/libavutil/imgutils.h delete mode 100644 third_party/ffmpeg/include/libavutil/intfloat.h delete mode 100644 third_party/ffmpeg/include/libavutil/intreadwrite.h delete mode 100644 third_party/ffmpeg/include/libavutil/lfg.h delete mode 100644 third_party/ffmpeg/include/libavutil/log.h delete mode 100644 third_party/ffmpeg/include/libavutil/lzo.h delete mode 100644 third_party/ffmpeg/include/libavutil/macros.h delete mode 100644 third_party/ffmpeg/include/libavutil/mastering_display_metadata.h delete mode 100644 third_party/ffmpeg/include/libavutil/mathematics.h delete mode 100644 third_party/ffmpeg/include/libavutil/md5.h delete mode 100644 third_party/ffmpeg/include/libavutil/mem.h delete mode 100644 third_party/ffmpeg/include/libavutil/motion_vector.h delete mode 100644 third_party/ffmpeg/include/libavutil/murmur3.h delete mode 100644 third_party/ffmpeg/include/libavutil/opt.h delete mode 100644 third_party/ffmpeg/include/libavutil/parseutils.h delete mode 100644 third_party/ffmpeg/include/libavutil/pixdesc.h delete mode 100644 third_party/ffmpeg/include/libavutil/pixelutils.h delete mode 100644 third_party/ffmpeg/include/libavutil/pixfmt.h delete mode 100644 third_party/ffmpeg/include/libavutil/random_seed.h delete mode 100644 third_party/ffmpeg/include/libavutil/rational.h delete mode 100644 third_party/ffmpeg/include/libavutil/rc4.h delete mode 100644 third_party/ffmpeg/include/libavutil/replaygain.h delete mode 100644 third_party/ffmpeg/include/libavutil/ripemd.h delete mode 100644 third_party/ffmpeg/include/libavutil/samplefmt.h delete mode 100644 third_party/ffmpeg/include/libavutil/sha.h delete mode 100644 third_party/ffmpeg/include/libavutil/sha512.h delete mode 100644 third_party/ffmpeg/include/libavutil/spherical.h delete mode 100644 third_party/ffmpeg/include/libavutil/stereo3d.h delete mode 100644 third_party/ffmpeg/include/libavutil/tea.h delete mode 100644 third_party/ffmpeg/include/libavutil/threadmessage.h delete mode 100644 third_party/ffmpeg/include/libavutil/time.h delete mode 100644 third_party/ffmpeg/include/libavutil/timecode.h delete mode 100644 third_party/ffmpeg/include/libavutil/timestamp.h delete mode 100644 third_party/ffmpeg/include/libavutil/tree.h delete mode 100644 third_party/ffmpeg/include/libavutil/twofish.h delete mode 100644 third_party/ffmpeg/include/libavutil/tx.h delete mode 100644 third_party/ffmpeg/include/libavutil/uuid.h delete mode 100644 third_party/ffmpeg/include/libavutil/version.h delete mode 100644 third_party/ffmpeg/include/libavutil/video_enc_params.h delete mode 100644 third_party/ffmpeg/include/libavutil/video_hint.h delete mode 100644 third_party/ffmpeg/include/libavutil/xtea.h delete mode 100644 third_party/ffmpeg/include/x264.h delete mode 100644 third_party/ffmpeg/include/x264_config.h delete mode 100644 third_party/ffmpeg/larch64/lib/libavcodec.a delete mode 100644 third_party/ffmpeg/larch64/lib/libavformat.a delete mode 100644 third_party/ffmpeg/larch64/lib/libavutil.a delete mode 100644 third_party/ffmpeg/larch64/lib/libx264.a delete mode 100644 third_party/ffmpeg/x86_64/lib/libavcodec.a delete mode 100644 third_party/ffmpeg/x86_64/lib/libavformat.a delete mode 100644 third_party/ffmpeg/x86_64/lib/libavutil.a delete mode 100644 third_party/ffmpeg/x86_64/lib/libx264.a diff --git a/.gitattributes b/.gitattributes index 152dbfe456..cc1605a132 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,7 +15,6 @@ third_party/**/*.a filter=lfs diff=lfs merge=lfs -text third_party/**/*.so filter=lfs diff=lfs merge=lfs -text third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text third_party/**/*.dylib filter=lfs diff=lfs merge=lfs -text -third_party/ffmpeg/include/**/*.h filter=lfs diff=lfs merge=lfs -text third_party/acados/*/t_renderer filter=lfs diff=lfs merge=lfs -text third_party/qt5/larch64/bin/lrelease filter=lfs diff=lfs merge=lfs -text third_party/qt5/larch64/bin/lupdate filter=lfs diff=lfs merge=lfs -text diff --git a/SConstruct b/SConstruct index a788b9a290..5b13bd635a 100644 --- a/SConstruct +++ b/SConstruct @@ -91,7 +91,6 @@ if arch == "larch64": ] libpath = [ - f"#third_party/ffmpeg/{arch}/lib", "/usr/local/lib", "/system/vendor/lib64", f"#third_party/acados/{arch}/lib", @@ -115,7 +114,6 @@ else: libpath = [ f"#third_party/libyuv/{arch}/lib", f"#third_party/acados/{arch}/lib", - f"#third_party/ffmpeg/{arch}/lib", f"{brew_prefix}/lib", f"{brew_prefix}/opt/openssl@3.0/lib", "/System/Library/Frameworks/OpenGL.framework/Libraries", @@ -132,7 +130,6 @@ else: libpath = [ f"#third_party/acados/{arch}/lib", f"#third_party/libyuv/{arch}/lib", - f"#third_party/ffmpeg/{arch}/lib", "/usr/lib", "/usr/local/lib", ] @@ -180,7 +177,6 @@ env = Environment( "#third_party/libyuv/include", "#third_party/json11", "#third_party/linux/include", - "#third_party/ffmpeg/include", "#third_party", "#msgq", ], diff --git a/system/loggerd/SConscript b/system/loggerd/SConscript index c87db4232b..cf169f4dc6 100644 --- a/system/loggerd/SConscript +++ b/system/loggerd/SConscript @@ -1,8 +1,8 @@ Import('env', 'arch', 'messaging', 'common', 'visionipc') libs = [common, messaging, visionipc, - 'yuv', 'OpenCL', 'pthread', 'zstd', - 'avformat', 'avcodec', 'avutil', 'x264'] + 'avformat', 'avcodec', 'avutil', + 'yuv', 'OpenCL', 'pthread', 'zstd'] src = ['logger.cc', 'zstd_writer.cc', 'video_writer.cc', 'encoder/encoder.cc', 'encoder/v4l_encoder.cc', 'encoder/jpeg_encoder.cc'] if arch != "larch64": diff --git a/system/loggerd/tests/test_loggerd.py b/system/loggerd/tests/test_loggerd.py index ae7196684d..c6a4b12e63 100644 --- a/system/loggerd/tests/test_loggerd.py +++ b/system/loggerd/tests/test_loggerd.py @@ -316,18 +316,8 @@ class TestLoggerd: self._publish_camera_and_audio_messages() qcamera_ts_path = os.path.join(self._get_latest_log_dir(), 'qcamera.ts') - - # simplest heuristic: look for AAC ADTS syncwords in the TS file - def ts_has_audio_stream(ts_path: str) -> bool: - try: - with open(ts_path, 'rb') as f: - data = f.read() - # ADTS headers typically start with 0xFFF1 or 0xFFF9 - return (b"\xFF\xF1" in data) or (b"\xFF\xF9" in data) - except Exception: - return False - - has_audio_stream = ts_has_audio_stream(qcamera_ts_path) + ffprobe_cmd = f"ffprobe -i {qcamera_ts_path} -show_streams -select_streams a -loglevel error" + has_audio_stream = subprocess.run(ffprobe_cmd, shell=True, capture_output=True).stdout.strip() != b'' assert has_audio_stream == record_audio raw_audio_in_rlog = any(m.which() == 'rawAudioData' for m in LogReader(os.path.join(self._get_latest_log_dir(), 'rlog.zst'))) diff --git a/third_party/.gitignore b/third_party/.gitignore index 0d5b3d2213..0d20b6487c 100644 --- a/third_party/.gitignore +++ b/third_party/.gitignore @@ -1,3 +1 @@ *.pyc -src/ -build/ diff --git a/third_party/ffmpeg/Darwin/lib/libavcodec.a b/third_party/ffmpeg/Darwin/lib/libavcodec.a deleted file mode 100644 index a87d3323ce..0000000000 --- a/third_party/ffmpeg/Darwin/lib/libavcodec.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2dcb729a6833558fc0e01abe5e728eba2b3c215abc7dece74d5ce1bc1d279e7d -size 2328832 diff --git a/third_party/ffmpeg/Darwin/lib/libavformat.a b/third_party/ffmpeg/Darwin/lib/libavformat.a deleted file mode 100644 index b7c48f7961..0000000000 --- a/third_party/ffmpeg/Darwin/lib/libavformat.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:825f145f7168226deae199538113f063e6b71a02999b297c75254dfa466b73cb -size 863544 diff --git a/third_party/ffmpeg/Darwin/lib/libavutil.a b/third_party/ffmpeg/Darwin/lib/libavutil.a deleted file mode 100644 index 17985d73b6..0000000000 --- a/third_party/ffmpeg/Darwin/lib/libavutil.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9c13c341440029d06a1044479876843bc93615849554dc636e386f31f6776850 -size 789024 diff --git a/third_party/ffmpeg/Darwin/lib/libx264.a b/third_party/ffmpeg/Darwin/lib/libx264.a deleted file mode 100644 index 27375793a9..0000000000 --- a/third_party/ffmpeg/Darwin/lib/libx264.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8bedf59051187092bd9f282acb7f22f7885ed41b2f89692e0478252329bd7e4b -size 1941808 diff --git a/third_party/ffmpeg/build.sh b/third_party/ffmpeg/build.sh deleted file mode 100755 index f8812d6f17..0000000000 --- a/third_party/ffmpeg/build.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash -set -e - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" -cd $DIR - -# Detect arch -ARCHNAME="x86_64" -if [ -f /TICI ]; then - ARCHNAME="larch64" -fi -if [[ "$OSTYPE" == "darwin"* ]]; then - ARCHNAME="Darwin" -fi - -VERSION="6.1.1" # LTS -PREFIX="$DIR/$ARCHNAME" -BUILD_DIR="$DIR/build/" - -mkdir -p "$BUILD_DIR" -rm -rf include/ && mkdir -p include/ -rm -rf "$PREFIX" && mkdir -p "$PREFIX" - -# *** build x264 *** -if [[ ! -d "$DIR/src/x264/" ]]; then - # TODO: pin to a commit - git clone --depth=1 --branch "stable" https://code.videolan.org/videolan/x264.git "$DIR/src/x264/" -fi -cd $DIR/src/x264 -git fetch origin b35605ace3ddf7c1a5d67a2eb553f034aef41d55 -git checkout -f FETCH_HEAD -./configure --prefix="$PREFIX" --enable-static --disable-opencl --enable-pic --disable-cli -make -j8 -make install -cp -a "$PREFIX/include/." "$DIR/include/" - -# *** build ffmpeg *** -mkdir -p "$DIR/src" -if [[ ! -d "$DIR/src/ffmpeg-$VERSION" ]]; then - echo "Downloading FFmpeg $VERSION ..." - curl -L "https://ffmpeg.org/releases/ffmpeg-${VERSION}.tar.xz" -o "$DIR/src/ffmpeg-${VERSION}.tar.xz" - tar -C "$DIR/src" -xf "$DIR/src/ffmpeg-${VERSION}.tar.xz" -fi - -cd $BUILD_DIR - -export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" -export EXTRA_CFLAGS="-I$PREFIX/include ${EXTRA_CFLAGS:-}" -export EXTRA_LDFLAGS="-L$PREFIX/lib ${EXTRA_LDFLAGS:-}" -# Configure minimal static FFmpeg for desktop Linux tools -"$DIR/src/ffmpeg-$VERSION/configure" \ - --prefix="$PREFIX" \ - --datadir="$PREFIX" \ - --docdir="$PREFIX" \ - --mandir="$PREFIX" \ - --enable-static --disable-shared \ - --disable-programs --disable-doc --disable-debug \ - --disable-network \ - --disable-avdevice --disable-swscale --disable-swresample --disable-postproc --disable-avfilter \ - --disable-autodetect --disable-iconv \ - --enable-avcodec --enable-avformat --enable-avutil \ - --enable-protocol=file \ - --pkg-config-flags=--static \ - --enable-gpl --enable-libx264 \ - --disable-decoders --enable-decoder=h264,hevc,aac \ - --disable-encoders --enable-encoder=libx264,ffvhuff,aac \ - --disable-demuxers --enable-demuxer=mpegts,hevc,h264,matroska,mov \ - --disable-muxers --enable-muxer=matroska,mpegts \ - --disable-parsers --enable-parser=h264,hevc,aac,vorbis \ - --disable-bsfs \ - --enable-small \ - --extra-cflags="${EXTRA_CFLAGS:-}" \ - --extra-ldflags="${EXTRA_LDFLAGS:-}" - - -make -j$(nproc) -make install -cp -a "$PREFIX/include/." "$DIR/include/" - -# *** cleanup *** -cd $PREFIX -rm -rf share/ doc/ man/ examples/ examples/ include/ lib/pkgconfig/ -rm -f lib/libavfilter* "$DIR/include/libavfilter*" diff --git a/third_party/ffmpeg/include/libavcodec/ac3_parser.h b/third_party/ffmpeg/include/libavcodec/ac3_parser.h deleted file mode 100644 index c8acedae83..0000000000 --- a/third_party/ffmpeg/include/libavcodec/ac3_parser.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:200c6d2e96975196e8ba5f5716223dc9dda999d51578dabce2fca93175a05252 -size 1207 diff --git a/third_party/ffmpeg/include/libavcodec/adts_parser.h b/third_party/ffmpeg/include/libavcodec/adts_parser.h deleted file mode 100644 index 031f10320a..0000000000 --- a/third_party/ffmpeg/include/libavcodec/adts_parser.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d466583a8dc1260b015e588adbe3abd45f3f8ca0e43722f3088b472e80492a15 -size 1354 diff --git a/third_party/ffmpeg/include/libavcodec/avcodec.h b/third_party/ffmpeg/include/libavcodec/avcodec.h deleted file mode 100644 index 82fc7d1bd1..0000000000 --- a/third_party/ffmpeg/include/libavcodec/avcodec.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fc38e85633a2c6c44fc60b319d7e046a6e4206456565aa61240dd93ee2e81188 -size 114251 diff --git a/third_party/ffmpeg/include/libavcodec/avdct.h b/third_party/ffmpeg/include/libavcodec/avdct.h deleted file mode 100644 index 1a57c43007..0000000000 --- a/third_party/ffmpeg/include/libavcodec/avdct.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7c125edc1985ec078f99abb1c9044c4fc76b06a03eb02c026cb968fb9d41fcca -size 2726 diff --git a/third_party/ffmpeg/include/libavcodec/avfft.h b/third_party/ffmpeg/include/libavcodec/avfft.h deleted file mode 100644 index f54c365c1c..0000000000 --- a/third_party/ffmpeg/include/libavcodec/avfft.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:81bb6c5359f57de53b0e39766591404b632d15e700d313c97ded234d63d4e393 -size 4081 diff --git a/third_party/ffmpeg/include/libavcodec/bsf.h b/third_party/ffmpeg/include/libavcodec/bsf.h deleted file mode 100644 index 54fc91b112..0000000000 --- a/third_party/ffmpeg/include/libavcodec/bsf.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cc26676aa44638fa5cbef953d967e19e2084b46aaa100f0a35ba2d34b302301c -size 11540 diff --git a/third_party/ffmpeg/include/libavcodec/codec.h b/third_party/ffmpeg/include/libavcodec/codec.h deleted file mode 100644 index b8c3841727..0000000000 --- a/third_party/ffmpeg/include/libavcodec/codec.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d8b75f2e4128ce59db3172e2bdcbf64540424e6242952dc0912002bee5314254 -size 13460 diff --git a/third_party/ffmpeg/include/libavcodec/codec_desc.h b/third_party/ffmpeg/include/libavcodec/codec_desc.h deleted file mode 100644 index 108aa120d9..0000000000 --- a/third_party/ffmpeg/include/libavcodec/codec_desc.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4c1a8d02398cc82c1913487405c483f4d74ec71ed3e666ed7e7570f581c8f479 -size 3974 diff --git a/third_party/ffmpeg/include/libavcodec/codec_id.h b/third_party/ffmpeg/include/libavcodec/codec_id.h deleted file mode 100644 index 72002e6892..0000000000 --- a/third_party/ffmpeg/include/libavcodec/codec_id.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:754a846797be20c7c1ca30358d2555873df3bb19b6b2c8011c85697440f600df -size 18020 diff --git a/third_party/ffmpeg/include/libavcodec/codec_par.h b/third_party/ffmpeg/include/libavcodec/codec_par.h deleted file mode 100644 index 2c2f42e885..0000000000 --- a/third_party/ffmpeg/include/libavcodec/codec_par.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5f2bef7f23782ac9c0b5769eeb54704103efa5074ca5b657d7f01890795baf08 -size 8065 diff --git a/third_party/ffmpeg/include/libavcodec/d3d11va.h b/third_party/ffmpeg/include/libavcodec/d3d11va.h deleted file mode 100644 index aa372312b4..0000000000 --- a/third_party/ffmpeg/include/libavcodec/d3d11va.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:74a55a2e3f19ce797e99624a224302f25efa89a115b9bf2e932c8fa179b0cc66 -size 2853 diff --git a/third_party/ffmpeg/include/libavcodec/defs.h b/third_party/ffmpeg/include/libavcodec/defs.h deleted file mode 100644 index 6467ef6639..0000000000 --- a/third_party/ffmpeg/include/libavcodec/defs.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0bb9d7049499d252c75f1fe15fced5022ebb30d1966d47217848236eb021604a -size 12358 diff --git a/third_party/ffmpeg/include/libavcodec/dirac.h b/third_party/ffmpeg/include/libavcodec/dirac.h deleted file mode 100644 index 8bf7c4a0d3..0000000000 --- a/third_party/ffmpeg/include/libavcodec/dirac.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09fd1f670422ee61713568abd90fdf371b30e6fe35bc6fb8213f1ad32b45cf56 -size 4126 diff --git a/third_party/ffmpeg/include/libavcodec/dv_profile.h b/third_party/ffmpeg/include/libavcodec/dv_profile.h deleted file mode 100644 index 738746f3fc..0000000000 --- a/third_party/ffmpeg/include/libavcodec/dv_profile.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19f59e0b20ac583de4bfd76d18889d334bf0b6cdf7b5356723a33f3874738466 -size 3694 diff --git a/third_party/ffmpeg/include/libavcodec/dxva2.h b/third_party/ffmpeg/include/libavcodec/dxva2.h deleted file mode 100644 index aa41fbba83..0000000000 --- a/third_party/ffmpeg/include/libavcodec/dxva2.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e69dc45a7d5a9206b3bfead10caf3117117005cd532c4fc599d9976637c1b9e3 -size 2361 diff --git a/third_party/ffmpeg/include/libavcodec/jni.h b/third_party/ffmpeg/include/libavcodec/jni.h deleted file mode 100644 index 942690da84..0000000000 --- a/third_party/ffmpeg/include/libavcodec/jni.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:18ca8eae5bce081b4eef1b1f61a62aefed1d4e6e3cde41487c81fb96ee709e51 -size 1650 diff --git a/third_party/ffmpeg/include/libavcodec/mediacodec.h b/third_party/ffmpeg/include/libavcodec/mediacodec.h deleted file mode 100644 index 2afacbd1dc..0000000000 --- a/third_party/ffmpeg/include/libavcodec/mediacodec.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1f64544000dd2f2ec94604d5fcf7c2a6d26d32d085264356084a4b53a7f0c3f0 -size 3570 diff --git a/third_party/ffmpeg/include/libavcodec/packet.h b/third_party/ffmpeg/include/libavcodec/packet.h deleted file mode 100644 index 7c11f362ec..0000000000 --- a/third_party/ffmpeg/include/libavcodec/packet.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:18eb41b9aa9ec55b3ee3932279cc54ead813710741f004c5ea5a1c2957896f59 -size 28678 diff --git a/third_party/ffmpeg/include/libavcodec/qsv.h b/third_party/ffmpeg/include/libavcodec/qsv.h deleted file mode 100644 index 02c4c961f3..0000000000 --- a/third_party/ffmpeg/include/libavcodec/qsv.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e45780237ab0e9ea9ec11aab9f62dfb8b059138f846632ecacdc024db16dfb1b -size 3844 diff --git a/third_party/ffmpeg/include/libavcodec/vdpau.h b/third_party/ffmpeg/include/libavcodec/vdpau.h deleted file mode 100644 index 6ccba34dbc..0000000000 --- a/third_party/ffmpeg/include/libavcodec/vdpau.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3e0c963348495936556724cfb4d2d35eb4c14007ba72d51be94dd26c83d93fb8 -size 5104 diff --git a/third_party/ffmpeg/include/libavcodec/version.h b/third_party/ffmpeg/include/libavcodec/version.h deleted file mode 100644 index 9f2069af2d..0000000000 --- a/third_party/ffmpeg/include/libavcodec/version.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b6938a71f9eb6b1ef2a201499f47ef1131869b90755932a2ee70a272b2ee0784 -size 1619 diff --git a/third_party/ffmpeg/include/libavcodec/version_major.h b/third_party/ffmpeg/include/libavcodec/version_major.h deleted file mode 100644 index 7781faab45..0000000000 --- a/third_party/ffmpeg/include/libavcodec/version_major.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e9562c933e8eebb020eb143d21c4b2b529e2dc38d2d7365bc13adb8d0ef8227b -size 2494 diff --git a/third_party/ffmpeg/include/libavcodec/videotoolbox.h b/third_party/ffmpeg/include/libavcodec/videotoolbox.h deleted file mode 100644 index 59f788e025..0000000000 --- a/third_party/ffmpeg/include/libavcodec/videotoolbox.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19b6ba05f4e9f600044f45f1a5dc04d0da9276dc9a1cb5b07826df5cab5012c9 -size 4677 diff --git a/third_party/ffmpeg/include/libavcodec/vorbis_parser.h b/third_party/ffmpeg/include/libavcodec/vorbis_parser.h deleted file mode 100644 index d578829d44..0000000000 --- a/third_party/ffmpeg/include/libavcodec/vorbis_parser.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:57077b2e1d28d42636cab0f69e4b92b1ad64ac2eaa2843c270a6afaf308a76ae -size 2285 diff --git a/third_party/ffmpeg/include/libavcodec/xvmc.h b/third_party/ffmpeg/include/libavcodec/xvmc.h deleted file mode 100644 index abe162a93b..0000000000 --- a/third_party/ffmpeg/include/libavcodec/xvmc.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ca59c5caaaa32368f7c84c714bbf2ad1cba61ec9ca5b5cb1a14cf6975affe8a2 -size 6136 diff --git a/third_party/ffmpeg/include/libavformat/avformat.h b/third_party/ffmpeg/include/libavformat/avformat.h deleted file mode 100644 index a4244e9137..0000000000 --- a/third_party/ffmpeg/include/libavformat/avformat.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c1ca6edf1d5eab00126fa7320f01c21ed8cea09dc2d931f84a4015b9e50f8d2a -size 110803 diff --git a/third_party/ffmpeg/include/libavformat/avio.h b/third_party/ffmpeg/include/libavformat/avio.h deleted file mode 100644 index af9666e186..0000000000 --- a/third_party/ffmpeg/include/libavformat/avio.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:de6fe4be374481b1ee5ba8722882af7e7698038fd744375de153d1c78f528836 -size 31681 diff --git a/third_party/ffmpeg/include/libavformat/version.h b/third_party/ffmpeg/include/libavformat/version.h deleted file mode 100644 index 07fe55f955..0000000000 --- a/third_party/ffmpeg/include/libavformat/version.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:08b64bb52109a07ae3ee1a1dafd12ad336d0a646c23aea075c5024b9437f3b29 -size 1652 diff --git a/third_party/ffmpeg/include/libavformat/version_major.h b/third_party/ffmpeg/include/libavformat/version_major.h deleted file mode 100644 index 0efde349bb..0000000000 --- a/third_party/ffmpeg/include/libavformat/version_major.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2d440097abe2039c3c831d3b91d315857829d04e462415ee632c9f36321d520d -size 2240 diff --git a/third_party/ffmpeg/include/libavutil/adler32.h b/third_party/ffmpeg/include/libavutil/adler32.h deleted file mode 100644 index 19f01b3441..0000000000 --- a/third_party/ffmpeg/include/libavutil/adler32.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f21a861957bf4b1812ed67fdc528890f9cff1bb483facf0f5109e4a51932fe3b -size 1696 diff --git a/third_party/ffmpeg/include/libavutil/aes.h b/third_party/ffmpeg/include/libavutil/aes.h deleted file mode 100644 index 6d81ffa43e..0000000000 --- a/third_party/ffmpeg/include/libavutil/aes.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0a86ebeaf9ed33548bf0f92359f7047e521d4d80fe6b0ef1c8ef9505e38b6d28 -size 1912 diff --git a/third_party/ffmpeg/include/libavutil/aes_ctr.h b/third_party/ffmpeg/include/libavutil/aes_ctr.h deleted file mode 100644 index 1a156aed36..0000000000 --- a/third_party/ffmpeg/include/libavutil/aes_ctr.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fbbb94888bfab2ea7141e7e1afa5872de099e7ee57baea17632c3d4fdd2cba3f -size 2443 diff --git a/third_party/ffmpeg/include/libavutil/ambient_viewing_environment.h b/third_party/ffmpeg/include/libavutil/ambient_viewing_environment.h deleted file mode 100644 index 42a94d1941..0000000000 --- a/third_party/ffmpeg/include/libavutil/ambient_viewing_environment.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:00135de08089f8c711bb113588f80575a1d26e86d50b6dac5928a27a0ff9a8c7 -size 2585 diff --git a/third_party/ffmpeg/include/libavutil/attributes.h b/third_party/ffmpeg/include/libavutil/attributes.h deleted file mode 100644 index 2a47842614..0000000000 --- a/third_party/ffmpeg/include/libavutil/attributes.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1f51258ba39861e08cbc8d407ec37a8d0fd28669aa7306c1365cb9c675fa1d76 -size 4850 diff --git a/third_party/ffmpeg/include/libavutil/audio_fifo.h b/third_party/ffmpeg/include/libavutil/audio_fifo.h deleted file mode 100644 index 28a3364eee..0000000000 --- a/third_party/ffmpeg/include/libavutil/audio_fifo.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c513ca46927346c0673cd9db302afe62dbbf976844f36da0307fd10f7f47bfd -size 5966 diff --git a/third_party/ffmpeg/include/libavutil/avassert.h b/third_party/ffmpeg/include/libavutil/avassert.h deleted file mode 100644 index 40ecca50e9..0000000000 --- a/third_party/ffmpeg/include/libavutil/avassert.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39dfe16f0790daa5d6b9b1bad3167c3c9e638c72452e0acdaf2d163c9c75ea40 -size 2408 diff --git a/third_party/ffmpeg/include/libavutil/avconfig.h b/third_party/ffmpeg/include/libavutil/avconfig.h deleted file mode 100644 index 4e1eedcf99..0000000000 --- a/third_party/ffmpeg/include/libavutil/avconfig.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:975611ad5eba15212d9e1d5fca9d4fdf0daec6d2269b2fcab8e29af8667164bc -size 180 diff --git a/third_party/ffmpeg/include/libavutil/avstring.h b/third_party/ffmpeg/include/libavutil/avstring.h deleted file mode 100644 index ce048414e3..0000000000 --- a/third_party/ffmpeg/include/libavutil/avstring.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ac51462bf55ff62da39db4b15c7a6b9b783387709f06879196a065691b34edbf -size 14940 diff --git a/third_party/ffmpeg/include/libavutil/avutil.h b/third_party/ffmpeg/include/libavutil/avutil.h deleted file mode 100644 index 7f3e2654eb..0000000000 --- a/third_party/ffmpeg/include/libavutil/avutil.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7640d72de6a72eb9589ed2c623b4ec0924a241097846086c62dc3e05ab70ee9e -size 9968 diff --git a/third_party/ffmpeg/include/libavutil/base64.h b/third_party/ffmpeg/include/libavutil/base64.h deleted file mode 100644 index db8221772a..0000000000 --- a/third_party/ffmpeg/include/libavutil/base64.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:81ac13d23f3744fe85ea2651ce903e201cd55fc63fcdd899d2cfe5560d50ef3d -size 2285 diff --git a/third_party/ffmpeg/include/libavutil/blowfish.h b/third_party/ffmpeg/include/libavutil/blowfish.h deleted file mode 100644 index c16f03bfd2..0000000000 --- a/third_party/ffmpeg/include/libavutil/blowfish.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b955a63c60c8b3be0203ec6c3973f9084d848cf884fe56cd56088301aeef7992 -size 2394 diff --git a/third_party/ffmpeg/include/libavutil/bprint.h b/third_party/ffmpeg/include/libavutil/bprint.h deleted file mode 100644 index 9a55285676..0000000000 --- a/third_party/ffmpeg/include/libavutil/bprint.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c19f6dc2c06e55d47bc1fbe9fc7658acc7a0d506b6baf16839a6119a03e873c1 -size 8812 diff --git a/third_party/ffmpeg/include/libavutil/bswap.h b/third_party/ffmpeg/include/libavutil/bswap.h deleted file mode 100644 index b8e017531e..0000000000 --- a/third_party/ffmpeg/include/libavutil/bswap.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:855e138f6d8c7947553b1c63a7bab06bcb71362f03c72b761fd9ed61820d71aa -size 2903 diff --git a/third_party/ffmpeg/include/libavutil/buffer.h b/third_party/ffmpeg/include/libavutil/buffer.h deleted file mode 100644 index fd462ec6b8..0000000000 --- a/third_party/ffmpeg/include/libavutil/buffer.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f16742d574216434580573a2b09f56fc5b66b7dda1960d4f02ba59e3269ba548 -size 11998 diff --git a/third_party/ffmpeg/include/libavutil/camellia.h b/third_party/ffmpeg/include/libavutil/camellia.h deleted file mode 100644 index 8d6fdaca6c..0000000000 --- a/third_party/ffmpeg/include/libavutil/camellia.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1db30753e71c73f1937e807850069e8215cdf37a1bc3ff89d3a6370a719c1fde -size 2139 diff --git a/third_party/ffmpeg/include/libavutil/cast5.h b/third_party/ffmpeg/include/libavutil/cast5.h deleted file mode 100644 index 2dfac6f5b2..0000000000 --- a/third_party/ffmpeg/include/libavutil/cast5.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:05b2e13aecaa0adbb470081a689f45baffb8e03a71997c31f37a22ea4e383a60 -size 2561 diff --git a/third_party/ffmpeg/include/libavutil/channel_layout.h b/third_party/ffmpeg/include/libavutil/channel_layout.h deleted file mode 100644 index 02549bd9a9..0000000000 --- a/third_party/ffmpeg/include/libavutil/channel_layout.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5df118692f2b7043820f0e641f1968cadb66c39f0ba85fab7435391322a223f5 -size 33727 diff --git a/third_party/ffmpeg/include/libavutil/common.h b/third_party/ffmpeg/include/libavutil/common.h deleted file mode 100644 index 8f2dfcaebb..0000000000 --- a/third_party/ffmpeg/include/libavutil/common.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d861dac3eaecc326fa8fb717d90f195d961cc8ac7898daccfefd10bd5ea42398 -size 17166 diff --git a/third_party/ffmpeg/include/libavutil/cpu.h b/third_party/ffmpeg/include/libavutil/cpu.h deleted file mode 100644 index 3181cd0896..0000000000 --- a/third_party/ffmpeg/include/libavutil/cpu.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:37c4ccea939e19b463a2a3e4c101ff8dd03c98847fc045395d28ba0c9db8b662 -size 6320 diff --git a/third_party/ffmpeg/include/libavutil/crc.h b/third_party/ffmpeg/include/libavutil/crc.h deleted file mode 100644 index 9cc23bb941..0000000000 --- a/third_party/ffmpeg/include/libavutil/crc.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5728cf65705a46723ea28b4f6c8361aad82b76a90e859943efe8af0edb79ec86 -size 3259 diff --git a/third_party/ffmpeg/include/libavutil/csp.h b/third_party/ffmpeg/include/libavutil/csp.h deleted file mode 100644 index f4c8c5652c..0000000000 --- a/third_party/ffmpeg/include/libavutil/csp.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7969fd662f31cf3180403510a6784a14af60d7f9bf3a569dde84585a696dff09 -size 4927 diff --git a/third_party/ffmpeg/include/libavutil/des.h b/third_party/ffmpeg/include/libavutil/des.h deleted file mode 100644 index 5df3354f97..0000000000 --- a/third_party/ffmpeg/include/libavutil/des.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:15ebdda1af65d91c4607a3444c5f749d5e9757ff5d7f4b04213b3194603f74d9 -size 2514 diff --git a/third_party/ffmpeg/include/libavutil/detection_bbox.h b/third_party/ffmpeg/include/libavutil/detection_bbox.h deleted file mode 100644 index f81ef0a64a..0000000000 --- a/third_party/ffmpeg/include/libavutil/detection_bbox.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8f5817d77af243a52e905947aa5ae73c218d68dba909040b2f63bd2ca6f93922 -size 3524 diff --git a/third_party/ffmpeg/include/libavutil/dict.h b/third_party/ffmpeg/include/libavutil/dict.h deleted file mode 100644 index e01fbf8bb3..0000000000 --- a/third_party/ffmpeg/include/libavutil/dict.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c95cba1829e7886e31469f4e058bb9af3c8f215619c7ffddbc2045c9039f0554 -size 9374 diff --git a/third_party/ffmpeg/include/libavutil/display.h b/third_party/ffmpeg/include/libavutil/display.h deleted file mode 100644 index 6bca44f9f8..0000000000 --- a/third_party/ffmpeg/include/libavutil/display.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b9c78c80aa9331b945802b6bcd1db4ecc9ec4f9fad41993cc82b880c0dec2576 -size 3472 diff --git a/third_party/ffmpeg/include/libavutil/dovi_meta.h b/third_party/ffmpeg/include/libavutil/dovi_meta.h deleted file mode 100644 index 0d545564b2..0000000000 --- a/third_party/ffmpeg/include/libavutil/dovi_meta.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3a67a422676c5b6e2dfd59b3e6a8e3f816b0602f5d73332c0b1d8e6b43644077 -size 7641 diff --git a/third_party/ffmpeg/include/libavutil/downmix_info.h b/third_party/ffmpeg/include/libavutil/downmix_info.h deleted file mode 100644 index cfebdff5be..0000000000 --- a/third_party/ffmpeg/include/libavutil/downmix_info.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2fc23ad8f0750d82fcd6aa3b653998e2ea9721f9d1664df7b6cb80e93d7fa3aa -size 3235 diff --git a/third_party/ffmpeg/include/libavutil/encryption_info.h b/third_party/ffmpeg/include/libavutil/encryption_info.h deleted file mode 100644 index 490da787ff..0000000000 --- a/third_party/ffmpeg/include/libavutil/encryption_info.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ccc3a4a889b8a3c5aaf37b9fb2407bcdf23a065487c7cba718518a517c463b18 -size 7056 diff --git a/third_party/ffmpeg/include/libavutil/error.h b/third_party/ffmpeg/include/libavutil/error.h deleted file mode 100644 index 30e492756d..0000000000 --- a/third_party/ffmpeg/include/libavutil/error.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f90feec75a317e491618e06ce14d19218e5b0257c885155613b704002a9d5bda -size 5489 diff --git a/third_party/ffmpeg/include/libavutil/eval.h b/third_party/ffmpeg/include/libavutil/eval.h deleted file mode 100644 index 49448a05c7..0000000000 --- a/third_party/ffmpeg/include/libavutil/eval.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5e92af6be91c610c3bcad3a344a2df5e3516153fc89045464447bb0ee44b1f56 -size 6599 diff --git a/third_party/ffmpeg/include/libavutil/executor.h b/third_party/ffmpeg/include/libavutil/executor.h deleted file mode 100644 index 91edf0541f..0000000000 --- a/third_party/ffmpeg/include/libavutil/executor.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c949cf5269ad933ed1a200ece06a3998db10147e1403629a398b7be52459cdc -size 1885 diff --git a/third_party/ffmpeg/include/libavutil/ffversion.h b/third_party/ffmpeg/include/libavutil/ffversion.h deleted file mode 100644 index 7fe4e44534..0000000000 --- a/third_party/ffmpeg/include/libavutil/ffversion.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1defa7afe4fab2090455bf74435ce185bce721d1b795f94c56994a031ae20080 -size 184 diff --git a/third_party/ffmpeg/include/libavutil/fifo.h b/third_party/ffmpeg/include/libavutil/fifo.h deleted file mode 100644 index 8e4cff8600..0000000000 --- a/third_party/ffmpeg/include/libavutil/fifo.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c77e489715a83e1dc1ce3187b5e23b1b0cd52762638919e955d43afae316f9b -size 15452 diff --git a/third_party/ffmpeg/include/libavutil/file.h b/third_party/ffmpeg/include/libavutil/file.h deleted file mode 100644 index 44cae78190..0000000000 --- a/third_party/ffmpeg/include/libavutil/file.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f91c41bc9a14a206bc86c87b9edd7215b5be63b8aade2bda40297dc6752de36 -size 3039 diff --git a/third_party/ffmpeg/include/libavutil/film_grain_params.h b/third_party/ffmpeg/include/libavutil/film_grain_params.h deleted file mode 100644 index 8d052ff6d2..0000000000 --- a/third_party/ffmpeg/include/libavutil/film_grain_params.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e1bda2934dce25cb0bb9fa6eaae230cd21aca79428905b66417f74fc8f0fa72a -size 8499 diff --git a/third_party/ffmpeg/include/libavutil/frame.h b/third_party/ffmpeg/include/libavutil/frame.h deleted file mode 100644 index 013f70247f..0000000000 --- a/third_party/ffmpeg/include/libavutil/frame.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0512cdd2a4479faac17b556f6311a3307ace2a5278217c4d738bd752c07dc37e -size 35894 diff --git a/third_party/ffmpeg/include/libavutil/hash.h b/third_party/ffmpeg/include/libavutil/hash.h deleted file mode 100644 index db71ca59f5..0000000000 --- a/third_party/ffmpeg/include/libavutil/hash.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b0896571267220736679eea28c454783795a02a0f1aef008ebe7c40489a75fdd -size 8457 diff --git a/third_party/ffmpeg/include/libavutil/hdr_dynamic_metadata.h b/third_party/ffmpeg/include/libavutil/hdr_dynamic_metadata.h deleted file mode 100644 index f920e30d6c..0000000000 --- a/third_party/ffmpeg/include/libavutil/hdr_dynamic_metadata.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:457274c2eda1fe91e83ae76b9d1ab8682e6144adfacbd2c86f5fb4a03ede421f -size 14385 diff --git a/third_party/ffmpeg/include/libavutil/hdr_dynamic_vivid_metadata.h b/third_party/ffmpeg/include/libavutil/hdr_dynamic_vivid_metadata.h deleted file mode 100644 index 64e00109fe..0000000000 --- a/third_party/ffmpeg/include/libavutil/hdr_dynamic_vivid_metadata.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f50e471376bc9e8ca504a338cfe74ef634b8f2c242ccefed2e4da67197112de -size 10004 diff --git a/third_party/ffmpeg/include/libavutil/hmac.h b/third_party/ffmpeg/include/libavutil/hmac.h deleted file mode 100644 index 919dc1ec2a..0000000000 --- a/third_party/ffmpeg/include/libavutil/hmac.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d14d625a897d6bba0668acdf33dc597bb0050237c5c1a5f7e568fe36822782e7 -size 2865 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext.h b/third_party/ffmpeg/include/libavutil/hwcontext.h deleted file mode 100644 index 5ccfbbe916..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e05c09b0a2f51c800aa57dc21277b5b4534e6783ab3df52dbf81c63e37fe8323 -size 24341 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_cuda.h b/third_party/ffmpeg/include/libavutil/hwcontext_cuda.h deleted file mode 100644 index c596a2781d..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_cuda.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4878f46347271bc7a9ff26bb1573449a99cc81447684e1034a3edd4b0ff91d9a -size 1843 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_d3d11va.h b/third_party/ffmpeg/include/libavutil/hwcontext_d3d11va.h deleted file mode 100644 index efdaa88006..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_d3d11va.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d99c1b5c5dab94c23709d4ce6cf489c2a0b4f4bd57bb04e2196371277dca5af7 -size 6669 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_drm.h b/third_party/ffmpeg/include/libavutil/hwcontext_drm.h deleted file mode 100644 index fa6732af71..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_drm.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b598f37f40cf1342f923c0b97784a6f2830b543868eccee046375e096fbd5f24 -size 4673 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_dxva2.h b/third_party/ffmpeg/include/libavutil/hwcontext_dxva2.h deleted file mode 100644 index d871979e67..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_dxva2.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:73a0333b65e99675834dcb1b63a5e9339638ccc619f1a2fcba85cdd0e179ade0 -size 2411 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_mediacodec.h b/third_party/ffmpeg/include/libavutil/hwcontext_mediacodec.h deleted file mode 100644 index efee54fea8..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_mediacodec.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c602859ebca906ba6e43ea548ff28821cf2886b4500b2be1deaaf2d552496d4 -size 1988 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_opencl.h b/third_party/ffmpeg/include/libavutil/hwcontext_opencl.h deleted file mode 100644 index 812b29caaf..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_opencl.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad521fa2fd015cb1aba962468ca4ac176f5fc6b2c4b7be28f05e1c03d89e1b31 -size 3097 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_qsv.h b/third_party/ffmpeg/include/libavutil/hwcontext_qsv.h deleted file mode 100644 index a5053d51de..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_qsv.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4986f86340b8162ef2724f0e77a380b5133aaa0612749cfb836c38b4c166d20d -size 1960 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_vaapi.h b/third_party/ffmpeg/include/libavutil/hwcontext_vaapi.h deleted file mode 100644 index b205bc5fcf..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_vaapi.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6f6c6a5250dd0f901cdc7de8b9b3db26102719b7e056cd17500009096bfd9b39 -size 3787 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_vdpau.h b/third_party/ffmpeg/include/libavutil/hwcontext_vdpau.h deleted file mode 100644 index 8cd234bf7b..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_vdpau.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6c96373d9e5deb2c500004f3f55ee1d2cea0f76cdfaeabaf5a3ad3e4938e8252 -size 1360 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_videotoolbox.h b/third_party/ffmpeg/include/libavutil/hwcontext_videotoolbox.h deleted file mode 100644 index db959d5fc1..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_videotoolbox.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2f74c83778a0df9738abcae823412436cbbeaff8e9083281fb6f38116cb401c2 -size 3431 diff --git a/third_party/ffmpeg/include/libavutil/hwcontext_vulkan.h b/third_party/ffmpeg/include/libavutil/hwcontext_vulkan.h deleted file mode 100644 index fce1192137..0000000000 --- a/third_party/ffmpeg/include/libavutil/hwcontext_vulkan.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:74067b6224e8a9d60969f192fdc17f97a12ff073274f5047c380543647026760 -size 11412 diff --git a/third_party/ffmpeg/include/libavutil/imgutils.h b/third_party/ffmpeg/include/libavutil/imgutils.h deleted file mode 100644 index 9636ebc81b..0000000000 --- a/third_party/ffmpeg/include/libavutil/imgutils.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4cceea6c7b3017dea61a9fd514d7460ab13592907addb69a2e92870872207f1 -size 14819 diff --git a/third_party/ffmpeg/include/libavutil/intfloat.h b/third_party/ffmpeg/include/libavutil/intfloat.h deleted file mode 100644 index 278a2a6311..0000000000 --- a/third_party/ffmpeg/include/libavutil/intfloat.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3a29e4eebc8c269cfd867b96de91d8231773d392c12a8820e46eaba96d2b4ca1 -size 1726 diff --git a/third_party/ffmpeg/include/libavutil/intreadwrite.h b/third_party/ffmpeg/include/libavutil/intreadwrite.h deleted file mode 100644 index e6ae02efbf..0000000000 --- a/third_party/ffmpeg/include/libavutil/intreadwrite.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f815f4edb0fb58c0b2c7b4f4763a27ee223672fa43ef4c5c0b99fb4575b66bf7 -size 18735 diff --git a/third_party/ffmpeg/include/libavutil/lfg.h b/third_party/ffmpeg/include/libavutil/lfg.h deleted file mode 100644 index 4e9488eaa0..0000000000 --- a/third_party/ffmpeg/include/libavutil/lfg.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c3bc1533172fe74870df8e37f168f28b0ff2e21bdb6a519b6555ec72710c910f -size 2541 diff --git a/third_party/ffmpeg/include/libavutil/log.h b/third_party/ffmpeg/include/libavutil/log.h deleted file mode 100644 index a3dd2c72af..0000000000 --- a/third_party/ffmpeg/include/libavutil/log.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8555ad5d41f5f2fa1c7dd0dc65f2145b0517c0a414654575aade8b3968e4c408 -size 12766 diff --git a/third_party/ffmpeg/include/libavutil/lzo.h b/third_party/ffmpeg/include/libavutil/lzo.h deleted file mode 100644 index 2e7db3c19d..0000000000 --- a/third_party/ffmpeg/include/libavutil/lzo.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:61e89928dee9d83030adececac06aa6c1ae2aada06c5682fde52c52015c53556 -size 2048 diff --git a/third_party/ffmpeg/include/libavutil/macros.h b/third_party/ffmpeg/include/libavutil/macros.h deleted file mode 100644 index 57932eaeb0..0000000000 --- a/third_party/ffmpeg/include/libavutil/macros.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b63b3a268b096f0eed1e91b821714cff334e5dc5bb34365148704393ae15321e -size 2304 diff --git a/third_party/ffmpeg/include/libavutil/mastering_display_metadata.h b/third_party/ffmpeg/include/libavutil/mastering_display_metadata.h deleted file mode 100644 index 90983df170..0000000000 --- a/third_party/ffmpeg/include/libavutil/mastering_display_metadata.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d5743a42306ac0158e26248a1281ae8be9ebfeb02e67f14e0e6ae770a543a65 -size 3944 diff --git a/third_party/ffmpeg/include/libavutil/mathematics.h b/third_party/ffmpeg/include/libavutil/mathematics.h deleted file mode 100644 index e707131c8f..0000000000 --- a/third_party/ffmpeg/include/libavutil/mathematics.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:64fac2eb3a42fd3788f5585ac8e65c7d5cd82711730d1f030042ba0a62fe1a62 -size 9563 diff --git a/third_party/ffmpeg/include/libavutil/md5.h b/third_party/ffmpeg/include/libavutil/md5.h deleted file mode 100644 index 33a63e05cb..0000000000 --- a/third_party/ffmpeg/include/libavutil/md5.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b42de1758d289f78b4d20c47686f443e4ea8a5a6411c0deb357f709d2ef34d7 -size 2092 diff --git a/third_party/ffmpeg/include/libavutil/mem.h b/third_party/ffmpeg/include/libavutil/mem.h deleted file mode 100644 index 5a81cd674a..0000000000 --- a/third_party/ffmpeg/include/libavutil/mem.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bf9a1d8c218c38b333efb16be076c5778d31d66a0691bdd6175f171c08283d7c -size 20457 diff --git a/third_party/ffmpeg/include/libavutil/motion_vector.h b/third_party/ffmpeg/include/libavutil/motion_vector.h deleted file mode 100644 index 6c61741e5e..0000000000 --- a/third_party/ffmpeg/include/libavutil/motion_vector.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dc0b0a15a638c8b91df95a418c5951ee5e787d518f22b6e3d70094922536e8bb -size 1770 diff --git a/third_party/ffmpeg/include/libavutil/murmur3.h b/third_party/ffmpeg/include/libavutil/murmur3.h deleted file mode 100644 index 353b5a763f..0000000000 --- a/third_party/ffmpeg/include/libavutil/murmur3.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:649258a51c4737fa19a025a489e2ac9e9b06a96eafa802f2765178c684382887 -size 3507 diff --git a/third_party/ffmpeg/include/libavutil/opt.h b/third_party/ffmpeg/include/libavutil/opt.h deleted file mode 100644 index ef2b874b26..0000000000 --- a/third_party/ffmpeg/include/libavutil/opt.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4a1679c21453e4337cc7d377efaec6610340a59204c0b58fc102ab3a9da51a39 -size 37201 diff --git a/third_party/ffmpeg/include/libavutil/parseutils.h b/third_party/ffmpeg/include/libavutil/parseutils.h deleted file mode 100644 index de2f938c60..0000000000 --- a/third_party/ffmpeg/include/libavutil/parseutils.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e8efed69396851f429a8258d50e9c4f0431f921687a7c31bf6db13d14f7482c3 -size 7888 diff --git a/third_party/ffmpeg/include/libavutil/pixdesc.h b/third_party/ffmpeg/include/libavutil/pixdesc.h deleted file mode 100644 index ade8e6906c..0000000000 --- a/third_party/ffmpeg/include/libavutil/pixdesc.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:253359c22bc051754a9ded9497a850e5296cbfd058a9b73957c23b53d7314d96 -size 16031 diff --git a/third_party/ffmpeg/include/libavutil/pixelutils.h b/third_party/ffmpeg/include/libavutil/pixelutils.h deleted file mode 100644 index 2734d292c9..0000000000 --- a/third_party/ffmpeg/include/libavutil/pixelutils.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:339cd6ffb6460d06401801c5dfb91ca66b9bdc028e1acc9ff4a0f447cfd3785c -size 2051 diff --git a/third_party/ffmpeg/include/libavutil/pixfmt.h b/third_party/ffmpeg/include/libavutil/pixfmt.h deleted file mode 100644 index 459bb057c9..0000000000 --- a/third_party/ffmpeg/include/libavutil/pixfmt.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:69104b943b1c44a54e46635be1e5da0ff6be7cd3f1af6ebc9924d4d965d0e420 -size 41556 diff --git a/third_party/ffmpeg/include/libavutil/random_seed.h b/third_party/ffmpeg/include/libavutil/random_seed.h deleted file mode 100644 index a4e8933e50..0000000000 --- a/third_party/ffmpeg/include/libavutil/random_seed.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4490fd79919aadb18f765caac0c210d22cafa4d63cddcf9275e6f5bf66e2fdea -size 1889 diff --git a/third_party/ffmpeg/include/libavutil/rational.h b/third_party/ffmpeg/include/libavutil/rational.h deleted file mode 100644 index 32a39febc7..0000000000 --- a/third_party/ffmpeg/include/libavutil/rational.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:700422cc782acde6898522ffe1b74658b12ca22e2cfbb8f44986266216de7f21 -size 6100 diff --git a/third_party/ffmpeg/include/libavutil/rc4.h b/third_party/ffmpeg/include/libavutil/rc4.h deleted file mode 100644 index a79ce5a1b4..0000000000 --- a/third_party/ffmpeg/include/libavutil/rc4.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4ea0fbee43677721ad0d846f703a785aaf9881794d1cca0bcb210241b260fc26 -size 2003 diff --git a/third_party/ffmpeg/include/libavutil/replaygain.h b/third_party/ffmpeg/include/libavutil/replaygain.h deleted file mode 100644 index 760b289ee1..0000000000 --- a/third_party/ffmpeg/include/libavutil/replaygain.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4ec82edbdc4e5493fba3cae6a27566f0f15d1399ccf16e25073ffd50ba8187ea -size 1607 diff --git a/third_party/ffmpeg/include/libavutil/ripemd.h b/third_party/ffmpeg/include/libavutil/ripemd.h deleted file mode 100644 index 34cd2714a6..0000000000 --- a/third_party/ffmpeg/include/libavutil/ripemd.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:df9ef8c29ee31e5bd8ea299b03d51bd25fe937583793a994db53d1df2b316620 -size 2158 diff --git a/third_party/ffmpeg/include/libavutil/samplefmt.h b/third_party/ffmpeg/include/libavutil/samplefmt.h deleted file mode 100644 index c37f4ace9d..0000000000 --- a/third_party/ffmpeg/include/libavutil/samplefmt.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e844d8b4a691256238d10de762a6a536ffe52995705166aee0d7f01ce79778a9 -size 10301 diff --git a/third_party/ffmpeg/include/libavutil/sha.h b/third_party/ffmpeg/include/libavutil/sha.h deleted file mode 100644 index 58b5ea658e..0000000000 --- a/third_party/ffmpeg/include/libavutil/sha.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:91280db6995b1b99b9e5aad0aa211a3177dc4d2841da2fea097f54964b7891fd -size 2368 diff --git a/third_party/ffmpeg/include/libavutil/sha512.h b/third_party/ffmpeg/include/libavutil/sha512.h deleted file mode 100644 index 4e62f7fb63..0000000000 --- a/third_party/ffmpeg/include/libavutil/sha512.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:da265152798b221706d7fe95293a0e8cd18fa2b5087bf32504a8120f10e7658f -size 2413 diff --git a/third_party/ffmpeg/include/libavutil/spherical.h b/third_party/ffmpeg/include/libavutil/spherical.h deleted file mode 100644 index 2e14e4f293..0000000000 --- a/third_party/ffmpeg/include/libavutil/spherical.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:18b8e559f69a9c93b251c9d3956b2aba7f47ba46ef5a7fa01f8ae858967b73da -size 7997 diff --git a/third_party/ffmpeg/include/libavutil/stereo3d.h b/third_party/ffmpeg/include/libavutil/stereo3d.h deleted file mode 100644 index 952eb6cf79..0000000000 --- a/third_party/ffmpeg/include/libavutil/stereo3d.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5ece3ffb6baafd288f9f2d8747cae92b1b3985e6693fa2de6bc200173a1981b6 -size 5224 diff --git a/third_party/ffmpeg/include/libavutil/tea.h b/third_party/ffmpeg/include/libavutil/tea.h deleted file mode 100644 index 5b3094167b..0000000000 --- a/third_party/ffmpeg/include/libavutil/tea.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c1e93c566630bb4eeedad3ef3c8719bd6050081ac1c764b1fde81aba4969076 -size 2035 diff --git a/third_party/ffmpeg/include/libavutil/threadmessage.h b/third_party/ffmpeg/include/libavutil/threadmessage.h deleted file mode 100644 index 70c8112bad..0000000000 --- a/third_party/ffmpeg/include/libavutil/threadmessage.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9bb242d7adc48662b947726843108aff7c34547d7a4a0d0e6f58f54a00fc4c9f -size 3910 diff --git a/third_party/ffmpeg/include/libavutil/time.h b/third_party/ffmpeg/include/libavutil/time.h deleted file mode 100644 index ef77ac592e..0000000000 --- a/third_party/ffmpeg/include/libavutil/time.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:40e11fa242e0585996753affb054443e78be25919b7c3063042d0aaff1656760 -size 1800 diff --git a/third_party/ffmpeg/include/libavutil/timecode.h b/third_party/ffmpeg/include/libavutil/timecode.h deleted file mode 100644 index 5b54b01bcf..0000000000 --- a/third_party/ffmpeg/include/libavutil/timecode.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:afd0a634b1abcb282c694ae6c43f0412fe2ccb37fb3f08433af33779ea9e572e -size 7843 diff --git a/third_party/ffmpeg/include/libavutil/timestamp.h b/third_party/ffmpeg/include/libavutil/timestamp.h deleted file mode 100644 index 828043fe81..0000000000 --- a/third_party/ffmpeg/include/libavutil/timestamp.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7cfbb6640449104c473cc1bc6f78244ce97636fca294da2867cedaab868c1b8c -size 2617 diff --git a/third_party/ffmpeg/include/libavutil/tree.h b/third_party/ffmpeg/include/libavutil/tree.h deleted file mode 100644 index ca3f72fff0..0000000000 --- a/third_party/ffmpeg/include/libavutil/tree.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2f8e906917612a05c138036dea7ed9f8faee5899413a523fdad4eb51711bc1e5 -size 5408 diff --git a/third_party/ffmpeg/include/libavutil/twofish.h b/third_party/ffmpeg/include/libavutil/twofish.h deleted file mode 100644 index 96cf965ab3..0000000000 --- a/third_party/ffmpeg/include/libavutil/twofish.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b71714336821e1c606b65620ba4b1ea47e431666be41f3174facbc51047fd814 -size 2245 diff --git a/third_party/ffmpeg/include/libavutil/tx.h b/third_party/ffmpeg/include/libavutil/tx.h deleted file mode 100644 index 223ffc4951..0000000000 --- a/third_party/ffmpeg/include/libavutil/tx.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:34f83ae31d074847e46579f25e3367210fbbfe1832b5315883cf4a28980ef101 -size 7140 diff --git a/third_party/ffmpeg/include/libavutil/uuid.h b/third_party/ffmpeg/include/libavutil/uuid.h deleted file mode 100644 index 3bc380c31a..0000000000 --- a/third_party/ffmpeg/include/libavutil/uuid.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e669ce76a6b987e189b4d7ff62d0fd9ad6e334fa4967076cc6d912976574b646 -size 4895 diff --git a/third_party/ffmpeg/include/libavutil/version.h b/third_party/ffmpeg/include/libavutil/version.h deleted file mode 100644 index 1f0fa95e08..0000000000 --- a/third_party/ffmpeg/include/libavutil/version.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fed7c6f02a873de2c7e2f7e6d24e48e747b3f5204e36d50e19b5e82358b76470 -size 4832 diff --git a/third_party/ffmpeg/include/libavutil/video_enc_params.h b/third_party/ffmpeg/include/libavutil/video_enc_params.h deleted file mode 100644 index 7ce1b57024..0000000000 --- a/third_party/ffmpeg/include/libavutil/video_enc_params.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f287486c4f828f82e579f93ea98fccb98749129544f660decfa56da6f818fd57 -size 5991 diff --git a/third_party/ffmpeg/include/libavutil/video_hint.h b/third_party/ffmpeg/include/libavutil/video_hint.h deleted file mode 100644 index 46ad47b113..0000000000 --- a/third_party/ffmpeg/include/libavutil/video_hint.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7c8b7768ffd03af5034a13274d1ca0cc6206b9f2c0c478400aba8a6c79ad5359 -size 3586 diff --git a/third_party/ffmpeg/include/libavutil/xtea.h b/third_party/ffmpeg/include/libavutil/xtea.h deleted file mode 100644 index 99bdc08b36..0000000000 --- a/third_party/ffmpeg/include/libavutil/xtea.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2eb91f780cc4ad86095e4ebbce453475d40f4e9b8737d52bdf20a068dfafcdf0 -size 2834 diff --git a/third_party/ffmpeg/include/x264.h b/third_party/ffmpeg/include/x264.h deleted file mode 100644 index 17c5741777..0000000000 --- a/third_party/ffmpeg/include/x264.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cec084bdb3cd2330800d0726bdbd99d91112df3fdb6d79f94e15ba67808b715c -size 49056 diff --git a/third_party/ffmpeg/include/x264_config.h b/third_party/ffmpeg/include/x264_config.h deleted file mode 100644 index 0b4ef0a07f..0000000000 --- a/third_party/ffmpeg/include/x264_config.h +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:81acf190604a6c03d8e44dd3a9751ea12dc4823cdff1c9f0b376faaeafe79ed2 -size 172 diff --git a/third_party/ffmpeg/larch64/lib/libavcodec.a b/third_party/ffmpeg/larch64/lib/libavcodec.a deleted file mode 100644 index 7e094e1813..0000000000 --- a/third_party/ffmpeg/larch64/lib/libavcodec.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9cbbf4f95193267b3799652b8c86975642a22ad6c91b1df40e24ec602d315368 -size 2368942 diff --git a/third_party/ffmpeg/larch64/lib/libavformat.a b/third_party/ffmpeg/larch64/lib/libavformat.a deleted file mode 100644 index 0cfd4d2df4..0000000000 --- a/third_party/ffmpeg/larch64/lib/libavformat.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f12e0f2a24ff87f328b4045b91d6453133588c2972a96980a05fc6339ff27eec -size 946966 diff --git a/third_party/ffmpeg/larch64/lib/libavutil.a b/third_party/ffmpeg/larch64/lib/libavutil.a deleted file mode 100644 index 0270811548..0000000000 --- a/third_party/ffmpeg/larch64/lib/libavutil.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c0944c03c21379aa6c264e0e1b59538789e0a15c6c77e5ac0ef8d571cb6f5b01 -size 891284 diff --git a/third_party/ffmpeg/larch64/lib/libx264.a b/third_party/ffmpeg/larch64/lib/libx264.a deleted file mode 100644 index 5a95720c7f..0000000000 --- a/third_party/ffmpeg/larch64/lib/libx264.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ceeac8654bb25feea79f64e91197e20a26fee4c2c36af6eae9f13943d8acf2c1 -size 2222366 diff --git a/third_party/ffmpeg/x86_64/lib/libavcodec.a b/third_party/ffmpeg/x86_64/lib/libavcodec.a deleted file mode 100644 index 00a4f4b48f..0000000000 --- a/third_party/ffmpeg/x86_64/lib/libavcodec.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:32ac488b287137c0537da1a81b3fa81e074bb79d9bf5af58185779f3dfe70070 -size 3267672 diff --git a/third_party/ffmpeg/x86_64/lib/libavformat.a b/third_party/ffmpeg/x86_64/lib/libavformat.a deleted file mode 100644 index 497fc2a34f..0000000000 --- a/third_party/ffmpeg/x86_64/lib/libavformat.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:53ba309c4adffc993671f4700e316fd5886f28aead91f051261ae1ffe8dbb03e -size 919414 diff --git a/third_party/ffmpeg/x86_64/lib/libavutil.a b/third_party/ffmpeg/x86_64/lib/libavutil.a deleted file mode 100644 index 0fc4523171..0000000000 --- a/third_party/ffmpeg/x86_64/lib/libavutil.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e4fed041aafbd9672bfff2b8988256ebd3fd77892c4ecdef705c7b81173a3c56 -size 1022522 diff --git a/third_party/ffmpeg/x86_64/lib/libx264.a b/third_party/ffmpeg/x86_64/lib/libx264.a deleted file mode 100644 index 84b9d47425..0000000000 --- a/third_party/ffmpeg/x86_64/lib/libx264.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e17625b8355736b3d5a8bf8ef49d10b8a531783b2c74d41246be7a35fdce3dff -size 3065330 diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index eb6f15817d..89be3cceb2 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -16,7 +16,7 @@ qt_libs = ['qt_util'] + base_libs cabana_env = qt_env.Clone() -cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, 'avformat', 'avcodec', 'avutil', 'x264', 'bz2', 'zstd', 'curl', 'yuv', 'usb-1.0'] + qt_libs +cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'zstd', 'curl', 'yuv', 'usb-1.0'] + qt_libs opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../opendbc/dbc").abspath) cabana_env['CXXFLAGS'] += [opendbc_path] diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index 6abc10c8e8..f33569704a 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -32,6 +32,12 @@ function install_ubuntu_common_requirements() { libcurl4-openssl-dev \ git \ git-lfs \ + ffmpeg \ + libavformat-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavutil-dev \ + libavfilter-dev \ libbz2-dev \ libeigen3-dev \ libffi-dev \ diff --git a/tools/mac_setup.sh b/tools/mac_setup.sh index b5ee988cd6..19ef77e01c 100755 --- a/tools/mac_setup.sh +++ b/tools/mac_setup.sh @@ -38,6 +38,7 @@ brew "zlib" brew "capnp" brew "coreutils" brew "eigen" +brew "ffmpeg" brew "glfw" brew "libarchive" brew "libusb" diff --git a/tools/replay/SConscript b/tools/replay/SConscript index 013cf76584..136c4119f6 100644 --- a/tools/replay/SConscript +++ b/tools/replay/SConscript @@ -17,7 +17,7 @@ if arch != "Darwin": replay_lib_src.append("qcom_decoder.cc") replay_lib = replay_env.Library("replay", replay_lib_src, LIBS=base_libs, FRAMEWORKS=base_frameworks) Export('replay_lib') -replay_libs = [replay_lib, 'avformat', 'avcodec', 'avutil', 'bz2', 'zstd', 'curl', 'yuv', 'ncurses', 'x264'] + base_libs +replay_libs = [replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'zstd', 'curl', 'yuv', 'ncurses'] + base_libs replay_env.Program("replay", ["main.cc"], LIBS=replay_libs, FRAMEWORKS=base_frameworks) if GetOption('extras'): From b622e3e0a705fb7f13fff30195f416dc0b7ff758 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 17 Sep 2025 16:33:48 -0700 Subject: [PATCH 021/341] raylib: generic click callback (#36166) * not to be used outside * same * rm * fix that * another fix * ehh probably better to still have * optional --- selfdrive/ui/onroad/augmented_road_view.py | 5 +++- system/ui/widgets/__init__.py | 33 +++++++++++++--------- system/ui/widgets/button.py | 11 ++------ 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index dbc1919221..f012e8b06b 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -102,10 +102,13 @@ class AugmentedRoadView(CameraView): # Handle click events if no HUD interaction occurred if not self._hud_renderer.handle_mouse_event(): - if self._click_callback and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): + if self._click_callback is not None and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): if rl.check_collision_point_rec(rl.get_mouse_position(), self._content_rect): self._click_callback() + def _handle_mouse_release(self, _): + pass + def _draw_border(self, rect: rl.Rectangle): border_color = BORDER_COLORS.get(ui_state.status, BORDER_COLORS[UIStatus.DISENGAGED]) rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, border_color) diff --git a/system/ui/widgets/__init__.py b/system/ui/widgets/__init__.py index cca2cec7ad..6372b1813d 100644 --- a/system/ui/widgets/__init__.py +++ b/system/ui/widgets/__init__.py @@ -15,12 +15,13 @@ class Widget(abc.ABC): def __init__(self): self._rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) self._parent_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) - self._is_pressed = [False] * MAX_TOUCH_SLOTS + self.__is_pressed = [False] * MAX_TOUCH_SLOTS # if current mouse/touch down started within the widget's rectangle - self._tracking_is_pressed = [False] * MAX_TOUCH_SLOTS + self.__tracking_is_pressed = [False] * MAX_TOUCH_SLOTS self._enabled: bool | Callable[[], bool] = True self._is_visible: bool | Callable[[], bool] = True self._touch_valid_callback: Callable[[], bool] | None = None + self._click_callback: Callable[[], None] | None = None self._multi_touch = False @property @@ -40,7 +41,7 @@ class Widget(abc.ABC): @property def is_pressed(self) -> bool: - return any(self._is_pressed) + return any(self.__is_pressed) @property def enabled(self) -> bool: @@ -56,6 +57,10 @@ class Widget(abc.ABC): def set_visible(self, visible: bool | Callable[[], bool]) -> None: self._is_visible = visible + def set_click_callback(self, click_callback: Callable[[], None] | None) -> None: + """Set a callback to be called when the widget is clicked.""" + self._click_callback = click_callback + def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: """Set a callback to determine if the widget can be clicked.""" self._touch_valid_callback = touch_callback @@ -91,28 +96,28 @@ class Widget(abc.ABC): # Allows touch to leave the rect and come back in focus if mouse did not release if mouse_event.left_pressed and self._touch_valid(): if rl.check_collision_point_rec(mouse_event.pos, self._rect): - self._is_pressed[mouse_event.slot] = True - self._tracking_is_pressed[mouse_event.slot] = True + self.__is_pressed[mouse_event.slot] = True + self.__tracking_is_pressed[mouse_event.slot] = True # Callback such as scroll panel signifies user is scrolling elif not self._touch_valid(): - self._is_pressed[mouse_event.slot] = False - self._tracking_is_pressed[mouse_event.slot] = False + self.__is_pressed[mouse_event.slot] = False + self.__tracking_is_pressed[mouse_event.slot] = False elif mouse_event.left_released: - if self._is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): + if self.__is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): self._handle_mouse_release(mouse_event.pos) - self._is_pressed[mouse_event.slot] = False - self._tracking_is_pressed[mouse_event.slot] = False + self.__is_pressed[mouse_event.slot] = False + self.__tracking_is_pressed[mouse_event.slot] = False # Mouse/touch is still within our rect elif rl.check_collision_point_rec(mouse_event.pos, self._rect): - if self._tracking_is_pressed[mouse_event.slot]: - self._is_pressed[mouse_event.slot] = True + if self.__tracking_is_pressed[mouse_event.slot]: + self.__is_pressed[mouse_event.slot] = True # Mouse/touch left our rect but may come back into focus later elif not rl.check_collision_point_rec(mouse_event.pos, self._rect): - self._is_pressed[mouse_event.slot] = False + self.__is_pressed[mouse_event.slot] = False return ret @@ -128,6 +133,8 @@ class Widget(abc.ABC): def _handle_mouse_release(self, mouse_pos: MousePos) -> bool: """Optionally handle mouse release events.""" + if self._click_callback: + self._click_callback() return False def show_event(self): diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index a5dab19789..eb38f20597 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -165,7 +165,7 @@ def gui_button( class Button(Widget): def __init__(self, text: str, - click_callback: Callable[[], None] = None, + click_callback: Callable[[], None] | None = None, font_size: int = DEFAULT_BUTTON_FONT_SIZE, font_weight: FontWeight = FontWeight.MEDIUM, button_style: ButtonStyle = ButtonStyle.NORMAL, @@ -190,10 +190,6 @@ class Button(Widget): def set_text(self, text): self._label.set_text(text) - def _handle_mouse_release(self, mouse_pos: MousePos): - if self._click_callback and self.enabled: - self._click_callback() - def _update_state(self): if self.enabled: self._label.set_text_color(BUTTON_TEXT_COLOR[self._button_style]) @@ -215,7 +211,7 @@ class ButtonRadio(Button): def __init__(self, text: str, icon, - click_callback: Callable[[], None] = None, + click_callback: Callable[[], None] | None = None, font_size: int = DEFAULT_BUTTON_FONT_SIZE, text_alignment: TextAlignment = TextAlignment.LEFT, border_radius: int = 10, @@ -230,9 +226,8 @@ class ButtonRadio(Button): self.selected = False def _handle_mouse_release(self, mouse_pos: MousePos): + super()._handle_mouse_release(mouse_pos) self.selected = not self.selected - if self._click_callback: - self._click_callback() def _update_state(self): if self.selected: From 30c388aea8961113555211201c6298099d53e4df Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 18 Sep 2025 00:07:06 -0700 Subject: [PATCH 022/341] fix `is_dirty` when switching branch with `updated` (#36162) * clean * fix --- system/updated/updated.py | 2 ++ system/version.py | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/system/updated/updated.py b/system/updated/updated.py index a80a663ec9..f9fad5f6f5 100755 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -113,6 +113,7 @@ def setup_git_options(cwd: str) -> None: ("protocol.version", "2"), ("gc.auto", "0"), ("gc.autoDetach", "false"), + ("remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"), ] for option, value in git_cfg: run(["git", "config", option, value], cwd) @@ -389,6 +390,7 @@ class Updater: cloudlog.info("git reset in progress") cmds = [ ["git", "checkout", "--force", "--no-recurse-submodules", "-B", branch, "FETCH_HEAD"], + ["git", "branch", "--set-upstream-to", f"origin/{branch}"], ["git", "reset", "--hard"], ["git", "clean", "-xdff"], ["git", "submodule", "sync"], diff --git a/system/version.py b/system/version.py index 5e4fcf5c33..e32c3d6033 100755 --- a/system/version.py +++ b/system/version.py @@ -37,9 +37,7 @@ def is_prebuilt(path: str = BASEDIR) -> bool: @cache def is_dirty(cwd: str = BASEDIR) -> bool: - origin = get_origin() - branch = get_branch() - if not origin or not branch: + if not get_origin() or not get_short_branch(): return True dirty = False @@ -52,6 +50,9 @@ def is_dirty(cwd: str = BASEDIR) -> bool: except subprocess.CalledProcessError: pass + branch = get_branch() + if not branch: + return True dirty = (subprocess.call(["git", "diff-index", "--quiet", branch, "--"], cwd=cwd)) != 0 except subprocess.CalledProcessError: cloudlog.exception("git subprocess failed while checking dirty") From 3751d9cf515d1e00656b0c575d49ff9020e22f3c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 18 Sep 2025 08:22:15 -0700 Subject: [PATCH 023/341] Remove libsystemd-dev from Ubuntu dependencies (#36167) Removed 'libsystemd-dev' from the list of dependencies. --- tools/install_ubuntu_dependencies.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index f33569704a..6ab9b30abb 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -53,7 +53,6 @@ function install_ubuntu_common_requirements() { libzmq3-dev \ libzstd-dev \ libsqlite3-dev \ - libsystemd-dev \ locales \ opencl-headers \ ocl-icd-libopencl1 \ From 852598fa0a8feef138ad96acadaf6715cf9f15c2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 18 Sep 2025 08:34:28 -0700 Subject: [PATCH 024/341] fix mac build (#36168) --- SConstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/SConstruct b/SConstruct index 5b13bd635a..d8a390f5df 100644 --- a/SConstruct +++ b/SConstruct @@ -116,6 +116,7 @@ else: f"#third_party/acados/{arch}/lib", f"{brew_prefix}/lib", f"{brew_prefix}/opt/openssl@3.0/lib", + f"{brew_prefix}/opt/llvm/lib/c++", "/System/Library/Frameworks/OpenGL.framework/Libraries", ] From c6a2c9912307144500d2bf6b5c58a6002573a20b Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 18 Sep 2025 09:17:26 -0700 Subject: [PATCH 025/341] prep for vendoring (#36169) * prep for vendoring * less stuff * comment --- tools/install_ubuntu_dependencies.sh | 24 ++++++++++++------------ tools/mac_setup.sh | 2 -- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index 6ab9b30abb..facc9eb9f6 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -20,40 +20,43 @@ fi # Install common packages function install_ubuntu_common_requirements() { $SUDO apt-get update + + # normal stuff, mostly for the bare docker image $SUDO apt-get install -y --no-install-recommends \ ca-certificates \ clang \ build-essential \ - gcc-arm-none-eabi \ - liblzma-dev \ - capnproto \ - libcapnp-dev \ curl \ + libssl-dev \ libcurl4-openssl-dev \ + locales \ git \ git-lfs \ + xvfb + + # TODO: vendor the rest of these in third_party/ + $SUDO apt-get install -y --no-install-recommends \ + gcc-arm-none-eabi \ + capnproto \ + libcapnp-dev \ ffmpeg \ libavformat-dev \ libavcodec-dev \ libavdevice-dev \ libavutil-dev \ libavfilter-dev \ - libbz2-dev \ libeigen3-dev \ libffi-dev \ - libglew-dev \ libgles2-mesa-dev \ libglfw3-dev \ libglib2.0-0 \ libjpeg-dev \ libqt5charts5-dev \ libncurses5-dev \ - libssl-dev \ libusb-1.0-0-dev \ libzmq3-dev \ libzstd-dev \ libsqlite3-dev \ - locales \ opencl-headers \ ocl-icd-libopencl1 \ ocl-icd-opencl-dev \ @@ -62,8 +65,7 @@ function install_ubuntu_common_requirements() { libqt5svg5-dev \ libqt5serialbus5-dev \ libqt5x11extras5-dev \ - libqt5opengl5-dev \ - xvfb + libqt5opengl5-dev } # Install Ubuntu 24.04 LTS packages @@ -73,8 +75,6 @@ function install_ubuntu_lts_latest_requirements() { $SUDO apt-get install -y --no-install-recommends \ g++-12 \ qtbase5-dev \ - qtchooser \ - qt5-qmake \ qtbase5-dev-tools \ python3-dev \ python3-venv diff --git a/tools/mac_setup.sh b/tools/mac_setup.sh index 19ef77e01c..430fc95222 100755 --- a/tools/mac_setup.sh +++ b/tools/mac_setup.sh @@ -34,13 +34,11 @@ fi brew bundle --file=- <<-EOS brew "git-lfs" -brew "zlib" brew "capnp" brew "coreutils" brew "eigen" brew "ffmpeg" brew "glfw" -brew "libarchive" brew "libusb" brew "libtool" brew "llvm" From b637ad49d992d367aaf34c7138e5ad11a5d41968 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 18 Sep 2025 09:25:41 -0700 Subject: [PATCH 026/341] vendor all fonts (#36170) add noto color --- selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf | 3 +++ tools/mac_setup.sh | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf diff --git a/selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf b/selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf new file mode 100644 index 0000000000..2579d30f65 --- /dev/null +++ b/selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69f216a4ec672bb910d652678301ffe3094c44e5d03276e794ef793d936a1f1d +size 25096376 diff --git a/tools/mac_setup.sh b/tools/mac_setup.sh index 430fc95222..0ae0b35359 100755 --- a/tools/mac_setup.sh +++ b/tools/mac_setup.sh @@ -48,7 +48,6 @@ brew "zeromq" cask "gcc-arm-embedded" brew "portaudio" brew "gcc@13" -cask "font-noto-color-emoji" EOS echo "[ ] finished brew install t=$SECONDS" From c7a9ea2bf4d43e6c565d22cef22ffdf0ce8cedc5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 18 Sep 2025 10:59:03 -0700 Subject: [PATCH 027/341] add back libbz2-dev (#36172) * add back libbz2-dev * try this * revert --- tools/install_ubuntu_dependencies.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index facc9eb9f6..97e06bcc6b 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -45,6 +45,7 @@ function install_ubuntu_common_requirements() { libavdevice-dev \ libavutil-dev \ libavfilter-dev \ + libbz2-dev \ libeigen3-dev \ libffi-dev \ libgles2-mesa-dev \ From d05cb31e2e916fba41ba8167030945f427fd811b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 18 Sep 2025 17:03:16 -0700 Subject: [PATCH 028/341] raylib: fix pairing url --- common/api.py | 4 +++- selfdrive/ui/widgets/pairing_dialog.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/common/api.py b/common/api.py index ac231400a4..005655b21d 100644 --- a/common/api.py +++ b/common/api.py @@ -22,7 +22,7 @@ class Api: def request(self, method, endpoint, timeout=None, access_token=None, **params): return api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params) - def get_token(self, expiry_hours=1): + def get_token(self, payload_extra=None, expiry_hours=1): now = datetime.now(UTC).replace(tzinfo=None) payload = { 'identity': self.dongle_id, @@ -30,6 +30,8 @@ class Api: 'iat': now, 'exp': now + timedelta(hours=expiry_hours) } + if payload_extra is not None: + payload.update(payload_extra) token = jwt.encode(payload, self.private_key, algorithm='RS256') if isinstance(token, bytes): token = token.decode('utf8') diff --git a/selfdrive/ui/widgets/pairing_dialog.py b/selfdrive/ui/widgets/pairing_dialog.py index 63298914ea..79d05eaf42 100644 --- a/selfdrive/ui/widgets/pairing_dialog.py +++ b/selfdrive/ui/widgets/pairing_dialog.py @@ -24,11 +24,11 @@ class PairingDialog: def _get_pairing_url(self) -> str: try: dongle_id = self.params.get("DongleId") or "" - token = Api(dongle_id).get_token() + token = Api(dongle_id).get_token({'pair': True}) except Exception as e: cloudlog.warning(f"Failed to get pairing token: {e}") token = "" - return f"https://connect.comma.ai/setup?token={token}" + return f"https://connect.comma.ai/?pair={token}" def _generate_qr_code(self) -> None: try: From 2a5de8e0f81cace1372b3fdf65305db15fdfc7c5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 18 Sep 2025 17:11:53 -0700 Subject: [PATCH 029/341] raylib: fix shader antialiasing (#36176) * fix * np --- system/ui/lib/shader_polygon.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index 39bc0d5aa4..d03ad5d8c6 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -99,19 +99,13 @@ float distanceToEdge(vec2 p) { void main() { vec2 pixel = fragTexCoord * resolution; - // Compute pixel size for anti-aliasing - vec2 pixelGrad = vec2(dFdx(pixel.x), dFdy(pixel.y)); - float pixelSize = length(pixelGrad); - float aaWidth = max(0.5, pixelSize * 1.5); - bool inside = isPointInsidePolygon(pixel); - if (inside) { - finalColor = useGradient == 1 ? getGradientColor(pixel) : fillColor; - return; - } + float sd = (inside ? 1.0 : -1.0) * distanceToEdge(pixel); - float sd = -distanceToEdge(pixel); - float alpha = smoothstep(-aaWidth, aaWidth, sd); + // ~1 pixel wide anti-aliasing + float w = max(0.75, fwidth(sd)); + + float alpha = smoothstep(-w, w, sd); if (alpha > 0.0){ vec4 color = useGradient == 1 ? getGradientColor(pixel) : fillColor; finalColor = vec4(color.rgb, color.a * alpha); From c5999702ae95ca538fa71bdaa8f0a98137bf18b4 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Fri, 19 Sep 2025 19:40:20 -0400 Subject: [PATCH 030/341] Honda: Add 2026 Honda Passport to release (#36179) * bump opendbc * regen CARS.md * update RELEASES.md --- RELEASES.md | 1 + docs/CARS.md | 3 ++- opendbc_repo | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 7c0b967abd..f5cf630607 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -8,6 +8,7 @@ Version 0.10.1 (2025-09-08) * Honda City 2023 support thanks to vanillagorillaa and drFritz! * Honda N-Box 2018 support thanks to miettal! * Honda Odyssey 2021-25 support thanks to csouers and MVL! +* Honda Passport 2026 support thanks to vanillagorillaa and MVL! Version 0.10.0 (2025-08-05) ======================== diff --git a/docs/CARS.md b/docs/CARS.md index 7d3f7a9442..22cb0d46c8 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 323 Supported Cars +# 324 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -102,6 +102,7 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Odyssey 2021-25|All|openpilot available[1](#footnotes)|0 mph|43 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Passport 2026|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Pilot 2016-22|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Pilot 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Ridgeline 2017-25|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index c2ba07083c..c9e7868cb8 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit c2ba07083c1cfbb0c5b86469bd7b87090291a0d3 +Subproject commit c9e7868cb830978c2254ef9f012b843bb4f0ddd7 From 5164555c4f4d6ba2cc6e7c12be785c58f7671087 Mon Sep 17 00:00:00 2001 From: mvl-boston Date: Fri, 19 Sep 2025 19:46:05 -0400 Subject: [PATCH 031/341] Steering Assist warning clarification (#36135) * clarifying warning message * more clarity with steering assist warnings --- selfdrive/selfdrived/events.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index d4a00115d4..c865cc94a6 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -240,7 +240,7 @@ def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging. def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: return Alert( - f"Steer Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}", + f"Steer Assist Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 0.4) @@ -489,7 +489,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { EventName.steerTempUnavailableSilent: { ET.WARNING: Alert( - "Steering Temporarily Unavailable", + "Steering Assist Temporarily Unavailable", "", AlertStatus.userPrompt, AlertSize.small, Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.8), @@ -735,7 +735,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { }, EventName.steerTempUnavailable: { - ET.SOFT_DISABLE: soft_disable_alert("Steering Temporarily Unavailable"), + ET.SOFT_DISABLE: soft_disable_alert("Steering Assist Temporarily Unavailable"), ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable"), }, From 6ed8f07cb629a6cdcb71739b2022b1281525c211 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 19 Sep 2025 16:46:23 -0700 Subject: [PATCH 032/341] Capnp memoryview (#36163) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * lock * bump opendbc * fix one * and * Add a memoryview fallback in webrtcd * fix * revert * rerevert * bump to master --------- Co-authored-by: Kacper Rączy --- opendbc_repo | 2 +- pyproject.toml | 4 +- system/loggerd/tests/test_loggerd.py | 2 +- system/webrtc/device/video.py | 2 +- system/webrtc/webrtcd.py | 11 +- uv.lock | 773 ++++++++++++++------------- 6 files changed, 406 insertions(+), 388 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index c9e7868cb8..0c8676f74c 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit c9e7868cb830978c2254ef9f012b843bb4f0ddd7 +Subproject commit 0c8676f74cf386ac914faa80a6b14303693ccde9 diff --git a/pyproject.toml b/pyproject.toml index 489876f78c..3d2c2a6f74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ # core "cffi", "scons", - "pycapnp==2.1.0", + "pycapnp", "Cython", "setuptools", "numpy >=2.0", @@ -119,7 +119,7 @@ dev = [ "tabulate", "types-requests", "types-tabulate", - "raylib", + "raylib==5.5.0.2", # 5.5.0.3 has regression with gui_set_style ] tools = [ diff --git a/system/loggerd/tests/test_loggerd.py b/system/loggerd/tests/test_loggerd.py index c6a4b12e63..d63d7d84eb 100644 --- a/system/loggerd/tests/test_loggerd.py +++ b/system/loggerd/tests/test_loggerd.py @@ -178,7 +178,7 @@ class TestLoggerd: assert logged_params['AccessToken'] == b'', f"DONT_LOG param value was logged: {repr(logged_params['AccessToken'])}" for param_key, initData_key, v in fake_params: assert getattr(initData, initData_key) == v - assert logged_params[param_key].decode() == v + assert logged_params[param_key].tobytes().decode() == v @pytest.mark.xdist_group("camera_encoder_tests") # setting xdist group ensures tests are run in same worker, prevents encoderd from crashing def test_rotation(self): diff --git a/system/webrtc/device/video.py b/system/webrtc/device/video.py index 1bca909294..ffa1565cbf 100644 --- a/system/webrtc/device/video.py +++ b/system/webrtc/device/video.py @@ -30,7 +30,7 @@ class LiveStreamVideoStreamTrack(TiciVideoStreamTrack): evta = getattr(msg, msg.which()) - packet = av.Packet(evta.header + evta.data) + packet = av.Packet(evta.header.tobytes() + evta.data.tobytes()) packet.time_base = self._time_base packet.pts = self._pts diff --git a/system/webrtc/webrtcd.py b/system/webrtc/webrtcd.py index fb93e565ff..34abe8ab4b 100755 --- a/system/webrtc/webrtcd.py +++ b/system/webrtc/webrtcd.py @@ -22,6 +22,13 @@ from openpilot.system.webrtc.schema import generate_field from cereal import messaging, log +# pycapnp 2.2.0+ is using memoryview instead of bytes for data fields +def memoryview_fallback(obj): + if isinstance(obj, memoryview): + return obj.tobytes().decode() + return obj + + class CerealOutgoingMessageProxy: def __init__(self, sm: messaging.SubMaster): self.sm = sm @@ -37,6 +44,8 @@ class CerealOutgoingMessageProxy: msg_dict = [self.to_json(msg) for msg in msg_content] elif isinstance(msg_content, bytes): msg_dict = msg_content.decode() + elif isinstance(msg_content, memoryview): + msg_dict = msg_content.tobytes().decode() else: msg_dict = msg_content @@ -51,7 +60,7 @@ class CerealOutgoingMessageProxy: msg_dict = self.to_json(self.sm[service]) mono_time, valid = self.sm.logMonoTime[service], self.sm.valid[service] outgoing_msg = {"type": service, "logMonoTime": mono_time, "valid": valid, "data": msg_dict} - encoded_msg = json.dumps(outgoing_msg).encode() + encoded_msg = json.dumps(outgoing_msg, default=memoryview_fallback).encode() for channel in self.channels: channel.send(encoded_msg) diff --git a/uv.lock b/uv.lock index 25d2b626cf..0b2bca29d4 100644 --- a/uv.lock +++ b/uv.lock @@ -152,21 +152,21 @@ wheels = [ [[package]] name = "azure-core" -version = "1.35.0" +version = "1.35.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/89/f53968635b1b2e53e4aad2dd641488929fef4ca9dfb0b97927fa7697ddf3/azure_core-1.35.0.tar.gz", hash = "sha256:c0be528489485e9ede59b6971eb63c1eaacf83ef53001bfe3904e475e972be5c", size = 339689, upload-time = "2025-07-03T00:55:23.496Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/6b/2653adc0f33adba8f11b1903701e6b1c10d34ce5d8e25dfa13a422f832b0/azure_core-1.35.1.tar.gz", hash = "sha256:435d05d6df0fff2f73fb3c15493bb4721ede14203f1ff1382aa6b6b2bdd7e562", size = 345290, upload-time = "2025-09-11T22:58:04.481Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/78/bf94897361fdd650850f0f2e405b2293e2f12808239046232bdedf554301/azure_core-1.35.0-py3-none-any.whl", hash = "sha256:8db78c72868a58f3de8991eb4d22c4d368fae226dac1002998d6c50437e7dad1", size = 210708, upload-time = "2025-07-03T00:55:25.238Z" }, + { url = "https://files.pythonhosted.org/packages/27/52/805980aa1ba18282077c484dba634ef0ede1e84eec8be9c92b2e162d0ed6/azure_core-1.35.1-py3-none-any.whl", hash = "sha256:12da0c9e08e48e198f9158b56ddbe33b421477e1dc98c2e1c8f9e254d92c468b", size = 211800, upload-time = "2025-09-11T22:58:06.281Z" }, ] [[package]] name = "azure-identity" -version = "1.24.0" +version = "1.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, @@ -175,9 +175,9 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/44/f3ee20bacb220b6b4a2b0a6cf7e742eecb383a5ccf604dd79ec27c286b7e/azure_identity-1.24.0.tar.gz", hash = "sha256:6c3a40b2a70af831e920b89e6421e8dcd4af78a0cb38b9642d86c67643d4930c", size = 271630, upload-time = "2025-08-07T22:27:36.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/9e/4c9682a286c3c89e437579bd9f64f311020e5125c1321fd3a653166b5716/azure_identity-1.25.0.tar.gz", hash = "sha256:4177df34d684cddc026e6cf684e1abb57767aa9d84e7f2129b080ec45eee7733", size = 278507, upload-time = "2025-09-12T01:30:04.418Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/74/17428cb429e8d52f6d0d69ed685f4760a545cb0156594963a9337b53b6c9/azure_identity-1.24.0-py3-none-any.whl", hash = "sha256:9e04997cde0ab02ed66422c74748548e620b7b29361c72ce622acab0267ff7c4", size = 187890, upload-time = "2025-08-07T22:27:38.033Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/81683b6756676a22e037b209695b08008258e603f7e47c56834029c5922a/azure_identity-1.25.0-py3-none-any.whl", hash = "sha256:becaec086bbdf8d1a6aa4fb080c2772a0f824a97d50c29637ec8cc4933f1e82d", size = 190861, upload-time = "2025-09-12T01:30:06.474Z" }, ] [[package]] @@ -197,25 +197,25 @@ wheels = [ [[package]] name = "casadi" -version = "3.7.1" +version = "3.7.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/21/1b4ea75dbddfcdbc7cd70d83147dde72899667097c0c05b288fd7015ef10/casadi-3.7.1.tar.gz", hash = "sha256:12577155cd3cd79ba162381bfed6add1541bc770ba3f1f1334e4eb159d9b39ba", size = 6065586, upload-time = "2025-07-23T21:47:56.483Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/62/1e98662024915ecb09c6894c26a3f497f4afa66570af3f53db4651fc45f1/casadi-3.7.2.tar.gz", hash = "sha256:b4d7bd8acdc4180306903ae1c9eddaf41be2a3ae2fa7154c57174ae64acdc60d", size = 6053600, upload-time = "2025-09-10T10:05:49.521Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/4d/cfe092b2deb65dbf913d38148e30dfe41bbe9f289f892ea7a5d3526532d2/casadi-3.7.1-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:c13fafdff0cdf73392fe2002245befe7496b5ac99b70adb64babff0099068c8a", size = 47100775, upload-time = "2025-07-23T19:56:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/13/41/c4ad627e447a083fbfd2826f1f4684223f0072fdc49f58157ca6a6ce0a77/casadi-3.7.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:74d9c6bad3c7cb347494d52cc3e6faf3fbf3300046fab0e1ddc9439d3fd68486", size = 42282155, upload-time = "2025-07-23T19:59:11.191Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e2/53733933d454657e319cc8074e004ec46660c4797cbffcaf7695a874b0da/casadi-3.7.1-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:6fbc0803e6c7ffb9ba9a41ba38e7c1dadee73835edbacacce3530343f078f3f9", size = 47272638, upload-time = "2025-07-23T20:03:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/e1/72/8002d4fe64325842eb6a7f7ac1e4574c02584e85e5b9d93a08a789095505/casadi-3.7.1-cp311-none-manylinux2014_i686.whl", hash = "sha256:38a16b0c7caff5297df91c7e3a65091824979367e6fc8e6eb0e5ab9f1f832af8", size = 72418203, upload-time = "2025-07-23T20:07:18.681Z" }, - { url = "https://files.pythonhosted.org/packages/96/e5/065287b78c4f6a00a010d7c3f5316df1b95f7c5bda644f2151b54a79a964/casadi-3.7.1-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:2dd75cc1b5ca8009586186e4a63e0cdd83a672bf308edc75ef84f9c896cd971d", size = 75558461, upload-time = "2025-07-23T21:33:20.771Z" }, - { url = "https://files.pythonhosted.org/packages/b0/4c/084d2f3bca33d0b3d509be17c3b678f6bcc03f3409d42e88750cb1e44966/casadi-3.7.1-cp311-none-win_amd64.whl", hash = "sha256:3ab68c6dd621b5f150635bf06aa8e1697cae2fbe0137e9d47270c54d305c334c", size = 50987255, upload-time = "2025-07-23T21:34:20.349Z" }, - { url = "https://files.pythonhosted.org/packages/e1/a3/65942d3e0f7589a72e69dd9dee575963ecf51504e985f132bce8b0d24988/casadi-3.7.1-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:4a29ae3212b0ae6931d439ca3bfa2ac279826e99c2c9a443704e84021933ab76", size = 47102059, upload-time = "2025-07-23T20:36:47.174Z" }, - { url = "https://files.pythonhosted.org/packages/2e/f4/dc371199583f6b154ba0966c4132418737a2d2e019c9f6ad227ebd279684/casadi-3.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:70edf1df3fc83401eb5a2f7053ae8394f4e612e61c2ad6de088e3d76d0ec845e", size = 42282702, upload-time = "2025-07-23T20:39:41.499Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ff/5f8a9378cd7b322d1dcc8b8aa9fbc454ad57152b754c94b50ffdc67254a0/casadi-3.7.1-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:a1717bbd07a6eb98acddf5ea5da7e78a9b85b75dbabb55190cc6b0f2812ef39d", size = 47271636, upload-time = "2025-07-23T20:42:59.961Z" }, - { url = "https://files.pythonhosted.org/packages/e1/11/e1b6a76bf288fc86dc8919cbcd2d3a654e7ead4240d9928deb8ed4ee91da/casadi-3.7.1-cp312-none-manylinux2014_i686.whl", hash = "sha256:57ae29d16af0716ebc15d161bcf0bcd97d35631b10c0e80838290d249cac80d7", size = 72410197, upload-time = "2025-07-23T21:35:35.509Z" }, - { url = "https://files.pythonhosted.org/packages/c1/76/bb43ff625066b178132172d41db0c5c962b4af9db15c88b8a23e65376d18/casadi-3.7.1-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:756f3d07d3414dc54f05ebec883ac3db6e307bfe661719d188340504b6bed420", size = 75552890, upload-time = "2025-07-23T21:37:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/11/0c/8026b0b238e00a2e63c7142505cbcd0931b4db4140d55c83962f9d8e0b02/casadi-3.7.1-cp312-none-win_amd64.whl", hash = "sha256:b228fbcd1f41eb8d5405dbc1e0ecb780daf89808392706bb77fd2b240f8a437e", size = 50984453, upload-time = "2025-07-23T21:38:18.21Z" }, + { url = "https://files.pythonhosted.org/packages/29/01/d5e3058775ec8e24a01eb74d36099493b872536ef9e39f1e49624b977778/casadi-3.7.2-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:f43b0562d05a5e6e81f1885fc4ae426c382e36eebfd8d27f1baff6052178a9b0", size = 47115880, upload-time = "2025-09-10T07:52:24.399Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cf/4af27e010d599a5419129d34fdde41637029a1cca2a40bef0965d6d52228/casadi-3.7.2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:70add3334b437b60a9bc0f864d094350f1a4fcbf9e8bafec870b61aed64674df", size = 42293337, upload-time = "2025-09-10T08:03:32.556Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4c/d1a50cc840103e00effcbaf8e911b6b3fb6ba2c8f4025466f524854968ed/casadi-3.7.2-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:392d3367a4b33cf223013dad8122a0e549da40b1702a5375f82f85b563e5c0cf", size = 47277175, upload-time = "2025-09-10T08:04:08.811Z" }, + { url = "https://files.pythonhosted.org/packages/be/29/6e5714d124e6ddafbccc3ed774ca603081caa1175c7f0e1c52484184dfb3/casadi-3.7.2-cp311-none-manylinux2014_i686.whl", hash = "sha256:2ce09e0ced6df33048dccd582b5cfa2c9ff5193b12858b2584078afc17761905", size = 72438460, upload-time = "2025-09-10T08:05:02.769Z" }, + { url = "https://files.pythonhosted.org/packages/23/32/ac1f3999273aa4aae48516f6f4b7b267e0cc70d8527866989798cb81312f/casadi-3.7.2-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:5086799a46d10ba884b72fd02c21be09dae52cbc189272354a5d424791b55f37", size = 75574474, upload-time = "2025-09-10T08:06:00.709Z" }, + { url = "https://files.pythonhosted.org/packages/68/78/7fd10709504c1757f70db3893870a891fcb9f1ec9f05e8ef2e3f3b9d7e2f/casadi-3.7.2-cp311-none-win_amd64.whl", hash = "sha256:72aa5727417d781ed216f16b5e93c6ddca5db27d83b0015a729e8ad570cdc465", size = 50994144, upload-time = "2025-09-10T08:06:42.384Z" }, + { url = "https://files.pythonhosted.org/packages/65/c8/689d085447b1966f42bdb8aa4fbebef49a09697dbee32ab02a865c17ac1b/casadi-3.7.2-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:309ea41a69c9230390d349b0dd899c6a19504d1904c0756bef463e47fb5c8f9a", size = 47116756, upload-time = "2025-09-10T07:53:00.931Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c0/3c4704394a6fd4dfb2123a4fd71ba64a001f340670a3eba45be7a19ac736/casadi-3.7.2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6033381234db810b2247d16c6352e679a009ec4365d04008fc768866e011ed58", size = 42293718, upload-time = "2025-09-10T08:07:16.415Z" }, + { url = "https://files.pythonhosted.org/packages/f3/24/4cf05469ddf8544da5e92f359f96d716a97e7482999f085a632bc4ef344a/casadi-3.7.2-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:732f2804d0766454bb75596339e4f2da6662ffb669621da0f630ed4af9e83d6a", size = 47276175, upload-time = "2025-09-10T08:08:09.29Z" }, + { url = "https://files.pythonhosted.org/packages/82/08/b5f57fea03128efd5c860673b6ac44776352e6c1af862b8177f4c503fffe/casadi-3.7.2-cp312-none-manylinux2014_i686.whl", hash = "sha256:cf17298ff0c162735bdf9bf72b765c636ae732130604017a3b52e26e35402857", size = 72430454, upload-time = "2025-09-10T08:09:10.781Z" }, + { url = "https://files.pythonhosted.org/packages/24/ab/d7233c915b12c005655437c6c4cf0ae46cbbb2b20d743cb5e4881ad3104a/casadi-3.7.2-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:cde616930fa1440ad66f1850670399423edd37354eed9b12e74b3817b98d1187", size = 75568903, upload-time = "2025-09-10T08:10:07.108Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/5b984124f539656efdf079f3d8f09d73667808ec8d0546e6bce6dc60ade6/casadi-3.7.2-cp312-none-win_amd64.whl", hash = "sha256:81d677d2b020c1307c1eb25eae15686e5de199bb066828c3eaabdfaaaf457ffd", size = 50991347, upload-time = "2025-09-10T08:10:46.629Z" }, ] [[package]] @@ -229,36 +229,38 @@ wheels = [ [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser" }, + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, ] [[package]] @@ -294,14 +296,14 @@ wheels = [ [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, ] [[package]] @@ -415,31 +417,31 @@ wheels = [ [[package]] name = "cython" -version = "3.1.3" +version = "3.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/ab/915337fb39ab4f4539a313df38fc69938df3bf14141b90d61dfd5c2919de/cython-3.1.3.tar.gz", hash = "sha256:10ee785e42328924b78f75a74f66a813cb956b4a9bc91c44816d089d5934c089", size = 3186689, upload-time = "2025-08-13T06:19:13.619Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/f6/d762df1f436a0618455d37f4e4c4872a7cd0dcfc8dec3022ee99e4389c69/cython-3.1.4.tar.gz", hash = "sha256:9aefefe831331e2d66ab31799814eae4d0f8a2d246cbaaaa14d1be29ef777683", size = 3190778, upload-time = "2025-09-16T07:20:33.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/51/54f5d1bed7b7d003d0584996a030542497aeb05b9241782c217ea71061f5/cython-3.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b3b2f6587b42efdece2d174a2aa4234da4524cc6673f3955c2e62b60c6d11fd", size = 3002973, upload-time = "2025-08-13T06:19:50.777Z" }, - { url = "https://files.pythonhosted.org/packages/05/07/b4043fed60070ee21b0d9ff3a877d2ecdc79231e55119ce852b79b690306/cython-3.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:963cf640d049fcca1cefd62d1653f859892d6dc8e4d958eb49a5babc491de6a1", size = 2865389, upload-time = "2025-08-13T06:19:52.675Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e3/67d349b5310a40f281e153e826ca24d9f7ba2997c06800eda781253dccfd/cython-3.1.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2ab05d1bf2d5522ecff35d94ca233b77db2300413597c3ca0b6448377fa4bd7c", size = 3410316, upload-time = "2025-08-13T06:19:55.217Z" }, - { url = "https://files.pythonhosted.org/packages/49/c4/84aae921135174111091547fa726ea5f8bba718cd12b2589a70c2aab8d5c/cython-3.1.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd517e3be052fb49443585b01f02f46080b3408e32c1108a0fdc4cc25b3c9d30", size = 3189568, upload-time = "2025-08-13T06:19:57.503Z" }, - { url = "https://files.pythonhosted.org/packages/e2/85/1bf18883f1a1f656ad83a671e54553caedb1ee2f39a3e792a14aa82557a2/cython-3.1.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a48e2180d74e3c528561d85b48f9a939a429537f9ea8aac7fb16180e7bff47e2", size = 3312649, upload-time = "2025-08-13T06:19:59.894Z" }, - { url = "https://files.pythonhosted.org/packages/68/da/cc1373decc0d14a25f1b434f47de5e27696f3092319aa5e19fcf84157415/cython-3.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7c9daa90b15f59aa2a0d638ac1b36777a7e80122099952a0295c71190ce14bc", size = 3203821, upload-time = "2025-08-13T06:20:02.124Z" }, - { url = "https://files.pythonhosted.org/packages/0a/32/e10582d6f7b02ee63607f388dbbd34e329c54559c85961ddc5c655266519/cython-3.1.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:08ac646ff42781827f23b7a9b61669cdb92055f52724cd8cbe0e1defc56fce2e", size = 3426853, upload-time = "2025-08-13T06:20:04.474Z" }, - { url = "https://files.pythonhosted.org/packages/e0/da/5b0d1b5a4c7622ec812ad9374c9c6b426d69818f13f51e36f4f25ca01c86/cython-3.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bb0e0e7fceaffa22e4dc9600f7f75998eef5cc6ac5a8c0733b482851ba765ef2", size = 3328872, upload-time = "2025-08-13T06:20:06.834Z" }, - { url = "https://files.pythonhosted.org/packages/b0/5f/f102f6c8d27338f0baf094754c67f920828a19612053abc903e66f84506f/cython-3.1.3-cp311-cp311-win32.whl", hash = "sha256:42b1c3ebe36a52e2a8e939c0651e9ca5d30b81d03f800bbf0499deb0503ab565", size = 2480850, upload-time = "2025-08-13T06:20:08.988Z" }, - { url = "https://files.pythonhosted.org/packages/b7/60/415d0f0f7c2e227707baec11c968387d33507d2eb7f26a2950a323c705e5/cython-3.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:34a973844998281951bf54cdd0b6a9946ba03ba94580820738583a00da167d8f", size = 2712560, upload-time = "2025-08-13T06:20:11.877Z" }, - { url = "https://files.pythonhosted.org/packages/79/26/f433fdfd5182b9231819a99acc49a1f856669532306e7a38dce63ba7297e/cython-3.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:849ef3d15d4354e5f74cdb6d3c80d80b03209b3bf1f4ff93315890b19da18944", size = 3014237, upload-time = "2025-08-13T06:20:13.77Z" }, - { url = "https://files.pythonhosted.org/packages/e5/6c/1bebf44f5f177f8c750e608f82c08cd699b8f28cc233e799379bfcf2a2c2/cython-3.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93dd0f62a3f8e93166d8584f8b243180d681ba8fed1f530b55d5f70c348c5797", size = 2844261, upload-time = "2025-08-13T06:20:15.619Z" }, - { url = "https://files.pythonhosted.org/packages/c7/74/983005ce5954f6dc8360b105a561e51a60ea619c53296afac98253d1c9d7/cython-3.1.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ff4a2cb84798faffb3988bd94636c3ad31a95ff44ef017f09121abffc56f84cf", size = 3388846, upload-time = "2025-08-13T06:20:17.679Z" }, - { url = "https://files.pythonhosted.org/packages/68/50/dbe7edefe9b652bc72d49da07785173e89197b9a2d64a2ac45da9795eccf/cython-3.1.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b05319e36f34d5388deea5cc2bcfd65f9ebf76f4ea050829421a69625dbba4a", size = 3167022, upload-time = "2025-08-13T06:20:19.863Z" }, - { url = "https://files.pythonhosted.org/packages/4a/55/b50548b77203e22262002feae23a172f95282b4b8deb5ad104f08e3dc20d/cython-3.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac902a17934a6da46f80755f49413bc4c03a569ae3c834f5d66da7288ba7e6c", size = 3317782, upload-time = "2025-08-13T06:20:21.962Z" }, - { url = "https://files.pythonhosted.org/packages/f6/1b/20a97507d528dc330d67be4fbad6bc767c681d56192bce8f7117a187b2ad/cython-3.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7d7a555a864b1b08576f9e8a67f3789796a065837544f9f683ebf3188012fdbd", size = 3179818, upload-time = "2025-08-13T06:20:24.419Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/7b32a19c4c6bb0e2cc7ae52269b6b23a42f67f730e4abd4f8dca63660f7a/cython-3.1.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b827ce7d97ef8624adcf2bdda594b3dcb6c9b4f124d8f72001d8aea27d69dc1c", size = 3403206, upload-time = "2025-08-13T06:20:26.846Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e1/08cfd4c5e99f79e62d4a7b0009bbd906748633270935c8a55eee51fead76/cython-3.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7851107204085f4f02d0eb6b660ddcad2ce4846e8b7a1eaba724a0bd3cd68a6b", size = 3327837, upload-time = "2025-08-13T06:20:28.946Z" }, - { url = "https://files.pythonhosted.org/packages/23/1b/f3d384be86fa2a6e110b42f42bf97295a513197aaa5532f451c73bf5b48e/cython-3.1.3-cp312-cp312-win32.whl", hash = "sha256:ed20f1b45b2da5a4f8e71a80025bca1cdc96ba35820b0b17658a4a025be920b0", size = 2485990, upload-time = "2025-08-13T06:20:31.517Z" }, - { url = "https://files.pythonhosted.org/packages/de/f0/17cff75e3c141bda73d770f7412632f53e55778d3bfdadfeec4dd3a7d1d6/cython-3.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:dc4ca0f4dec55124cd79ddcfc555be1cbe0092cc99bcf1403621d17b9c6218bb", size = 2704002, upload-time = "2025-08-13T06:20:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/56/c8/46ac27096684f33e27dab749ef43c6b0119c6a0d852971eaefb73256dc4c/cython-3.1.3-py3-none-any.whl", hash = "sha256:d13025b34f72f77bf7f65c1cd628914763e6c285f4deb934314c922b91e6be5a", size = 1225725, upload-time = "2025-08-13T06:19:09.593Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ab/0a568bac7c4c052db4ae27edf01e16f3093cdfef04a2dfd313ef1b3c478a/cython-3.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d1d7013dba5fb0506794d4ef8947ff5ed021370614950a8d8d04e57c8c84499e", size = 3026389, upload-time = "2025-09-16T07:22:02.212Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b7/51f5566e1309215a7fef744975b2fabb56d3fdc5fa1922fd7e306c14f523/cython-3.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eed989f5c139d6550ef2665b783d86fab99372590c97f10a3c26c4523c5fce9e", size = 2955954, upload-time = "2025-09-16T07:22:03.782Z" }, + { url = "https://files.pythonhosted.org/packages/28/fd/ad8314520000fe96292fb8208c640fa862baa3053d2f3453a2acb50cafb8/cython-3.1.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3df3beb8b024dfd73cfddb7f2f7456751cebf6e31655eed3189c209b634bc2f2", size = 3412005, upload-time = "2025-09-16T07:22:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3b/e570f8bcb392e7943fc9a25d1b2d1646ef0148ff017d3681511acf6bbfdc/cython-3.1.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8354703f1168e1aaa01348940f719734c1f11298be333bdb5b94101d49677c0", size = 3191100, upload-time = "2025-09-16T07:22:07.144Z" }, + { url = "https://files.pythonhosted.org/packages/78/81/f1ea09f563ebab732542cb11bf363710e53f3842458159ea2c160788bc8e/cython-3.1.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a928bd7d446247855f54f359057ab4a32c465219c8c1e299906a483393a59a9e", size = 3313786, upload-time = "2025-09-16T07:22:09.15Z" }, + { url = "https://files.pythonhosted.org/packages/ca/17/06575eb6175a926523bada7dac1cd05cc74add96cebbf2e8b492a2494291/cython-3.1.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c233bfff4cc7b9d629eecb7345f9b733437f76dc4441951ec393b0a6e29919fc", size = 3205775, upload-time = "2025-09-16T07:22:10.745Z" }, + { url = "https://files.pythonhosted.org/packages/10/ba/61a8cf56a76ab21ddf6476b70884feff2a2e56b6d9010e1e1b1e06c46f70/cython-3.1.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e9691a2cbc2faf0cd819108bceccf9bfc56c15a06d172eafe74157388c44a601", size = 3428423, upload-time = "2025-09-16T07:22:12.404Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/42cf9239088d6b4b62c1c017c36e0e839f64c8d68674ce4172d0e0168d3b/cython-3.1.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ada319207432ea7c6691c70b5c112d261637d79d21ba086ae3726fedde79bfbf", size = 3330489, upload-time = "2025-09-16T07:22:14.576Z" }, + { url = "https://files.pythonhosted.org/packages/b5/08/36a619d6b1fc671a11744998e5cdd31790589e3cb4542927c97f3f351043/cython-3.1.4-cp311-cp311-win32.whl", hash = "sha256:dae81313c28222bf7be695f85ae1d16625aac35a0973a3af1e001f63379440c5", size = 2482410, upload-time = "2025-09-16T07:22:17.373Z" }, + { url = "https://files.pythonhosted.org/packages/6d/58/7d9ae7944bcd32e6f02d1a8d5d0c3875125227d050e235584127f2c64ffd/cython-3.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:60d2f192059ac34c5c26527f2beac823d34aaa766ef06792a3b7f290c18ac5e2", size = 2713755, upload-time = "2025-09-16T07:22:18.949Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/2939c739cfdc67ab94935a2c4fcc75638afd15e1954552655503a4112e92/cython-3.1.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0d26af46505d0e54fe0f05e7ad089fd0eed8fa04f385f3ab88796f554467bcb9", size = 3062976, upload-time = "2025-09-16T07:22:20.517Z" }, + { url = "https://files.pythonhosted.org/packages/eb/bd/a84de57fd01017bf5dba84a49aeee826db21112282bf8d76ab97567ee15d/cython-3.1.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66ac8bb5068156c92359e3f0eefa138c177d59d1a2e8a89467881fa7d06aba3b", size = 2970701, upload-time = "2025-09-16T07:22:22.644Z" }, + { url = "https://files.pythonhosted.org/packages/71/79/a09004c8e42f5be188c7636b1be479cdb244a6d8837e1878d062e4e20139/cython-3.1.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2e42714faec723d2305607a04bafb49a48a8d8f25dd39368d884c058dbcfbc", size = 3387730, upload-time = "2025-09-16T07:22:24.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bd/979f8c59e247f562642f3eb98a1b453530e1f7954ef071835c08ed2bf6ba/cython-3.1.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0fd655b27997a209a574873304ded9629de588f021154009e8f923475e2c677", size = 3167289, upload-time = "2025-09-16T07:22:26.35Z" }, + { url = "https://files.pythonhosted.org/packages/34/f8/0b98537f0b4e8c01f76d2a6cf75389987538e4d4ac9faf25836fd18c9689/cython-3.1.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9def7c41f4dc339003b1e6875f84edf059989b9c7f5e9a245d3ce12c190742d9", size = 3321099, upload-time = "2025-09-16T07:22:27.957Z" }, + { url = "https://files.pythonhosted.org/packages/f3/39/437968a2e7c7f57eb6e1144f6aca968aa15fbbf169b2d4da5d1ff6c21442/cython-3.1.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:196555584a8716bf7e017e23ca53e9f632ed493f9faa327d0718e7551588f55d", size = 3179897, upload-time = "2025-09-16T07:22:30.014Z" }, + { url = "https://files.pythonhosted.org/packages/2c/04/b3f42915f034d133f1a34e74a2270bc2def02786f9b40dc9028fbb968814/cython-3.1.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7fff0e739e07a20726484b8898b8628a7b87acb960d0fc5486013c6b77b7bb97", size = 3400936, upload-time = "2025-09-16T07:22:31.705Z" }, + { url = "https://files.pythonhosted.org/packages/21/eb/2ad9fa0896ab6cf29875a09a9f4aaea37c28b79b869a013bf9b58e4e652e/cython-3.1.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2754034fa10f95052949cd6b07eb2f61d654c1b9cfa0b17ea53a269389422e8", size = 3332131, upload-time = "2025-09-16T07:22:33.32Z" }, + { url = "https://files.pythonhosted.org/packages/3c/bf/f19283f8405e7e564c3353302a8665ea2c589be63a8e1be1b503043366a9/cython-3.1.4-cp312-cp312-win32.whl", hash = "sha256:2e0808ff3614a1dbfd1adfcbff9b2b8119292f1824b3535b4a173205109509f8", size = 2487672, upload-time = "2025-09-16T07:22:35.227Z" }, + { url = "https://files.pythonhosted.org/packages/30/bf/32150a2e6c7b50b81c5dc9e942d41969400223a9c49d04e2ed955709894c/cython-3.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:f262b32327b6bce340cce5d45bbfe3972cb62543a4930460d8564a489f3aea12", size = 2705348, upload-time = "2025-09-16T07:22:37.922Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/f7351052cf9db771fe4f32fca47fd66e6d9b53d8613b17faf7d130a9d553/cython-3.1.4-py3-none-any.whl", hash = "sha256:d194d95e4fa029a3f6c7d46bdd16d973808c7ea4797586911fdb67cb98b1a2c6", size = 1227541, upload-time = "2025-09-16T07:20:29.595Z" }, ] [[package]] @@ -477,11 +479,11 @@ wheels = [ [[package]] name = "dnspython" -version = "2.7.0" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] [[package]] @@ -525,27 +527,27 @@ wheels = [ [[package]] name = "fonttools" -version = "4.59.2" +version = "4.60.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/a5/fba25f9fbdab96e26dedcaeeba125e5f05a09043bf888e0305326e55685b/fonttools-4.59.2.tar.gz", hash = "sha256:e72c0749b06113f50bcb80332364c6be83a9582d6e3db3fe0b280f996dc2ef22", size = 3540889, upload-time = "2025-08-27T16:40:30.97Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/d9/4eabd956fe123651a1f0efe29d9758b3837b5ae9a98934bdb571117033bb/fonttools-4.60.0.tar.gz", hash = "sha256:8f5927f049091a0ca74d35cce7f78e8f7775c83a6901a8fbe899babcc297146a", size = 3553671, upload-time = "2025-09-17T11:34:01.504Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/53/742fcd750ae0bdc74de4c0ff923111199cc2f90a4ee87aaddad505b6f477/fonttools-4.59.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:511946e8d7ea5c0d6c7a53c4cb3ee48eda9ab9797cd9bf5d95829a398400354f", size = 2774961, upload-time = "2025-08-27T16:38:47.536Z" }, - { url = "https://files.pythonhosted.org/packages/57/2a/976f5f9fa3b4dd911dc58d07358467bec20e813d933bc5d3db1a955dd456/fonttools-4.59.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5e2682cf7be766d84f462ba8828d01e00c8751a8e8e7ce12d7784ccb69a30d", size = 2344690, upload-time = "2025-08-27T16:38:49.723Z" }, - { url = "https://files.pythonhosted.org/packages/c1/8f/b7eefc274fcf370911e292e95565c8253b0b87c82a53919ab3c795a4f50e/fonttools-4.59.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5729e12a982dba3eeae650de48b06f3b9ddb51e9aee2fcaf195b7d09a96250e2", size = 5026910, upload-time = "2025-08-27T16:38:51.904Z" }, - { url = "https://files.pythonhosted.org/packages/69/95/864726eaa8f9d4e053d0c462e64d5830ec7c599cbdf1db9e40f25ca3972e/fonttools-4.59.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c52694eae5d652361d59ecdb5a2246bff7cff13b6367a12da8499e9df56d148d", size = 4971031, upload-time = "2025-08-27T16:38:53.676Z" }, - { url = "https://files.pythonhosted.org/packages/24/4c/b8c4735ebdea20696277c70c79e0de615dbe477834e5a7c2569aa1db4033/fonttools-4.59.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f1bbc23ba1312bd8959896f46f667753b90216852d2a8cfa2d07e0cb234144", size = 5006112, upload-time = "2025-08-27T16:38:55.69Z" }, - { url = "https://files.pythonhosted.org/packages/3b/23/f9ea29c292aa2fc1ea381b2e5621ac436d5e3e0a5dee24ffe5404e58eae8/fonttools-4.59.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1a1bfe5378962825dabe741720885e8b9ae9745ec7ecc4a5ec1f1ce59a6062bf", size = 5117671, upload-time = "2025-08-27T16:38:58.984Z" }, - { url = "https://files.pythonhosted.org/packages/ba/07/cfea304c555bf06e86071ff2a3916bc90f7c07ec85b23bab758d4908c33d/fonttools-4.59.2-cp311-cp311-win32.whl", hash = "sha256:e937790f3c2c18a1cbc7da101550a84319eb48023a715914477d2e7faeaba570", size = 2218157, upload-time = "2025-08-27T16:39:00.75Z" }, - { url = "https://files.pythonhosted.org/packages/d7/de/35d839aa69db737a3f9f3a45000ca24721834d40118652a5775d5eca8ebb/fonttools-4.59.2-cp311-cp311-win_amd64.whl", hash = "sha256:9836394e2f4ce5f9c0a7690ee93bd90aa1adc6b054f1a57b562c5d242c903104", size = 2265846, upload-time = "2025-08-27T16:39:02.453Z" }, - { url = "https://files.pythonhosted.org/packages/ba/3d/1f45db2df51e7bfa55492e8f23f383d372200be3a0ded4bf56a92753dd1f/fonttools-4.59.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82906d002c349cad647a7634b004825a7335f8159d0d035ae89253b4abf6f3ea", size = 2769711, upload-time = "2025-08-27T16:39:04.423Z" }, - { url = "https://files.pythonhosted.org/packages/29/df/cd236ab32a8abfd11558f296e064424258db5edefd1279ffdbcfd4fd8b76/fonttools-4.59.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a10c1bd7644dc58f8862d8ba0cf9fb7fef0af01ea184ba6ce3f50ab7dfe74d5a", size = 2340225, upload-time = "2025-08-27T16:39:06.143Z" }, - { url = "https://files.pythonhosted.org/packages/98/12/b6f9f964fe6d4b4dd4406bcbd3328821c3de1f909ffc3ffa558fe72af48c/fonttools-4.59.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:738f31f23e0339785fd67652a94bc69ea49e413dfdb14dcb8c8ff383d249464e", size = 4912766, upload-time = "2025-08-27T16:39:08.138Z" }, - { url = "https://files.pythonhosted.org/packages/73/78/82bde2f2d2c306ef3909b927363170b83df96171f74e0ccb47ad344563cd/fonttools-4.59.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ec99f9bdfee9cdb4a9172f9e8fd578cce5feb231f598909e0aecf5418da4f25", size = 4955178, upload-time = "2025-08-27T16:39:10.094Z" }, - { url = "https://files.pythonhosted.org/packages/92/77/7de766afe2d31dda8ee46d7e479f35c7d48747e558961489a2d6e3a02bd4/fonttools-4.59.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0476ea74161322e08c7a982f83558a2b81b491509984523a1a540baf8611cc31", size = 4897898, upload-time = "2025-08-27T16:39:12.087Z" }, - { url = "https://files.pythonhosted.org/packages/c5/77/ce0e0b905d62a06415fda9f2b2e109a24a5db54a59502b769e9e297d2242/fonttools-4.59.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:95922a922daa1f77cc72611747c156cfb38030ead72436a2c551d30ecef519b9", size = 5049144, upload-time = "2025-08-27T16:39:13.84Z" }, - { url = "https://files.pythonhosted.org/packages/d9/ea/870d93aefd23fff2e07cbeebdc332527868422a433c64062c09d4d5e7fe6/fonttools-4.59.2-cp312-cp312-win32.whl", hash = "sha256:39ad9612c6a622726a6a130e8ab15794558591f999673f1ee7d2f3d30f6a3e1c", size = 2206473, upload-time = "2025-08-27T16:39:15.854Z" }, - { url = "https://files.pythonhosted.org/packages/61/c4/e44bad000c4a4bb2e9ca11491d266e857df98ab6d7428441b173f0fe2517/fonttools-4.59.2-cp312-cp312-win_amd64.whl", hash = "sha256:980fd7388e461b19a881d35013fec32c713ffea1fc37aef2f77d11f332dfd7da", size = 2254706, upload-time = "2025-08-27T16:39:17.893Z" }, - { url = "https://files.pythonhosted.org/packages/65/a4/d2f7be3c86708912c02571db0b550121caab8cd88a3c0aacb9cfa15ea66e/fonttools-4.59.2-py3-none-any.whl", hash = "sha256:8bd0f759020e87bb5d323e6283914d9bf4ae35a7307dafb2cbd1e379e720ad37", size = 1132315, upload-time = "2025-08-27T16:40:28.984Z" }, + { url = "https://files.pythonhosted.org/packages/da/3d/c57731fbbf204ef1045caca28d5176430161ead73cd9feac3e9d9ef77ee6/fonttools-4.60.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a9106c202d68ff5f9b4a0094c4d7ad2eaa7e9280f06427b09643215e706eb016", size = 2830883, upload-time = "2025-09-17T11:32:10.552Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2d/b7a6ebaed464ce441c755252cc222af11edc651d17c8f26482f429cc2c0e/fonttools-4.60.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9da3a4a3f2485b156bb429b4f8faa972480fc01f553f7c8c80d05d48f17eec89", size = 2356005, upload-time = "2025-09-17T11:32:13.248Z" }, + { url = "https://files.pythonhosted.org/packages/ee/c2/ea834e921324e2051403e125c1fe0bfbdde4951a7c1784e4ae6bdbd286cc/fonttools-4.60.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f84de764c6057b2ffd4feb50ddef481d92e348f0c70f2c849b723118d352bf3", size = 5041201, upload-time = "2025-09-17T11:32:15.373Z" }, + { url = "https://files.pythonhosted.org/packages/93/3c/1c64a338e9aa410d2d0728827d5bb1301463078cb225b94589f27558b427/fonttools-4.60.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800b3fa0d5c12ddff02179d45b035a23989a6c597a71c8035c010fff3b2ef1bb", size = 4977696, upload-time = "2025-09-17T11:32:17.674Z" }, + { url = "https://files.pythonhosted.org/packages/07/cc/c8c411a0d9732bb886b870e052f20658fec9cf91118314f253950d2c1d65/fonttools-4.60.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd68f60b030277f292a582d31c374edfadc60bb33d51ec7b6cd4304531819ba", size = 5020386, upload-time = "2025-09-17T11:32:20.089Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/1d3bc07cf92e7f4fc27f06d4494bf6078dc595b2e01b959157a4fd23df12/fonttools-4.60.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:53328e3ca9e5c8660ef6de07c35f8f312c189b757535e12141be7a8ec942de6e", size = 5131575, upload-time = "2025-09-17T11:32:22.582Z" }, + { url = "https://files.pythonhosted.org/packages/5a/16/08db3917ee19e89d2eb0ee637d37cd4136c849dc421ff63f406b9165c1a1/fonttools-4.60.0-cp311-cp311-win32.whl", hash = "sha256:d493c175ddd0b88a5376e61163e3e6fde3be8b8987db9b092e0a84650709c9e7", size = 2229297, upload-time = "2025-09-17T11:32:24.834Z" }, + { url = "https://files.pythonhosted.org/packages/d2/0b/76764da82c0dfcea144861f568d9e83f4b921e84f2be617b451257bb25a7/fonttools-4.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc2770c9dc49c2d0366e9683f4d03beb46c98042d7ccc8ddbadf3459ecb051a7", size = 2277193, upload-time = "2025-09-17T11:32:27.094Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9b/706ebf84b55ab03439c1f3a94d6915123c0d96099f4238b254fdacffe03a/fonttools-4.60.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8c68928a438d60dfde90e2f09aa7f848ed201176ca6652341744ceec4215859f", size = 2831953, upload-time = "2025-09-17T11:32:29.39Z" }, + { url = "https://files.pythonhosted.org/packages/76/40/782f485be450846e4f3aecff1f10e42af414fc6e19d235c70020f64278e1/fonttools-4.60.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b7133821249097cffabf0624eafd37f5a3358d5ce814febe9db688e3673e724e", size = 2351716, upload-time = "2025-09-17T11:32:31.46Z" }, + { url = "https://files.pythonhosted.org/packages/39/77/ad8d2a6ecc19716eb488c8cf118de10f7802e14bdf61d136d7b52358d6b1/fonttools-4.60.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3638905d3d77ac8791127ce181f7cb434f37e4204d8b2e31b8f1e154320b41f", size = 4922729, upload-time = "2025-09-17T11:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/6b/48/aa543037c6e7788e1bc36b3f858ac70a59d32d0f45915263d0b330a35140/fonttools-4.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7968a26ef010ae89aabbb2f8e9dec1e2709a2541bb8620790451ee8aeb4f6fbf", size = 4967188, upload-time = "2025-09-17T11:32:35.74Z" }, + { url = "https://files.pythonhosted.org/packages/ac/58/e407d2028adc6387947eff8f2940b31f4ed40b9a83c2c7bbc8b9255126e2/fonttools-4.60.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ef01ca7847c356b0fe026b7b92304bc31dc60a4218689ee0acc66652c1a36b2", size = 4910043, upload-time = "2025-09-17T11:32:38.054Z" }, + { url = "https://files.pythonhosted.org/packages/16/ef/e78519b3c296ef757a21b792fc6a785aa2ef9a2efb098083d8ed5f6ee2ba/fonttools-4.60.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3482d7ed7867edfcf785f77c1dffc876c4b2ddac19539c075712ff2a0703cf5", size = 5061980, upload-time = "2025-09-17T11:32:40.457Z" }, + { url = "https://files.pythonhosted.org/packages/00/4c/ad72444d1e3ef704ee90af8d5abf198016a39908d322bf41235562fb01a0/fonttools-4.60.0-cp312-cp312-win32.whl", hash = "sha256:8c937c4fe8addff575a984c9519433391180bf52cf35895524a07b520f376067", size = 2217750, upload-time = "2025-09-17T11:32:42.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/55/3e8ac21963e130242f5a9ea2ebc57f5726d704bf4dcca89088b5b637b2d3/fonttools-4.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:99b06d5d6f29f32e312adaed0367112f5ff2d300ea24363d377ec917daf9e8c5", size = 2266025, upload-time = "2025-09-17T11:32:44.8Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a4/247d3e54eb5ed59e94e09866cfc4f9567e274fbf310ba390711851f63b3b/fonttools-4.60.0-py3-none-any.whl", hash = "sha256:496d26e4d14dcccdd6ada2e937e4d174d3138e3d73f5c9b6ec6eb2fd1dab4f66", size = 1142186, upload-time = "2025-09-17T11:33:59.287Z" }, ] [[package]] @@ -637,10 +639,10 @@ name = "gymnasium" version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { 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'" }, + { name = "cloudpickle" }, + { name = "farama-notifications" }, + { name = "numpy" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fd/17/c2a0e15c2cd5a8e788389b280996db927b923410de676ec5c7b2695e9261/gymnasium-1.2.0.tar.gz", hash = "sha256:344e87561012558f603880baf264ebc97f8a5c997a957b0c9f910281145534b0", size = 821142, upload-time = "2025-06-27T08:21:20.262Z" } wheels = [ @@ -737,11 +739,11 @@ wheels = [ [[package]] name = "kaitaistruct" -version = "0.10" +version = "0.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/04/dd60b9cb65d580ef6cb6eaee975ad1bdd22d46a3f51b07a1e0606710ea88/kaitaistruct-0.10.tar.gz", hash = "sha256:a044dee29173d6afbacf27bcac39daf89b654dd418cfa009ab82d9178a9ae52a", size = 7061, upload-time = "2022-07-09T00:34:06.729Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/b8/ca7319556912f68832daa4b81425314857ec08dfccd8dbc8c0f65c992108/kaitaistruct-0.11.tar.gz", hash = "sha256:053ee764288e78b8e53acf748e9733268acbd579b8d82a427b1805453625d74b", size = 11519, upload-time = "2025-09-08T15:46:25.037Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/bf/88ad23efc08708bda9a2647169828e3553bb2093a473801db61f75356395/kaitaistruct-0.10-py2.py3-none-any.whl", hash = "sha256:a97350919adbf37fda881f75e9365e2fb88d04832b7a4e57106ec70119efb235", size = 7013, upload-time = "2022-07-09T00:34:03.905Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/cf14bf3b1f5ffb13c69cf5f0ea78031247790558ee88984a8bdd22fae60d/kaitaistruct-0.11-py2.py3-none-any.whl", hash = "sha256:5c6ce79177b4e193a577ecd359e26516d1d6d000a0bffd6e1010f2a46a62a561", size = 11372, upload-time = "2025-09-08T15:46:23.635Z" }, ] [[package]] @@ -842,11 +844,11 @@ wheels = [ [[package]] name = "markdown" -version = "3.8.2" +version = "3.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071, upload-time = "2025-06-19T17:12:44.483Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/37/02347f6d6d8279247a5837082ebc26fc0d5aaeaf75aa013fcbb433c777ab/markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a", size = 364585, upload-time = "2025-09-04T20:25:22.885Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827, upload-time = "2025-06-19T17:12:42.994Z" }, + { url = "https://files.pythonhosted.org/packages/70/ae/44c4a6a4cbb496d93c6257954260fe3a6e91b7bed2240e5dad2a717f5111/markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280", size = 107441, upload-time = "2025-09-04T20:25:21.784Z" }, ] [[package]] @@ -927,22 +929,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", 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'" }, + { 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" }, ] 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" }, @@ -1128,28 +1130,28 @@ wheels = [ [[package]] name = "mypy" -version = "1.17.1" +version = "1.18.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, - { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, - { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, - { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, - { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, - { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, - { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, - { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, - { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, + { url = "https://files.pythonhosted.org/packages/88/87/cafd3ae563f88f94eec33f35ff722d043e09832ea8530ef149ec1efbaf08/mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f", size = 12731198, upload-time = "2025-09-19T00:09:44.857Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e0/1e96c3d4266a06d4b0197ace5356d67d937d8358e2ee3ffac71faa843724/mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341", size = 11817879, upload-time = "2025-09-19T00:09:47.131Z" }, + { url = "https://files.pythonhosted.org/packages/72/ef/0c9ba89eb03453e76bdac5a78b08260a848c7bfc5d6603634774d9cd9525/mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d", size = 12427292, upload-time = "2025-09-19T00:10:22.472Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/ec4a061dd599eb8179d5411d99775bec2a20542505988f40fc2fee781068/mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86", size = 13163750, upload-time = "2025-09-19T00:09:51.472Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5f/2cf2ceb3b36372d51568f2208c021870fe7834cf3186b653ac6446511839/mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37", size = 13351827, upload-time = "2025-09-19T00:09:58.311Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/2697b930179e7277529eaaec1513f8de622818696857f689e4a5432e5e27/mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8", size = 9757983, upload-time = "2025-09-19T00:10:09.071Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" }, + { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" }, + { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" }, + { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" }, + { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" }, ] [[package]] @@ -1172,39 +1174,39 @@ wheels = [ [[package]] name = "numpy" -version = "2.3.2" +version = "2.3.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/7d/3fec4199c5ffb892bed55cff901e4f39a58c81df9c44c280499e92cad264/numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48", size = 20489306, upload-time = "2025-07-24T21:32:07.553Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/26/1320083986108998bd487e2931eed2aeedf914b6e8905431487543ec911d/numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9", size = 21259016, upload-time = "2025-07-24T20:24:35.214Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2b/792b341463fa93fc7e55abbdbe87dac316c5b8cb5e94fb7a59fb6fa0cda5/numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168", size = 14451158, upload-time = "2025-07-24T20:24:58.397Z" }, - { url = "https://files.pythonhosted.org/packages/b7/13/e792d7209261afb0c9f4759ffef6135b35c77c6349a151f488f531d13595/numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b", size = 5379817, upload-time = "2025-07-24T20:25:07.746Z" }, - { url = "https://files.pythonhosted.org/packages/49/ce/055274fcba4107c022b2113a213c7287346563f48d62e8d2a5176ad93217/numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8", size = 6913606, upload-time = "2025-07-24T20:25:18.84Z" }, - { url = "https://files.pythonhosted.org/packages/17/f2/e4d72e6bc5ff01e2ab613dc198d560714971900c03674b41947e38606502/numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d", size = 14589652, upload-time = "2025-07-24T20:25:40.356Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b0/fbeee3000a51ebf7222016e2939b5c5ecf8000a19555d04a18f1e02521b8/numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3", size = 16938816, upload-time = "2025-07-24T20:26:05.721Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ec/2f6c45c3484cc159621ea8fc000ac5a86f1575f090cac78ac27193ce82cd/numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f", size = 16370512, upload-time = "2025-07-24T20:26:30.545Z" }, - { url = "https://files.pythonhosted.org/packages/b5/01/dd67cf511850bd7aefd6347aaae0956ed415abea741ae107834aae7d6d4e/numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097", size = 18884947, upload-time = "2025-07-24T20:26:58.24Z" }, - { url = "https://files.pythonhosted.org/packages/a7/17/2cf60fd3e6a61d006778735edf67a222787a8c1a7842aed43ef96d777446/numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220", size = 6599494, upload-time = "2025-07-24T20:27:09.786Z" }, - { url = "https://files.pythonhosted.org/packages/d5/03/0eade211c504bda872a594f045f98ddcc6caef2b7c63610946845e304d3f/numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170", size = 13087889, upload-time = "2025-07-24T20:27:29.558Z" }, - { url = "https://files.pythonhosted.org/packages/13/32/2c7979d39dafb2a25087e12310fc7f3b9d3c7d960df4f4bc97955ae0ce1d/numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89", size = 10459560, upload-time = "2025-07-24T20:27:46.803Z" }, - { url = "https://files.pythonhosted.org/packages/00/6d/745dd1c1c5c284d17725e5c802ca4d45cfc6803519d777f087b71c9f4069/numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b", size = 20956420, upload-time = "2025-07-24T20:28:18.002Z" }, - { url = "https://files.pythonhosted.org/packages/bc/96/e7b533ea5740641dd62b07a790af5d9d8fec36000b8e2d0472bd7574105f/numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f", size = 14184660, upload-time = "2025-07-24T20:28:39.522Z" }, - { url = "https://files.pythonhosted.org/packages/2b/53/102c6122db45a62aa20d1b18c9986f67e6b97e0d6fbc1ae13e3e4c84430c/numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0", size = 5113382, upload-time = "2025-07-24T20:28:48.544Z" }, - { url = "https://files.pythonhosted.org/packages/2b/21/376257efcbf63e624250717e82b4fae93d60178f09eb03ed766dbb48ec9c/numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b", size = 6647258, upload-time = "2025-07-24T20:28:59.104Z" }, - { url = "https://files.pythonhosted.org/packages/91/ba/f4ebf257f08affa464fe6036e13f2bf9d4642a40228781dc1235da81be9f/numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370", size = 14281409, upload-time = "2025-07-24T20:40:30.298Z" }, - { url = "https://files.pythonhosted.org/packages/59/ef/f96536f1df42c668cbacb727a8c6da7afc9c05ece6d558927fb1722693e1/numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73", size = 16641317, upload-time = "2025-07-24T20:40:56.625Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a7/af813a7b4f9a42f498dde8a4c6fcbff8100eed00182cc91dbaf095645f38/numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc", size = 16056262, upload-time = "2025-07-24T20:41:20.797Z" }, - { url = "https://files.pythonhosted.org/packages/8b/5d/41c4ef8404caaa7f05ed1cfb06afe16a25895260eacbd29b4d84dff2920b/numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be", size = 18579342, upload-time = "2025-07-24T20:41:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/9950e44c5a11636f4a3af6e825ec23003475cc9a466edb7a759ed3ea63bd/numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036", size = 6320610, upload-time = "2025-07-24T20:42:01.551Z" }, - { url = "https://files.pythonhosted.org/packages/7c/2f/244643a5ce54a94f0a9a2ab578189c061e4a87c002e037b0829dd77293b6/numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f", size = 12786292, upload-time = "2025-07-24T20:42:20.738Z" }, - { url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ea/50ebc91d28b275b23b7128ef25c3d08152bc4068f42742867e07a870a42a/numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15", size = 21130338, upload-time = "2025-07-24T20:57:54.37Z" }, - { url = "https://files.pythonhosted.org/packages/9f/57/cdd5eac00dd5f137277355c318a955c0d8fb8aa486020c22afd305f8b88f/numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec", size = 14375776, upload-time = "2025-07-24T20:58:16.303Z" }, - { url = "https://files.pythonhosted.org/packages/83/85/27280c7f34fcd305c2209c0cdca4d70775e4859a9eaa92f850087f8dea50/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712", size = 5304882, upload-time = "2025-07-24T20:58:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/48/b4/6500b24d278e15dd796f43824e69939d00981d37d9779e32499e823aa0aa/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c", size = 6818405, upload-time = "2025-07-24T20:58:37.341Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c9/142c1e03f199d202da8e980c2496213509291b6024fd2735ad28ae7065c7/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296", size = 14419651, upload-time = "2025-07-24T20:58:59.048Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/8023e87cbea31a750a6c00ff9427d65ebc5fef104a136bfa69f76266d614/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981", size = 16760166, upload-time = "2025-07-24T21:28:56.38Z" }, - { url = "https://files.pythonhosted.org/packages/78/e3/6690b3f85a05506733c7e90b577e4762517404ea78bab2ca3a5cb1aeb78d/numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619", size = 12977811, upload-time = "2025-07-24T21:29:18.234Z" }, + { url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d", size = 21259253, upload-time = "2025-09-09T15:56:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569", size = 14450980, upload-time = "2025-09-09T15:56:05.926Z" }, + { url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f", size = 5379709, upload-time = "2025-09-09T15:56:07.95Z" }, + { url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125", size = 6913923, upload-time = "2025-09-09T15:56:09.443Z" }, + { url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48", size = 14589591, upload-time = "2025-09-09T15:56:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6", size = 16938714, upload-time = "2025-09-09T15:56:14.637Z" }, + { url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa", size = 16370592, upload-time = "2025-09-09T15:56:17.285Z" }, + { url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30", size = 18884474, upload-time = "2025-09-09T15:56:20.943Z" }, + { url = "https://files.pythonhosted.org/packages/45/fa/7f43ba10c77575e8be7b0138d107e4f44ca4a1ef322cd16980ea3e8b8222/numpy-2.3.3-cp311-cp311-win32.whl", hash = "sha256:eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57", size = 6599794, upload-time = "2025-09-09T15:56:23.258Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a2/a4f78cb2241fe5664a22a10332f2be886dcdea8784c9f6a01c272da9b426/numpy-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa", size = 13088104, upload-time = "2025-09-09T15:56:25.476Z" }, + { url = "https://files.pythonhosted.org/packages/79/64/e424e975adbd38282ebcd4891661965b78783de893b381cbc4832fb9beb2/numpy-2.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7", size = 10460772, upload-time = "2025-09-09T15:56:27.679Z" }, + { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, + { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, + { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, + { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, + { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, + { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e", size = 21131019, upload-time = "2025-09-09T15:58:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150", size = 14376288, upload-time = "2025-09-09T15:58:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3", size = 5305425, upload-time = "2025-09-09T15:58:48.6Z" }, + { url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0", size = 6819053, upload-time = "2025-09-09T15:58:50.401Z" }, + { url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e", size = 14420354, upload-time = "2025-09-09T15:58:52.704Z" }, + { url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db", size = 16760413, upload-time = "2025-09-09T15:58:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844, upload-time = "2025-09-09T15:58:57.359Z" }, ] [[package]] @@ -1261,7 +1263,6 @@ dependencies = [ { name = "cffi" }, { name = "crcmod" }, { name = "cython" }, - { name = "dearpygui" }, { name = "future-fstrings" }, { name = "inputs" }, { name = "json-rpc" }, @@ -1337,6 +1338,7 @@ testing = [ { name = "ruff" }, ] tools = [ + { name = "dearpygui" }, { name = "metadrive-simulator", marker = "platform_machine != 'aarch64'" }, ] @@ -1353,7 +1355,7 @@ requires-dist = [ { name = "crcmod" }, { name = "cython" }, { name = "dbus-next", marker = "extra == 'dev'" }, - { name = "dearpygui", specifier = ">=2.1.0" }, + { name = "dearpygui", marker = "extra == 'tools'", specifier = ">=2.1.0" }, { name = "dictdiffer", marker = "extra == 'dev'" }, { name = "future-fstrings" }, { name = "hypothesis", marker = "extra == 'testing'", specifier = "==6.47.*" }, @@ -1397,7 +1399,7 @@ requires-dist = [ { name = "pywinctl", marker = "extra == 'dev'" }, { name = "pyzmq" }, { name = "qrcode" }, - { name = "raylib", marker = "extra == 'dev'" }, + { name = "raylib", marker = "extra == 'dev'", specifier = "==5.5.0.2" }, { name = "requests" }, { name = "ruff", marker = "extra == 'testing'" }, { name = "scons" }, @@ -1450,8 +1452,8 @@ name = "panda3d-gltf" version = "0.13" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "panda3d-simplepbr", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d" }, + { name = "panda3d-simplepbr" }, ] sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573, upload-time = "2021-05-21T05:46:32.738Z" } wheels = [ @@ -1463,8 +1465,8 @@ name = "panda3d-simplepbr" version = "0.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload-time = "2025-03-30T16:57:41.087Z" } wheels = [ @@ -1605,31 +1607,32 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.0" +version = "6.32.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, - { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, + { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, + { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, ] [[package]] name = "psutil" -version = "7.0.0" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/31/4723d756b59344b643542936e37a31d1d3204bcdc42a7daa8ee9eb06fb50/psutil-7.1.0.tar.gz", hash = "sha256:655708b3c069387c8b77b072fc429a57d0e214221d01c0a772df7dfedcb3bcd2", size = 497660, upload-time = "2025-09-17T20:14:52.902Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, + { url = "https://files.pythonhosted.org/packages/46/62/ce4051019ee20ce0ed74432dd73a5bb087a6704284a470bb8adff69a0932/psutil-7.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76168cef4397494250e9f4e73eb3752b146de1dd950040b29186d0cce1d5ca13", size = 245242, upload-time = "2025-09-17T20:14:56.126Z" }, + { url = "https://files.pythonhosted.org/packages/38/61/f76959fba841bf5b61123fbf4b650886dc4094c6858008b5bf73d9057216/psutil-7.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:5d007560c8c372efdff9e4579c2846d71de737e4605f611437255e81efcca2c5", size = 246682, upload-time = "2025-09-17T20:14:58.25Z" }, + { url = "https://files.pythonhosted.org/packages/88/7a/37c99d2e77ec30d63398ffa6a660450b8a62517cabe44b3e9bae97696e8d/psutil-7.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22e4454970b32472ce7deaa45d045b34d3648ce478e26a04c7e858a0a6e75ff3", size = 287994, upload-time = "2025-09-17T20:14:59.901Z" }, + { url = "https://files.pythonhosted.org/packages/9d/de/04c8c61232f7244aa0a4b9a9fbd63a89d5aeaf94b2fc9d1d16e2faa5cbb0/psutil-7.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c70e113920d51e89f212dd7be06219a9b88014e63a4cec69b684c327bc474e3", size = 291163, upload-time = "2025-09-17T20:15:01.481Z" }, + { url = "https://files.pythonhosted.org/packages/f4/58/c4f976234bf6d4737bc8c02a81192f045c307b72cf39c9e5c5a2d78927f6/psutil-7.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d4a113425c037300de3ac8b331637293da9be9713855c4fc9d2d97436d7259d", size = 293625, upload-time = "2025-09-17T20:15:04.492Z" }, + { url = "https://files.pythonhosted.org/packages/79/87/157c8e7959ec39ced1b11cc93c730c4fb7f9d408569a6c59dbd92ceb35db/psutil-7.1.0-cp37-abi3-win32.whl", hash = "sha256:09ad740870c8d219ed8daae0ad3b726d3bf9a028a198e7f3080f6a1888b99bca", size = 244812, upload-time = "2025-09-17T20:15:07.462Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e9/b44c4f697276a7a95b8e94d0e320a7bf7f3318521b23de69035540b39838/psutil-7.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:57f5e987c36d3146c0dd2528cd42151cf96cd359b9d67cfff836995cc5df9a3d", size = 247965, upload-time = "2025-09-17T20:15:09.673Z" }, + { url = "https://files.pythonhosted.org/packages/26/65/1070a6e3c036f39142c2820c4b52e9243246fcfc3f96239ac84472ba361e/psutil-7.1.0-cp37-abi3-win_arm64.whl", hash = "sha256:6937cb68133e7c97b6cc9649a570c9a18ba0efebed46d8c5dae4c07fa1b67a07", size = 244971, upload-time = "2025-09-17T20:15:12.262Z" }, ] [[package]] @@ -1662,47 +1665,47 @@ sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de7 [[package]] name = "pycapnp" -version = "2.0.0" +version = "2.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/fb/54b46b52c1fa2acd9afd81bd05810c61bb1b05c6084c9625b64bc6d41843/pycapnp-2.0.0.tar.gz", hash = "sha256:503ab9b7b16773590ee226f2460408972c6b1c2cb2d819037115b919bef682be", size = 574848, upload-time = "2024-04-12T15:35:44.019Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/96/0b1696ed89950b34506594a2c1f0c19c31d91385dfee81295113e9f22442/pycapnp-2.2.0.tar.gz", hash = "sha256:06466a74e44858b38f920784a8fe74172463a8e73e8cb8298dfe02a95b8997d6", size = 707734, upload-time = "2025-09-13T16:42:04.868Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/82/cf311b1a9800b605759a38a0c337a55a639b685427364294e98a0f9b7306/pycapnp-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:829c7eb4e5f23dbcac25466110faf72a691035cf87c5d46e5053da15790e428d", size = 1673673, upload-time = "2024-04-12T15:33:32.211Z" }, - { url = "https://files.pythonhosted.org/packages/ae/55/4c03ca95c568776a1f637db9ffdcf302fb63f46e4d2a4f13edd8cb1a5f90/pycapnp-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab60fbe3e4eaf99ec97918a0a776216c6c149b6d49261383d91c2201adb475d", size = 1513351, upload-time = "2024-04-12T15:33:35.156Z" }, - { url = "https://files.pythonhosted.org/packages/55/98/e4b2dea076f8a2575abc45cd879a91bc9aa975c69ae2ac1cab61d83c5087/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c48a0582078bb74d7326d28571db0b8e6919563365537a5a13e8f5360c12bfc", size = 4910666, upload-time = "2024-04-12T15:33:37.798Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ee/3b5a182588f89074f4002fa6247e3f963bb85bc808acd1ac8deed91f1fa7/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb5ab54aff857e3711d2c0cc934194aaffacdeb3481daa56863daef07d27941", size = 5007434, upload-time = "2024-04-12T15:33:40.362Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e9/515a2ca7fdc84d57c654280d0b71dfd782fd1773d384c0ec0d56dc6fc35b/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9600778036e6fe9dbea68f0c37678c5f4d561d2f2306b3cb741de5e1670ef2ae", size = 5188923, upload-time = "2024-04-12T15:33:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/70/60/5db346e238985a526ba7589ed24f92195dad39e7dec9d85b17a567600b6f/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7278ba0262fab8c398e77d634ae7ba026866d44b52cbfc27262be8d396ecacd1", size = 5048105, upload-time = "2024-04-12T15:33:44.294Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/02b4a87c9ff9793f26d8f3d95312d902d260c094f216d84e19528a506606/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b2458d43c82302980a96518c96df257429204d2cc02bfff0c8cb6ebb371e01", size = 5063172, upload-time = "2024-04-12T15:33:46.954Z" }, - { url = "https://files.pythonhosted.org/packages/10/a1/35a7e14d765f99cfdcdfdcebc69bdf382f27016944470daa7a03c557f681/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd7755cc3fedc2ad8cc7864a0729471ddeff10c184963fe0f3689e295130f1b2", size = 5408718, upload-time = "2024-04-12T15:33:49.587Z" }, - { url = "https://files.pythonhosted.org/packages/5c/59/8bc8a993c38808c6fd90b10becba8de4a54543e8441bd87ce44ef3b7eee4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d47baf6b3db9981625ffc5ff188e089f2ebca8e7e1afb97aa5eb7bebb7bf3650", size = 5596714, upload-time = "2024-04-12T15:33:51.518Z" }, - { url = "https://files.pythonhosted.org/packages/ea/7d/79c481ef77f29e81355e92bb250f0d2a37a76f5fe0ba9433bf6c6c88b6e4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b375be92d93fdb6f7ac127ea9390bcec0fed4e485db137b084f9e7114dde7c83", size = 5709896, upload-time = "2024-04-12T15:33:53.716Z" }, - { url = "https://files.pythonhosted.org/packages/59/8d/f2eceeea1e8cae8b8a70a4752af5b772916f455e2ed388d0887e2b57d080/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:959bfdf1cddb3e5528e2293c4a375382be9a1bf044b073bc2e7eca1eb6b3a9a2", size = 5594823, upload-time = "2024-04-12T15:33:55.573Z" }, - { url = "https://files.pythonhosted.org/packages/2c/86/f8284637b61f83232e5618dd561a66080dd98ce2272d7e3ae89335d4fd97/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d873af167cf5cc7578ce5432eefcb442f866c8f7a6c57d188baf8c5e709fa39d", size = 5572564, upload-time = "2024-04-12T15:33:59.112Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b2/7f99d28a9935d1e37ec6955922c57b2be24fe0b74fe25929643686cc11e5/pycapnp-2.0.0-cp311-cp311-win32.whl", hash = "sha256:40ca8018e0b7686d549b920f087049b92a3e6f06976d9f5a8112603fc560cac4", size = 1040268, upload-time = "2024-04-12T15:34:00.933Z" }, - { url = "https://files.pythonhosted.org/packages/1d/37/89ab98961f18cffeae20d98cfc24afcfa85024bc014ecc48b0c4ac264fe0/pycapnp-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d15cd8e46d541a899c84809095d7d7b3951f43642d1859e7a39bd91910778479", size = 1141758, upload-time = "2024-04-12T15:34:02.607Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d5/0ee84de3ce34a86c373b6cfbea17d5486c2ca942d51efa99a0069723c1e3/pycapnp-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0c111ef96676df25b8afef98f369d45f838ad4434e2898e48199eb43ef704efe", size = 1645816, upload-time = "2024-04-12T15:34:04.428Z" }, - { url = "https://files.pythonhosted.org/packages/35/1e/580572083165ba791fac5ae2d8917facb94db6e3f0500421673f55165dac/pycapnp-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d18906eb1fd1b9f206d93a9591ceedce1d52e7766b66e68f271453f104e9dca", size = 1507892, upload-time = "2024-04-12T15:34:06.933Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ed/46b3cc5d32c525b6a3acb67eb43de2cec692a62775ec1ab66dafe2b7d6ad/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5d1ed365ab1beabb8838068907a7190cc0b6f16de3499d783627e670fcc0eb2", size = 4707960, upload-time = "2024-04-12T15:34:08.771Z" }, - { url = "https://files.pythonhosted.org/packages/8e/51/0a0a4d4e44138adb84959478ea4966196c5ad32022f768b9b64d1590cb3e/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:495b39a7aa2629931bbca27ad743ce591c6c41e8f81792276be424742d9cd1c1", size = 4791780, upload-time = "2024-04-12T15:34:10.863Z" }, - { url = "https://files.pythonhosted.org/packages/28/71/2b59c6ddb253b25b3d01ee6f7b32b0297ac205c7272beeb6d13399054430/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50e814fbde072dcc3d868b5b5cbb9b7a66a70bff9ad03942f3be9baf3ca1cfc6", size = 4961068, upload-time = "2024-04-12T15:34:13.543Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b8/b64fdefa59d6d2802b5ee0a9439396c23a3e5954da6909be81f2722a234c/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:920fdda62d5fdef7a48339104dff0ceb9dcc21b138491f854457ba3a3d4d63ec", size = 4872917, upload-time = "2024-04-12T15:34:15.636Z" }, - { url = "https://files.pythonhosted.org/packages/c8/55/867595f575eb6cb3662e9a0b50a24b4be42df86f2938003e586f6c81606f/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f9142eb4714c152b09dda0b055ea9dd43fd8fd894132e7eb4fa235fb4915edd", size = 4912169, upload-time = "2024-04-12T15:34:17.758Z" }, - { url = "https://files.pythonhosted.org/packages/e4/11/0d36b45e5005ecdf8510081d16c6fb7b22b49651f64af36d138df97980cc/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c98f1d0c4d32109d03e42828ce3c65236afc895033633cbed3ca092993702e7b", size = 5201744, upload-time = "2024-04-12T15:34:20.468Z" }, - { url = "https://files.pythonhosted.org/packages/05/29/ad1357998656b7141939e55bb3aea727c7a5478026feed7f8ee8cf52c935/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4d3250c1875a309d67551843cd8bf3c5e7fccf159b7f5c118a92aee36c0e871c", size = 5351113, upload-time = "2024-04-12T15:34:23.173Z" }, - { url = "https://files.pythonhosted.org/packages/5a/b1/f4c442907948a29b6427dd7436f31d3732bb0d77f5c1dbcad749ba56dac0/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:174e6babe01f5507111c0ed226cd0b5e9325a9d2850751cfe4a57c1670f13881", size = 5472055, upload-time = "2024-04-12T15:34:25.799Z" }, - { url = "https://files.pythonhosted.org/packages/c1/06/a6eceb8b8015f518c0ccae1de5d1a6e18ed73b62b4b111aff54ce5f4f566/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:ed38ece414341285695526792e020f391f29f5064b2126d0367c8bdeef28e3e9", size = 5395743, upload-time = "2024-04-12T15:34:28.134Z" }, - { url = "https://files.pythonhosted.org/packages/e7/b0/63f2b0327853ae08158de61b4dfc7fa43ae5a5c00f1d28f769e7c30cdf55/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a20b7dc55ef83a1fa446bf12680bce25caeb8f81788b623b072c3ec820db50d", size = 5405076, upload-time = "2024-04-12T15:34:30.305Z" }, - { url = "https://files.pythonhosted.org/packages/7d/24/e025dd95f1abf34e373fbab8841ac8e5fa62afe3af4a4b0c61bd01354400/pycapnp-2.0.0-cp312-cp312-win32.whl", hash = "sha256:145eea66233fb5ac9152cd1c06b999ddb691815126f87f5cc37b9cda5d569f8a", size = 1030361, upload-time = "2024-04-12T15:34:32.25Z" }, - { url = "https://files.pythonhosted.org/packages/3f/70/a71108ee9d4db9a027b665a2c383202407207174f1956195d5be45aca705/pycapnp-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:b8b03000769b29b36a8810f458b931f0f706f42027ee6676821eff28092d7734", size = 1135121, upload-time = "2024-04-12T15:34:34.208Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8d/11dd189691d78e2ca2200bf2a7677bd6bf7594f74ccc7b10ccafafd6761b/pycapnp-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e000956e08cb75070aa35123cbeae73d045e0e6c6aa347a09d53769dc675fa9", size = 1645501, upload-time = "2025-09-13T16:39:36.828Z" }, + { url = "https://files.pythonhosted.org/packages/05/9f/321878b9aeea9eeac1977504a6daf46625a636ec99b687242528263fbbe3/pycapnp-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a45081bde2adaca6a5bf98ff949962d466f94114f6e98a0eab8f55d424786f24", size = 1509785, upload-time = "2025-09-13T16:39:38.623Z" }, + { url = "https://files.pythonhosted.org/packages/79/ec/7d6085792c75855785f8e7e97a35ea8746058124238479d4509f0a25852a/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a1b10ab77d2a61bc033e74e0c90669222ede6f6b462eb8f1f97f592e6b547500", size = 5338620, upload-time = "2025-09-13T16:39:40.05Z" }, + { url = "https://files.pythonhosted.org/packages/3a/13/4244ffd7d513fca5e5c086251649586c05734923ee16b754cd22a85d7bda/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:f325fb69a8a4181ca3591ec311f1ae854dd71a199f6f55761008fdab96544fbb", size = 5420575, upload-time = "2025-09-13T16:39:42.255Z" }, + { url = "https://files.pythonhosted.org/packages/c5/7f/b2f592836d870e1e35a2cc0d4a8ece4c877dbeb2e55b040e0c00b95a01c8/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:e61420b11178a856b962e5e4a89c1755e3b1677762e976a98aa628ad96b70776", size = 5835575, upload-time = "2025-09-13T16:39:43.85Z" }, + { url = "https://files.pythonhosted.org/packages/e4/36/76ce7c4da4141f7e75db0fd4e95d51328f1f583e3d30eb52ed7dcde91bcf/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:6b7b6a5f96ee95a8b33b85f7f87d18129bdba4e3c22cad3b6485b675607b1aeb", size = 5868584, upload-time = "2025-09-13T16:39:45.893Z" }, + { url = "https://files.pythonhosted.org/packages/4b/20/c83deb8734b2b0cc083344ede75b81f5af6466a7d95868adbaf14180c21d/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d109f8e9d3b46fb28c64b1a87cae7f26447bf07253de1e7dfd057ee38b0654a4", size = 5530789, upload-time = "2025-09-13T16:39:47.63Z" }, + { url = "https://files.pythonhosted.org/packages/4c/5d/a2600534c492196cb01f102c515bdea3271352bb7d37c6910603b248ddb0/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:93db5b840a400c6c22ceb93b0494182d087a5a40f81cf179881f0f5bbcd6e439", size = 6228635, upload-time = "2025-09-13T16:39:49.366Z" }, + { url = "https://files.pythonhosted.org/packages/27/5b/2e21278f7746a36b29e2cde28b21ffc479dd09c2cb1ed963cf0f89e8caf8/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab4fd171102c2d7222fd1e80fa190652a95a58e56dd731f6f7e81806ed5bc012", size = 6560947, upload-time = "2025-09-13T16:39:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/74/4e/87bf0adb4644d45658e10eb97e08d16992a7864ff9816061a002b121da23/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:da4ea4932569279c1901e97cfa761754a7666389dfef5ae5df90ffa20de7566d", size = 6748953, upload-time = "2025-09-13T16:39:53.859Z" }, + { url = "https://files.pythonhosted.org/packages/14/09/8e2c007a9514793fb7a2f675212e4ebc31ae7e59d131c3d561f123793d0a/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:289da6a5329ec7ff323aa8d10f06709ea086111c3c4558774370907b8f43c7ba", size = 6779013, upload-time = "2025-09-13T16:39:55.931Z" }, + { url = "https://files.pythonhosted.org/packages/33/6f/747705eacdf75797feef5429ad42756ba6afcc51a96bc3f1627c3f2b11a2/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:16b7670c6a5720277823c7299401fc1009b62dffb5777e3b121ae66997a6674b", size = 6496769, upload-time = "2025-09-13T16:39:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/5d/4a/d4c65b305a2156f4fe073e84121938780eacd9528d7c977cbc6f1659787e/pycapnp-2.2.0-cp311-cp311-win32.whl", hash = "sha256:ec226235a0677e9cfb020a847fd5ff553ef2de0d67d55e19c1ea4b313263a594", size = 1064932, upload-time = "2025-09-13T16:39:58.967Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3f/66cc7cae9d02462ab633c443ca408003328156218480ab4a4c61272fbab2/pycapnp-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:402ec2aacf6256a0969eff6b8b28ab9f5ec5ba92b9cc6b7dffc41756043e15d0", size = 1220053, upload-time = "2025-09-13T16:40:00.89Z" }, + { url = "https://files.pythonhosted.org/packages/3e/79/3d82be14faf0ec38817e5b2e838905356ef801b8d45c40dcaf685d742142/pycapnp-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a4a96ebe3f854f5e0f57b4d3571a0fe802686f7fd8cfa603973ec692fb47abce", size = 1637264, upload-time = "2025-09-13T16:40:02.557Z" }, + { url = "https://files.pythonhosted.org/packages/20/b5/a31b28de6093a3907e33a3604e0a24d27c9a24ad420261d23dea519e4559/pycapnp-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:70426b32474f29101293dd4271ca9b309ae549422ffb3ba98eb0483897af5cbe", size = 1501144, upload-time = "2025-09-13T16:40:04.376Z" }, + { url = "https://files.pythonhosted.org/packages/cc/76/e2b2702fadeb2d9492ed6ef7df9f5d57d5bac1709108a73c072b279fb7db/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fc9eff6b76a2414c377a77c927f187282bc7b83005adfc3df63b43e7cd676d39", size = 5295044, upload-time = "2025-09-13T16:40:06.283Z" }, + { url = "https://files.pythonhosted.org/packages/99/4d/16e8d8ccb4b8b63d4d2c0fd745ed88287de0323e596b091fa515e775fe4f/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:a7af220f446e84373764c701197c25c1eabcc13832a7532911c191d81a61387f", size = 5343558, upload-time = "2025-09-13T16:40:07.85Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ae/a570f5cc825b71510877ebe5b8041c437d8e0e9993d5b134efbc31daf844/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c5caf1e6fcd9a068c7fafb286a915e75fe7585c9525eeac9e8a641cbd92f1982", size = 5667031, upload-time = "2025-09-13T16:40:09.489Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/d03a871153e90846fe8ea43274f7e161294f0a443fcf6c784c78edf2ceb0/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4456362dcc6acec2c801d817ad6e62a798a08aacb5e725a25f013d8318b6ce42", size = 5804310, upload-time = "2025-09-13T16:40:11.097Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8e/fc827b28128dc3921462d563ae8981a8a9ae25bd8f49bc4fe83c0793f804/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c66ba31fac33a1707d38eb0fe53aa29fd27ce6a2a4f71f415a1229c30706a75e", size = 5522994, upload-time = "2025-09-13T16:40:12.788Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4d/4bc662dd058b9f79d5a8a691a56bf9941fbe4e340098cf7f9ae49c6c8761/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:33d0d1a286e1135e1913d4a3cb0a2c972a206b1984c3d23e87cdf11e1d523b70", size = 6145416, upload-time = "2025-09-13T16:40:14.444Z" }, + { url = "https://files.pythonhosted.org/packages/97/63/44cc5dc368ed833ecc0f20be8523842b965177f4ac16ff81a3663d2f46c3/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:63aeb83c042c84e5bd9bce9f699be30279d925ea00c3a92b3a4cb8e3f2cbe957", size = 6474037, upload-time = "2025-09-13T16:40:16.104Z" }, + { url = "https://files.pythonhosted.org/packages/95/39/52753dd2dbfc4874b0ac2321a629ccd1355d9470323632701d3b7c1ab0cc/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e9d025d04115d7341148ca654e144ddecbc9af7d215426e69fa6ec8036b1467f", size = 6604316, upload-time = "2025-09-13T16:40:17.868Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a6/3f178b23a5b9f2afe2d12614f2eea740defa3dd0797397870c6f62952e69/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:51c658e633fa41fae616fcddebf048a505e3080378853b7e3e267858c2fd20f7", size = 6710383, upload-time = "2025-09-13T16:40:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ed/934f2f2c8d678c76b5616b165a0277d49d0d40dbe8e3d62a7eb1bf9bac95/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5802b63303f66066e32b704d094e2629eff1d40add9ae7c4317753ab601456b4", size = 6445575, upload-time = "2025-09-13T16:40:21.826Z" }, + { url = "https://files.pythonhosted.org/packages/c9/42/cef707de9fbc2e70aa47a26e3c17444846bb7e19cd9ea51a6ed9dce4eced/pycapnp-2.2.0-cp312-cp312-win32.whl", hash = "sha256:e21f69ce0deac8dbd68424067560bfe31102ab6728d410bd66a34c9b7aaceba5", size = 1049436, upload-time = "2025-09-13T16:40:23.425Z" }, + { url = "https://files.pythonhosted.org/packages/17/c9/82dee36033c30c39e4860df82fc475ccc6323b075dfaa518092f1cc722f6/pycapnp-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ddf0364c26028bff6e0f90da41ac28fae96703a74279a7ba6c179eaff794ecbf", size = 1188941, upload-time = "2025-09-13T16:40:24.766Z" }, ] [[package]] name = "pycparser" -version = "2.22" +version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] [[package]] @@ -1828,9 +1831,12 @@ wheels = [ [[package]] name = "pymsgbox" -version = "1.0.9" +version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/ff/4c6f31a4f08979f12a663f2aeb6c8b765d3bd592e66eaaac445f547bb875/PyMsgBox-1.0.9.tar.gz", hash = "sha256:2194227de8bff7a3d6da541848705a155dcbb2a06ee120d9f280a1d7f51263ff", size = 18829, upload-time = "2020-10-11T01:51:43.227Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/6a/e80da7594ee598a776972d09e2813df2b06b3bc29218f440631dfa7c78a8/pymsgbox-2.0.1.tar.gz", hash = "sha256:98d055c49a511dcc10fa08c3043e7102d468f5e4b3a83c6d3c61df722c7d798d", size = 20768, upload-time = "2025-09-09T00:38:56.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/3e/08c8cac81b2b2f7502746e6b9c8e5b0ec6432cd882c605560fc409aaf087/pymsgbox-2.0.1-py3-none-any.whl", hash = "sha256:5de8ec19bca2ca7e6c09d39c817c83f17c75cee80275235f43a9931db699f73b", size = 9994, upload-time = "2025-09-09T00:38:55.672Z" }, +] [[package]] name = "pyobjc" @@ -4201,9 +4207,9 @@ name = "pyopencl" version = "2025.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { 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'" }, + { name = "numpy" }, + { name = "platformdirs" }, + { name = "pytools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510, upload-time = "2025-01-22T00:16:58.421Z" } wheels = [ @@ -4233,18 +4239,21 @@ wheels = [ [[package]] name = "pyparsing" -version = "3.2.3" +version = "3.2.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/c9/b4594e6a81371dfa9eb7a2c110ad682acf985d96115ae8b25a1d63b4bf3b/pyparsing-3.2.4.tar.gz", hash = "sha256:fff89494f45559d0f2ce46613b419f632bbb6afbdaed49696d322bcf98a58e99", size = 1098809, upload-time = "2025-09-13T05:47:19.732Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" }, + { url = "https://files.pythonhosted.org/packages/53/b8/fbab973592e23ae313042d450fc26fa24282ebffba21ba373786e1ce63b4/pyparsing-3.2.4-py3-none-any.whl", hash = "sha256:91d0fcde680d42cd031daf3a6ba20da3107e08a75de50da58360e7d94ab24d36", size = 113869, upload-time = "2025-09-13T05:47:17.863Z" }, ] [[package]] name = "pyperclip" -version = "1.9.0" +version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961, upload-time = "2024-06-18T20:38:48.401Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/99/25f4898cf420efb6f45f519de018f4faea5391114a8618b16736ef3029f1/pyperclip-1.10.0.tar.gz", hash = "sha256:180c8346b1186921c75dfd14d9048a6b5d46bfc499778811952c6dd6eb1ca6be", size = 12193, upload-time = "2025-09-18T00:54:00.384Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/bc/22540e73c5f5ae18f02924cd3954a6c9a4aa6b713c841a94c98335d333a1/pyperclip-1.10.0-py3-none-any.whl", hash = "sha256:596fbe55dc59263bff26e61d2afbe10223e2fccb5210c9c96a28d6887cfcc7ec", size = 11062, upload-time = "2025-09-18T00:53:59.252Z" }, +] [[package]] name = "pyprof2calltree" @@ -4278,7 +4287,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.1" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -4287,21 +4296,22 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] name = "pytest-asyncio" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] @@ -4318,26 +4328,26 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.14.1" +version = "3.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, ] [[package]] name = "pytest-randomly" -version = "3.16.0" +version = "4.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367, upload-time = "2024-10-25T15:45:34.274Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/1d/258a4bf1109258c00c35043f40433be5c16647387b6e7cd5582d638c116b/pytest_randomly-4.0.1.tar.gz", hash = "sha256:174e57bb12ac2c26f3578188490bd333f0e80620c3f47340158a86eca0593cd8", size = 14130, upload-time = "2025-09-12T15:23:00.085Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396, upload-time = "2024-10-25T15:45:32.78Z" }, + { url = "https://files.pythonhosted.org/packages/33/3e/a4a9227807b56869790aad3e24472a554b585974fe7e551ea350f50897ae/pytest_randomly-4.0.1-py3-none-any.whl", hash = "sha256:e0dfad2fd4f35e07beff1e47c17fbafcf98f9bf4531fd369d9260e2f858bfcb7", size = 8304, upload-time = "2025-09-12T15:22:58.946Z" }, ] [[package]] @@ -4379,7 +4389,7 @@ wheels = [ [[package]] name = "pytest-xdist" -version = "3.7.1.dev24+g2b4372b" +version = "3.7.1.dev24+g2b4372bd6" source = { git = "https://github.com/sshane/pytest-xdist?rev=2b4372bd62699fb412c4fe2f95bf9f01bd2018da#2b4372bd62699fb412c4fe2f95bf9f01bd2018da" } dependencies = [ { name = "execnet" }, @@ -4421,9 +4431,9 @@ name = "pytools" version = "2024.1.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { 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'" }, + { name = "platformdirs" }, + { name = "siphash24" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741, upload-time = "2024-07-17T18:47:38.287Z" } wheels = [ @@ -4521,38 +4531,38 @@ wheels = [ [[package]] name = "pyzmq" -version = "27.0.2" +version = "27.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/66/159f38d184f08b5f971b467f87b1ab142ab1320d5200825c824b32b84b66/pyzmq-27.0.2.tar.gz", hash = "sha256:b398dd713b18de89730447347e96a0240225e154db56e35b6bb8447ffdb07798", size = 281440, upload-time = "2025-08-21T04:23:26.334Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/73/034429ab0f4316bf433eb6c20c3f49d1dc13b2ed4e4d951b283d300a0f35/pyzmq-27.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:063845960df76599ad4fad69fa4d884b3ba38304272104fdcd7e3af33faeeb1d", size = 1333169, upload-time = "2025-08-21T04:21:12.483Z" }, - { url = "https://files.pythonhosted.org/packages/35/02/c42b3b526eb03a570c889eea85a5602797f800a50ba8b09ddbf7db568b78/pyzmq-27.0.2-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:845a35fb21b88786aeb38af8b271d41ab0967985410f35411a27eebdc578a076", size = 909176, upload-time = "2025-08-21T04:21:13.835Z" }, - { url = "https://files.pythonhosted.org/packages/1b/35/a1c0b988fabbdf2dc5fe94b7c2bcfd61e3533e5109297b8e0daf1d7a8d2d/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:515d20b5c3c86db95503faa989853a8ab692aab1e5336db011cd6d35626c4cb1", size = 668972, upload-time = "2025-08-21T04:21:15.315Z" }, - { url = "https://files.pythonhosted.org/packages/a0/63/908ac865da32ceaeecea72adceadad28ca25b23a2ca5ff018e5bff30116f/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:862aedec0b0684a5050cdb5ec13c2da96d2f8dffda48657ed35e312a4e31553b", size = 856962, upload-time = "2025-08-21T04:21:16.652Z" }, - { url = "https://files.pythonhosted.org/packages/2f/5a/90b3cc20b65cdf9391896fcfc15d8db21182eab810b7ea05a2986912fbe2/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5bcfc51c7a4fce335d3bc974fd1d6a916abbcdd2b25f6e89d37b8def25f57", size = 1657712, upload-time = "2025-08-21T04:21:18.666Z" }, - { url = "https://files.pythonhosted.org/packages/c4/3c/32a5a80f9be4759325b8d7b22ce674bb87e586b4c80c6a9d77598b60d6f0/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:38ff75b2a36e3a032e9fef29a5871e3e1301a37464e09ba364e3c3193f62982a", size = 2035054, upload-time = "2025-08-21T04:21:20.073Z" }, - { url = "https://files.pythonhosted.org/packages/13/61/71084fe2ff2d7dc5713f8740d735336e87544845dae1207a8e2e16d9af90/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a5709abe8d23ca158a9d0a18c037f4193f5b6afeb53be37173a41e9fb885792", size = 1894010, upload-time = "2025-08-21T04:21:21.96Z" }, - { url = "https://files.pythonhosted.org/packages/cb/6b/77169cfb13b696e50112ca496b2ed23c4b7d8860a1ec0ff3e4b9f9926221/pyzmq-27.0.2-cp311-cp311-win32.whl", hash = "sha256:47c5dda2018c35d87be9b83de0890cb92ac0791fd59498847fc4eca6ff56671d", size = 566819, upload-time = "2025-08-21T04:21:23.31Z" }, - { url = "https://files.pythonhosted.org/packages/37/cd/86c4083e0f811f48f11bc0ddf1e7d13ef37adfd2fd4f78f2445f1cc5dec0/pyzmq-27.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:f54ca3e98f8f4d23e989c7d0edcf9da7a514ff261edaf64d1d8653dd5feb0a8b", size = 633264, upload-time = "2025-08-21T04:21:24.761Z" }, - { url = "https://files.pythonhosted.org/packages/a0/69/5b8bb6a19a36a569fac02153a9e083738785892636270f5f68a915956aea/pyzmq-27.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:2ef3067cb5b51b090fb853f423ad7ed63836ec154374282780a62eb866bf5768", size = 559316, upload-time = "2025-08-21T04:21:26.1Z" }, - { url = "https://files.pythonhosted.org/packages/68/69/b3a729e7b03e412bee2b1823ab8d22e20a92593634f664afd04c6c9d9ac0/pyzmq-27.0.2-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:5da05e3c22c95e23bfc4afeee6ff7d4be9ff2233ad6cb171a0e8257cd46b169a", size = 1305910, upload-time = "2025-08-21T04:21:27.609Z" }, - { url = "https://files.pythonhosted.org/packages/15/b7/f6a6a285193d489b223c340b38ee03a673467cb54914da21c3d7849f1b10/pyzmq-27.0.2-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4520577971d01d47e2559bb3175fce1be9103b18621bf0b241abe0a933d040", size = 895507, upload-time = "2025-08-21T04:21:29.005Z" }, - { url = "https://files.pythonhosted.org/packages/17/e6/c4ed2da5ef9182cde1b1f5d0051a986e76339d71720ec1a00be0b49275ad/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d7de7bf73165b90bd25a8668659ccb134dd28449116bf3c7e9bab5cf8a8ec9", size = 652670, upload-time = "2025-08-21T04:21:30.71Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d781ab0636570d32c745c4e389b1c6b713115905cca69ab6233508622edd/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340e7cddc32f147c6c00d116a3f284ab07ee63dbd26c52be13b590520434533c", size = 840581, upload-time = "2025-08-21T04:21:32.008Z" }, - { url = "https://files.pythonhosted.org/packages/a6/df/f24790caf565d72544f5c8d8500960b9562c1dc848d6f22f3c7e122e73d4/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba95693f9df8bb4a9826464fb0fe89033936f35fd4a8ff1edff09a473570afa0", size = 1641931, upload-time = "2025-08-21T04:21:33.371Z" }, - { url = "https://files.pythonhosted.org/packages/65/65/77d27b19fc5e845367f9100db90b9fce924f611b14770db480615944c9c9/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:ca42a6ce2d697537da34f77a1960d21476c6a4af3e539eddb2b114c3cf65a78c", size = 2021226, upload-time = "2025-08-21T04:21:35.301Z" }, - { url = "https://files.pythonhosted.org/packages/5b/65/1ed14421ba27a4207fa694772003a311d1142b7f543179e4d1099b7eb746/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e44e665d78a07214b2772ccbd4b9bcc6d848d7895f1b2d7653f047b6318a4f6", size = 1878047, upload-time = "2025-08-21T04:21:36.749Z" }, - { url = "https://files.pythonhosted.org/packages/dd/dc/e578549b89b40dc78a387ec471c2a360766690c0a045cd8d1877d401012d/pyzmq-27.0.2-cp312-abi3-win32.whl", hash = "sha256:272d772d116615397d2be2b1417b3b8c8bc8671f93728c2f2c25002a4530e8f6", size = 558757, upload-time = "2025-08-21T04:21:38.2Z" }, - { url = "https://files.pythonhosted.org/packages/b5/89/06600980aefcc535c758414da969f37a5194ea4cdb73b745223f6af3acfb/pyzmq-27.0.2-cp312-abi3-win_amd64.whl", hash = "sha256:734be4f44efba0aa69bf5f015ed13eb69ff29bf0d17ea1e21588b095a3147b8e", size = 619281, upload-time = "2025-08-21T04:21:39.909Z" }, - { url = "https://files.pythonhosted.org/packages/30/84/df8a5c089552d17c9941d1aea4314b606edf1b1622361dae89aacedc6467/pyzmq-27.0.2-cp312-abi3-win_arm64.whl", hash = "sha256:41f0bd56d9279392810950feb2785a419c2920bbf007fdaaa7f4a07332ae492d", size = 552680, upload-time = "2025-08-21T04:21:41.571Z" }, - { url = "https://files.pythonhosted.org/packages/c7/60/027d0032a1e3b1aabcef0e309b9ff8a4099bdd5a60ab38b36a676ff2bd7b/pyzmq-27.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e297784aea724294fe95e442e39a4376c2f08aa4fae4161c669f047051e31b02", size = 836007, upload-time = "2025-08-21T04:23:00.447Z" }, - { url = "https://files.pythonhosted.org/packages/25/20/2ed1e6168aaea323df9bb2c451309291f53ba3af372ffc16edd4ce15b9e5/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3659a79ded9745bc9c2aef5b444ac8805606e7bc50d2d2eb16dc3ab5483d91f", size = 799932, upload-time = "2025-08-21T04:23:02.052Z" }, - { url = "https://files.pythonhosted.org/packages/fd/25/5c147307de546b502c9373688ce5b25dc22288d23a1ebebe5d587bf77610/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3dba49ff037d02373a9306b58d6c1e0be031438f822044e8767afccfdac4c6b", size = 567459, upload-time = "2025-08-21T04:23:03.593Z" }, - { url = "https://files.pythonhosted.org/packages/71/06/0dc56ffc615c8095cd089c9b98ce5c733e990f09ce4e8eea4aaf1041a532/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de84e1694f9507b29e7b263453a2255a73e3d099d258db0f14539bad258abe41", size = 747088, upload-time = "2025-08-21T04:23:05.334Z" }, - { url = "https://files.pythonhosted.org/packages/06/f6/4a50187e023b8848edd3f0a8e197b1a7fb08d261d8c60aae7cb6c3d71612/pyzmq-27.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f0944d65ba2b872b9fcece08411d6347f15a874c775b4c3baae7f278550da0fb", size = 544639, upload-time = "2025-08-21T04:23:07.279Z" }, + { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328, upload-time = "2025-09-08T23:07:45.946Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803, upload-time = "2025-09-08T23:07:47.551Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836, upload-time = "2025-09-08T23:07:49.436Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038, upload-time = "2025-09-08T23:07:51.234Z" }, + { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531, upload-time = "2025-09-08T23:07:52.795Z" }, + { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786, upload-time = "2025-09-08T23:07:55.047Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220, upload-time = "2025-09-08T23:07:57.172Z" }, + { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155, upload-time = "2025-09-08T23:07:59.05Z" }, + { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428, upload-time = "2025-09-08T23:08:00.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497, upload-time = "2025-09-08T23:08:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265, upload-time = "2025-09-08T23:09:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208, upload-time = "2025-09-08T23:09:51.073Z" }, + { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747, upload-time = "2025-09-08T23:09:52.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371, upload-time = "2025-09-08T23:09:54.563Z" }, + { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" }, ] [[package]] @@ -4655,28 +4665,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.11" +version = "0.13.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/55/16ab6a7d88d93001e1ae4c34cbdcfb376652d761799459ff27c1dc20f6fa/ruff-0.12.11.tar.gz", hash = "sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d", size = 5347103, upload-time = "2025-08-28T13:59:08.87Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/33/c8e89216845615d14d2d42ba2bee404e7206a8db782f33400754f3799f05/ruff-0.13.1.tar.gz", hash = "sha256:88074c3849087f153d4bb22e92243ad4c1b366d7055f98726bc19aa08dc12d51", size = 5397987, upload-time = "2025-09-18T19:52:44.33Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/a2/3b3573e474de39a7a475f3fbaf36a25600bfeb238e1a90392799163b64a0/ruff-0.12.11-py3-none-linux_armv6l.whl", hash = "sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065", size = 11979885, upload-time = "2025-08-28T13:58:26.654Z" }, - { url = "https://files.pythonhosted.org/packages/76/e4/235ad6d1785a2012d3ded2350fd9bc5c5af8c6f56820e696b0118dfe7d24/ruff-0.12.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93", size = 12742364, upload-time = "2025-08-28T13:58:30.256Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/15b72c5fe6b1e402a543aa9d8960e0a7e19dfb079f5b0b424db48b7febab/ruff-0.12.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd", size = 11920111, upload-time = "2025-08-28T13:58:33.677Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c0/f66339d7893798ad3e17fa5a1e587d6fd9806f7c1c062b63f8b09dda6702/ruff-0.12.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee", size = 12160060, upload-time = "2025-08-28T13:58:35.74Z" }, - { url = "https://files.pythonhosted.org/packages/03/69/9870368326db26f20c946205fb2d0008988aea552dbaec35fbacbb46efaa/ruff-0.12.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0", size = 11799848, upload-time = "2025-08-28T13:58:38.051Z" }, - { url = "https://files.pythonhosted.org/packages/25/8c/dd2c7f990e9b3a8a55eee09d4e675027d31727ce33cdb29eab32d025bdc9/ruff-0.12.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644", size = 13536288, upload-time = "2025-08-28T13:58:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/7a/30/d5496fa09aba59b5e01ea76775a4c8897b13055884f56f1c35a4194c2297/ruff-0.12.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211", size = 14490633, upload-time = "2025-08-28T13:58:42.285Z" }, - { url = "https://files.pythonhosted.org/packages/9b/2f/81f998180ad53445d403c386549d6946d0748e536d58fce5b5e173511183/ruff-0.12.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793", size = 13888430, upload-time = "2025-08-28T13:58:44.641Z" }, - { url = "https://files.pythonhosted.org/packages/87/71/23a0d1d5892a377478c61dbbcffe82a3476b050f38b5162171942a029ef3/ruff-0.12.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee", size = 12913133, upload-time = "2025-08-28T13:58:47.039Z" }, - { url = "https://files.pythonhosted.org/packages/80/22/3c6cef96627f89b344c933781ed38329bfb87737aa438f15da95907cbfd5/ruff-0.12.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8", size = 13169082, upload-time = "2025-08-28T13:58:49.157Z" }, - { url = "https://files.pythonhosted.org/packages/05/b5/68b3ff96160d8b49e8dd10785ff3186be18fd650d356036a3770386e6c7f/ruff-0.12.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f", size = 13139490, upload-time = "2025-08-28T13:58:51.593Z" }, - { url = "https://files.pythonhosted.org/packages/59/b9/050a3278ecd558f74f7ee016fbdf10591d50119df8d5f5da45a22c6afafc/ruff-0.12.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000", size = 11958928, upload-time = "2025-08-28T13:58:53.943Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bc/93be37347db854806904a43b0493af8d6873472dfb4b4b8cbb27786eb651/ruff-0.12.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2", size = 11764513, upload-time = "2025-08-28T13:58:55.976Z" }, - { url = "https://files.pythonhosted.org/packages/7a/a1/1471751e2015a81fd8e166cd311456c11df74c7e8769d4aabfbc7584c7ac/ruff-0.12.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39", size = 12745154, upload-time = "2025-08-28T13:58:58.16Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/2542b14890d0f4872dd81b7b2a6aed3ac1786fae1ce9b17e11e6df9e31e3/ruff-0.12.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9", size = 13227653, upload-time = "2025-08-28T13:59:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/22/16/2fbfc61047dbfd009c58a28369a693a1484ad15441723be1cd7fe69bb679/ruff-0.12.11-py3-none-win32.whl", hash = "sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3", size = 11944270, upload-time = "2025-08-28T13:59:02.347Z" }, - { url = "https://files.pythonhosted.org/packages/08/a5/34276984705bfe069cd383101c45077ee029c3fe3b28225bf67aa35f0647/ruff-0.12.11-py3-none-win_amd64.whl", hash = "sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd", size = 13046600, upload-time = "2025-08-28T13:59:04.751Z" }, - { url = "https://files.pythonhosted.org/packages/84/a8/001d4a7c2b37623a3fd7463208267fb906df40ff31db496157549cfd6e72/ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea", size = 12135290, upload-time = "2025-08-28T13:59:06.933Z" }, + { url = "https://files.pythonhosted.org/packages/f3/41/ca37e340938f45cfb8557a97a5c347e718ef34702546b174e5300dbb1f28/ruff-0.13.1-py3-none-linux_armv6l.whl", hash = "sha256:b2abff595cc3cbfa55e509d89439b5a09a6ee3c252d92020bd2de240836cf45b", size = 12304308, upload-time = "2025-09-18T19:51:56.253Z" }, + { url = "https://files.pythonhosted.org/packages/ff/84/ba378ef4129415066c3e1c80d84e539a0d52feb250685091f874804f28af/ruff-0.13.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4ee9f4249bf7f8bb3984c41bfaf6a658162cdb1b22e3103eabc7dd1dc5579334", size = 12937258, upload-time = "2025-09-18T19:52:00.184Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b6/ec5e4559ae0ad955515c176910d6d7c93edcbc0ed1a3195a41179c58431d/ruff-0.13.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c5da4af5f6418c07d75e6f3224e08147441f5d1eac2e6ce10dcce5e616a3bae", size = 12214554, upload-time = "2025-09-18T19:52:02.753Z" }, + { url = "https://files.pythonhosted.org/packages/70/d6/cb3e3b4f03b9b0c4d4d8f06126d34b3394f6b4d764912fe80a1300696ef6/ruff-0.13.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80524f84a01355a59a93cef98d804e2137639823bcee2931f5028e71134a954e", size = 12448181, upload-time = "2025-09-18T19:52:05.279Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ea/bf60cb46d7ade706a246cd3fb99e4cfe854efa3dfbe530d049c684da24ff/ruff-0.13.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff7f5ce8d7988767dd46a148192a14d0f48d1baea733f055d9064875c7d50389", size = 12104599, upload-time = "2025-09-18T19:52:07.497Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3e/05f72f4c3d3a69e65d55a13e1dd1ade76c106d8546e7e54501d31f1dc54a/ruff-0.13.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c55d84715061f8b05469cdc9a446aa6c7294cd4bd55e86a89e572dba14374f8c", size = 13791178, upload-time = "2025-09-18T19:52:10.189Z" }, + { url = "https://files.pythonhosted.org/packages/81/e7/01b1fc403dd45d6cfe600725270ecc6a8f8a48a55bc6521ad820ed3ceaf8/ruff-0.13.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ac57fed932d90fa1624c946dc67a0a3388d65a7edc7d2d8e4ca7bddaa789b3b0", size = 14814474, upload-time = "2025-09-18T19:52:12.866Z" }, + { url = "https://files.pythonhosted.org/packages/fa/92/d9e183d4ed6185a8df2ce9faa3f22e80e95b5f88d9cc3d86a6d94331da3f/ruff-0.13.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c366a71d5b4f41f86a008694f7a0d75fe409ec298685ff72dc882f882d532e36", size = 14217531, upload-time = "2025-09-18T19:52:15.245Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4a/6ddb1b11d60888be224d721e01bdd2d81faaf1720592858ab8bac3600466/ruff-0.13.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4ea9d1b5ad3e7a83ee8ebb1229c33e5fe771e833d6d3dcfca7b77d95b060d38", size = 13265267, upload-time = "2025-09-18T19:52:17.649Z" }, + { url = "https://files.pythonhosted.org/packages/81/98/3f1d18a8d9ea33ef2ad508f0417fcb182c99b23258ec5e53d15db8289809/ruff-0.13.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f70202996055b555d3d74b626406476cc692f37b13bac8828acff058c9966a", size = 13243120, upload-time = "2025-09-18T19:52:20.332Z" }, + { url = "https://files.pythonhosted.org/packages/8d/86/b6ce62ce9c12765fa6c65078d1938d2490b2b1d9273d0de384952b43c490/ruff-0.13.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f8cff7a105dad631085d9505b491db33848007d6b487c3c1979dd8d9b2963783", size = 13443084, upload-time = "2025-09-18T19:52:23.032Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6e/af7943466a41338d04503fb5a81b2fd07251bd272f546622e5b1599a7976/ruff-0.13.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9761e84255443316a258dd7dfbd9bfb59c756e52237ed42494917b2577697c6a", size = 12295105, upload-time = "2025-09-18T19:52:25.263Z" }, + { url = "https://files.pythonhosted.org/packages/3f/97/0249b9a24f0f3ebd12f007e81c87cec6d311de566885e9309fcbac5b24cc/ruff-0.13.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3d376a88c3102ef228b102211ef4a6d13df330cb0f5ca56fdac04ccec2a99700", size = 12072284, upload-time = "2025-09-18T19:52:27.478Z" }, + { url = "https://files.pythonhosted.org/packages/f6/85/0b64693b2c99d62ae65236ef74508ba39c3febd01466ef7f354885e5050c/ruff-0.13.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cbefd60082b517a82c6ec8836989775ac05f8991715d228b3c1d86ccc7df7dae", size = 12970314, upload-time = "2025-09-18T19:52:30.212Z" }, + { url = "https://files.pythonhosted.org/packages/96/fc/342e9f28179915d28b3747b7654f932ca472afbf7090fc0c4011e802f494/ruff-0.13.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd16b9a5a499fe73f3c2ef09a7885cb1d97058614d601809d37c422ed1525317", size = 13422360, upload-time = "2025-09-18T19:52:32.676Z" }, + { url = "https://files.pythonhosted.org/packages/37/54/6177a0dc10bce6f43e392a2192e6018755473283d0cf43cc7e6afc182aea/ruff-0.13.1-py3-none-win32.whl", hash = "sha256:55e9efa692d7cb18580279f1fbb525146adc401f40735edf0aaeabd93099f9a0", size = 12178448, upload-time = "2025-09-18T19:52:35.545Z" }, + { url = "https://files.pythonhosted.org/packages/64/51/c6a3a33d9938007b8bdc8ca852ecc8d810a407fb513ab08e34af12dc7c24/ruff-0.13.1-py3-none-win_amd64.whl", hash = "sha256:3a3fb595287ee556de947183489f636b9f76a72f0fa9c028bdcabf5bab2cc5e5", size = 13286458, upload-time = "2025-09-18T19:52:38.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/04/afc078a12cf68592345b1e2d6ecdff837d286bac023d7a22c54c7a698c5b/ruff-0.13.1-py3-none-win_arm64.whl", hash = "sha256:c0bae9ffd92d54e03c2bf266f466da0a65e145f298ee5b5846ed435f6a00518a", size = 12437893, upload-time = "2025-09-18T19:52:41.283Z" }, ] [[package]] @@ -4690,47 +4700,46 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.35.2" +version = "2.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/79/0ecb942f3f1ad26c40c27f81ff82392d85c01d26a45e3c72c2b37807e680/sentry_sdk-2.35.2.tar.gz", hash = "sha256:e9e8f3c795044beb59f2c8f4c6b9b0f9779e5e604099882df05eec525e782cc6", size = 343377, upload-time = "2025-09-01T11:00:58.633Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/22/60fd703b34d94d216b2387e048ac82de3e86b63bc28869fb076f8bb0204a/sentry_sdk-2.38.0.tar.gz", hash = "sha256:792d2af45e167e2f8a3347143f525b9b6bac6f058fb2014720b40b84ccbeb985", size = 348116, upload-time = "2025-09-15T15:00:37.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/91/a43308dc82a0e32d80cd0dfdcfca401ecbd0f431ab45f24e48bb97b7800d/sentry_sdk-2.35.2-py2.py3-none-any.whl", hash = "sha256:38c98e3cbb620dd3dd80a8d6e39c753d453dd41f8a9df581b0584c19a52bc926", size = 363975, upload-time = "2025-09-01T11:00:56.574Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/bde4c4bbb269b71bc09316af8eb00da91f67814d40337cc12ef9c8742541/sentry_sdk-2.38.0-py2.py3-none-any.whl", hash = "sha256:2324aea8573a3fa1576df7fb4d65c4eb8d9929c8fa5939647397a07179eef8d0", size = 370346, upload-time = "2025-09-15T15:00:35.821Z" }, ] [[package]] name = "setproctitle" -version = "1.3.6" +version = "1.3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/af/56efe21c53ac81ac87e000b15e60b3d8104224b4313b6eacac3597bd183d/setproctitle-1.3.6.tar.gz", hash = "sha256:c9f32b96c700bb384f33f7cf07954bb609d35dd82752cef57fb2ee0968409169", size = 26889, upload-time = "2025-04-29T13:35:00.184Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/48/49393a96a2eef1ab418b17475fb92b8fcfad83d099e678751b05472e69de/setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e", size = 27002, upload-time = "2025-09-05T12:51:25.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/3b/8288d0cd969a63500dd62fc2c99ce6980f9909ccef0770ab1f86c361e0bf/setproctitle-1.3.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a1d856b0f4e4a33e31cdab5f50d0a14998f3a2d726a3fd5cb7c4d45a57b28d1b", size = 17412, upload-time = "2025-04-29T13:32:58.135Z" }, - { url = "https://files.pythonhosted.org/packages/39/37/43a5a3e25ca1048dbbf4db0d88d346226f5f1acd131bb8e660f4bfe2799f/setproctitle-1.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50706b9c0eda55f7de18695bfeead5f28b58aa42fd5219b3b1692d554ecbc9ec", size = 11963, upload-time = "2025-04-29T13:32:59.17Z" }, - { url = "https://files.pythonhosted.org/packages/5b/47/f103c40e133154783c91a10ab08ac9fc410ed835aa85bcf7107cb882f505/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af188f3305f0a65c3217c30c6d4c06891e79144076a91e8b454f14256acc7279", size = 31718, upload-time = "2025-04-29T13:33:00.36Z" }, - { url = "https://files.pythonhosted.org/packages/1f/13/7325dd1c008dd6c0ebd370ddb7505977054a87e406f142318e395031a792/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce0ed8b3f64c71c140f0ec244e5fdf8ecf78ddf8d2e591d4a8b6aa1c1214235", size = 33027, upload-time = "2025-04-29T13:33:01.499Z" }, - { url = "https://files.pythonhosted.org/packages/0c/0a/6075bfea05a71379d77af98a9ac61163e8b6e5ef1ae58cd2b05871b2079c/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70100e2087fe05359f249a0b5f393127b3a1819bf34dec3a3e0d4941138650c9", size = 30223, upload-time = "2025-04-29T13:33:03.259Z" }, - { url = "https://files.pythonhosted.org/packages/cc/41/fbf57ec52f4f0776193bd94334a841f0bc9d17e745f89c7790f336420c65/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1065ed36bd03a3fd4186d6c6de5f19846650b015789f72e2dea2d77be99bdca1", size = 31204, upload-time = "2025-04-29T13:33:04.455Z" }, - { url = "https://files.pythonhosted.org/packages/97/b5/f799fb7a00de29fb0ac1dfd015528dea425b9e31a8f1068a0b3df52d317f/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4adf6a0013fe4e0844e3ba7583ec203ca518b9394c6cc0d3354df2bf31d1c034", size = 31181, upload-time = "2025-04-29T13:33:05.697Z" }, - { url = "https://files.pythonhosted.org/packages/b5/b7/81f101b612014ec61723436022c31146178813d6ca6b947f7b9c84e9daf4/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb7452849f6615871eabed6560ffedfe56bc8af31a823b6be4ce1e6ff0ab72c5", size = 30101, upload-time = "2025-04-29T13:33:07.223Z" }, - { url = "https://files.pythonhosted.org/packages/67/23/681232eed7640eab96719daa8647cc99b639e3daff5c287bd270ef179a73/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a094b7ce455ca341b59a0f6ce6be2e11411ba6e2860b9aa3dbb37468f23338f4", size = 32438, upload-time = "2025-04-29T13:33:08.538Z" }, - { url = "https://files.pythonhosted.org/packages/19/f8/4d075a7bdc3609ac71535b849775812455e4c40aedfbf0778a6f123b1774/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad1c2c2baaba62823a7f348f469a967ece0062140ca39e7a48e4bbb1f20d54c4", size = 30625, upload-time = "2025-04-29T13:33:09.707Z" }, - { url = "https://files.pythonhosted.org/packages/5f/73/a2a8259ebee166aee1ca53eead75de0e190b3ddca4f716e5c7470ebb7ef6/setproctitle-1.3.6-cp311-cp311-win32.whl", hash = "sha256:8050c01331135f77ec99d99307bfbc6519ea24d2f92964b06f3222a804a3ff1f", size = 11488, upload-time = "2025-04-29T13:33:10.953Z" }, - { url = "https://files.pythonhosted.org/packages/c9/15/52cf5e1ff0727d53704cfdde2858eaf237ce523b0b04db65faa84ff83e13/setproctitle-1.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:9b73cf0fe28009a04a35bb2522e4c5b5176cc148919431dcb73fdbdfaab15781", size = 12201, upload-time = "2025-04-29T13:33:12.389Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fb/99456fd94d4207c5f6c40746a048a33a52b4239cd7d9c8d4889e2210ec82/setproctitle-1.3.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:af44bb7a1af163806bbb679eb8432fa7b4fb6d83a5d403b541b675dcd3798638", size = 17399, upload-time = "2025-04-29T13:33:13.406Z" }, - { url = "https://files.pythonhosted.org/packages/d5/48/9699191fe6062827683c43bfa9caac33a2c89f8781dd8c7253fa3dba85fd/setproctitle-1.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cca16fd055316a48f0debfcbfb6af7cea715429fc31515ab3fcac05abd527d8", size = 11966, upload-time = "2025-04-29T13:33:14.976Z" }, - { url = "https://files.pythonhosted.org/packages/33/03/b085d192b9ecb9c7ce6ad6ef30ecf4110b7f39430b58a56245569827fcf4/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea002088d5554fd75e619742cefc78b84a212ba21632e59931b3501f0cfc8f67", size = 32017, upload-time = "2025-04-29T13:33:16.163Z" }, - { url = "https://files.pythonhosted.org/packages/ae/68/c53162e645816f97212002111420d1b2f75bf6d02632e37e961dc2cd6d8b/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb465dd5825356c1191a038a86ee1b8166e3562d6e8add95eec04ab484cfb8a2", size = 33419, upload-time = "2025-04-29T13:33:18.239Z" }, - { url = "https://files.pythonhosted.org/packages/ac/0d/119a45d15a816a6cf5ccc61b19729f82620095b27a47e0a6838216a95fae/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2c8e20487b3b73c1fa72c56f5c89430617296cd380373e7af3a538a82d4cd6d", size = 30711, upload-time = "2025-04-29T13:33:19.571Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fb/5e9b5068df9e9f31a722a775a5e8322a29a638eaaa3eac5ea7f0b35e6314/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d6252098e98129a1decb59b46920d4eca17b0395f3d71b0d327d086fefe77d", size = 31742, upload-time = "2025-04-29T13:33:21.172Z" }, - { url = "https://files.pythonhosted.org/packages/35/88/54de1e73e8fce87d587889c7eedb48fc4ee2bbe4e4ca6331690d03024f86/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf355fbf0d4275d86f9f57be705d8e5eaa7f8ddb12b24ced2ea6cbd68fdb14dc", size = 31925, upload-time = "2025-04-29T13:33:22.427Z" }, - { url = "https://files.pythonhosted.org/packages/f3/01/65948d7badd66e63e3db247b923143da142790fa293830fdecf832712c2d/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e288f8a162d663916060beb5e8165a8551312b08efee9cf68302687471a6545d", size = 30981, upload-time = "2025-04-29T13:33:23.739Z" }, - { url = "https://files.pythonhosted.org/packages/22/20/c495e61786f1d38d5dc340b9d9077fee9be3dfc7e89f515afe12e1526dbc/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b2e54f4a2dc6edf0f5ea5b1d0a608d2af3dcb5aa8c8eeab9c8841b23e1b054fe", size = 33209, upload-time = "2025-04-29T13:33:24.915Z" }, - { url = "https://files.pythonhosted.org/packages/98/3f/a457b8550fbd34d5b482fe20b8376b529e76bf1fbf9a474a6d9a641ab4ad/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b6f4abde9a2946f57e8daaf1160b2351bcf64274ef539e6675c1d945dbd75e2a", size = 31587, upload-time = "2025-04-29T13:33:26.123Z" }, - { url = "https://files.pythonhosted.org/packages/44/fe/743517340e5a635e3f1c4310baea20c16c66202f96a6f4cead222ffd6d84/setproctitle-1.3.6-cp312-cp312-win32.whl", hash = "sha256:db608db98ccc21248370d30044a60843b3f0f3d34781ceeea67067c508cd5a28", size = 11487, upload-time = "2025-04-29T13:33:27.403Z" }, - { url = "https://files.pythonhosted.org/packages/60/9a/d88f1c1f0f4efff1bd29d9233583ee341114dda7d9613941453984849674/setproctitle-1.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:082413db8a96b1f021088e8ec23f0a61fec352e649aba20881895815388b66d3", size = 12208, upload-time = "2025-04-29T13:33:28.852Z" }, + { url = "https://files.pythonhosted.org/packages/04/cd/1b7ba5cad635510720ce19d7122154df96a2387d2a74217be552887c93e5/setproctitle-1.3.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a600eeb4145fb0ee6c287cb82a2884bd4ec5bbb076921e287039dcc7b7cc6dd0", size = 18085, upload-time = "2025-09-05T12:49:22.183Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1a/b2da0a620490aae355f9d72072ac13e901a9fec809a6a24fc6493a8f3c35/setproctitle-1.3.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97a090fed480471bb175689859532709e28c085087e344bca45cf318034f70c4", size = 13097, upload-time = "2025-09-05T12:49:23.322Z" }, + { url = "https://files.pythonhosted.org/packages/18/2e/bd03ff02432a181c1787f6fc2a678f53b7dacdd5ded69c318fe1619556e8/setproctitle-1.3.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1607b963e7b53e24ec8a2cb4e0ab3ae591d7c6bf0a160feef0551da63452b37f", size = 32191, upload-time = "2025-09-05T12:49:24.567Z" }, + { url = "https://files.pythonhosted.org/packages/28/78/1e62fc0937a8549f2220445ed2175daacee9b6764c7963b16148119b016d/setproctitle-1.3.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a20fb1a3974e2dab857870cf874b325b8705605cb7e7e8bcbb915bca896f52a9", size = 33203, upload-time = "2025-09-05T12:49:25.871Z" }, + { url = "https://files.pythonhosted.org/packages/a0/3c/65edc65db3fa3df400cf13b05e9d41a3c77517b4839ce873aa6b4043184f/setproctitle-1.3.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8d961bba676e07d77665204f36cffaa260f526e7b32d07ab3df6a2c1dfb44ba", size = 34963, upload-time = "2025-09-05T12:49:27.044Z" }, + { url = "https://files.pythonhosted.org/packages/a1/32/89157e3de997973e306e44152522385f428e16f92f3cf113461489e1e2ee/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:db0fd964fbd3a9f8999b502f65bd2e20883fdb5b1fae3a424e66db9a793ed307", size = 32398, upload-time = "2025-09-05T12:49:28.909Z" }, + { url = "https://files.pythonhosted.org/packages/4a/18/77a765a339ddf046844cb4513353d8e9dcd8183da9cdba6e078713e6b0b2/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:db116850fcf7cca19492030f8d3b4b6e231278e8fe097a043957d22ce1bdf3ee", size = 33657, upload-time = "2025-09-05T12:49:30.323Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/f0b6205c64d74d2a24a58644a38ec77bdbaa6afc13747e75973bf8904932/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:316664d8b24a5c91ee244460bdaf7a74a707adaa9e14fbe0dc0a53168bb9aba1", size = 31836, upload-time = "2025-09-05T12:49:32.309Z" }, + { url = "https://files.pythonhosted.org/packages/ba/51/e1277f9ba302f1a250bbd3eedbbee747a244b3cc682eb58fb9733968f6d8/setproctitle-1.3.7-cp311-cp311-win32.whl", hash = "sha256:b74774ca471c86c09b9d5037c8451fff06bb82cd320d26ae5a01c758088c0d5d", size = 12556, upload-time = "2025-09-05T12:49:33.529Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7b/822a23f17e9003dfdee92cd72758441ca2a3680388da813a371b716fb07f/setproctitle-1.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:acb9097213a8dd3410ed9f0dc147840e45ca9797785272928d4be3f0e69e3be4", size = 13243, upload-time = "2025-09-05T12:49:34.553Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f0/2dc88e842077719d7384d86cc47403e5102810492b33680e7dadcee64cd8/setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e", size = 18049, upload-time = "2025-09-05T12:49:36.241Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b4/50940504466689cda65680c9e9a1e518e5750c10490639fa687489ac7013/setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798", size = 13079, upload-time = "2025-09-05T12:49:38.088Z" }, + { url = "https://files.pythonhosted.org/packages/d0/99/71630546b9395b095f4082be41165d1078204d1696c2d9baade3de3202d0/setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629", size = 32932, upload-time = "2025-09-05T12:49:39.271Z" }, + { url = "https://files.pythonhosted.org/packages/50/22/cee06af4ffcfb0e8aba047bd44f5262e644199ae7527ae2c1f672b86495c/setproctitle-1.3.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6915964a6dda07920a1159321dcd6d94fc7fc526f815ca08a8063aeca3c204f1", size = 33736, upload-time = "2025-09-05T12:49:40.565Z" }, + { url = "https://files.pythonhosted.org/packages/5c/00/a5949a8bb06ef5e7df214fc393bb2fb6aedf0479b17214e57750dfdd0f24/setproctitle-1.3.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cff72899861c765bd4021d1ff1c68d60edc129711a2fdba77f9cb69ef726a8b6", size = 35605, upload-time = "2025-09-05T12:49:42.362Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3a/50caca532a9343828e3bf5778c7a84d6c737a249b1796d50dd680290594d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b7cb05bd446687ff816a3aaaf831047fc4c364feff7ada94a66024f1367b448c", size = 33143, upload-time = "2025-09-05T12:49:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ca/14/b843a251296ce55e2e17c017d6b9f11ce0d3d070e9265de4ecad948b913d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3a57b9a00de8cae7e2a1f7b9f0c2ac7b69372159e16a7708aa2f38f9e5cc987a", size = 34434, upload-time = "2025-09-05T12:49:45.31Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b7/06145c238c0a6d2c4bc881f8be230bb9f36d2bf51aff7bddcb796d5eed67/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d8828b356114f6b308b04afe398ed93803d7fca4a955dd3abe84430e28d33739", size = 32795, upload-time = "2025-09-05T12:49:46.419Z" }, + { url = "https://files.pythonhosted.org/packages/ef/dc/ef76a81fac9bf27b84ed23df19c1f67391a753eed6e3c2254ebcb5133f56/setproctitle-1.3.7-cp312-cp312-win32.whl", hash = "sha256:b0304f905efc845829ac2bc791ddebb976db2885f6171f4a3de678d7ee3f7c9f", size = 12552, upload-time = "2025-09-05T12:49:47.635Z" }, + { url = "https://files.pythonhosted.org/packages/e2/5b/a9fe517912cd6e28cf43a212b80cb679ff179a91b623138a99796d7d18a0/setproctitle-1.3.7-cp312-cp312-win_amd64.whl", hash = "sha256:9888ceb4faea3116cf02a920ff00bfbc8cc899743e4b4ac914b03625bdc3c300", size = 13247, upload-time = "2025-09-05T12:49:49.16Z" }, + { url = "https://files.pythonhosted.org/packages/c3/5b/5e1c117ac84e3cefcf8d7a7f6b2461795a87e20869da065a5c087149060b/setproctitle-1.3.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b1cac6a4b0252b8811d60b6d8d0f157c0fdfed379ac89c25a914e6346cf355a1", size = 12587, upload-time = "2025-09-05T12:51:21.195Z" }, + { url = "https://files.pythonhosted.org/packages/73/02/b9eadc226195dcfa90eed37afe56b5dd6fa2f0e5220ab8b7867b8862b926/setproctitle-1.3.7-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1704c9e041f2b1dc38f5be4552e141e1432fba3dd52c72eeffd5bc2db04dc65", size = 14286, upload-time = "2025-09-05T12:51:22.61Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/1be1d2a53c2a91ec48fa2ff4a409b395f836798adf194d99de9c059419ea/setproctitle-1.3.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b08b61976ffa548bd5349ce54404bf6b2d51bd74d4f1b241ed1b0f25bce09c3a", size = 13282, upload-time = "2025-09-05T12:51:24.094Z" }, ] [[package]] @@ -4747,7 +4756,7 @@ name = "shapely" version = "2.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } wheels = [ @@ -4771,22 +4780,22 @@ wheels = [ [[package]] name = "siphash24" -version = "1.7" +version = "1.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/be/f0a0ffbb00c51c5633b41459b5ce9b017c025a9256b4403e648c18e70850/siphash24-1.7.tar.gz", hash = "sha256:6e90fee5f199ea25b4e7303646b31872a437174fe885a93dbd4cf7784eb48164", size = 19801, upload-time = "2024-10-15T13:41:51.924Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/a2/e049b6fccf7a94bd1b2f68b3059a7d6a7aea86a808cac80cb9ae71ab6254/siphash24-1.8.tar.gz", hash = "sha256:aa932f0af4a7335caef772fdaf73a433a32580405c41eb17ff24077944b0aa97", size = 19946, upload-time = "2025-09-02T20:42:04.856Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/67/4ffd23a848739966e1b314ef99f6410035bccee00be14261313787b8f506/siphash24-1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de75488e93f1cd12c8d5004efd1ebd958c0265205a9d73e8dd8b071900838841", size = 80493, upload-time = "2024-10-15T13:41:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/56/bd/ec198a8c7aef65e967ae84f633bd9950d784c9e527d738c9a3e4bccc34a5/siphash24-1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffca9908450f9f346e97a223185fcd16217d67d84c6f246f3080c4224f41a514", size = 75350, upload-time = "2024-10-15T13:41:16.262Z" }, - { url = "https://files.pythonhosted.org/packages/50/5a/77838c916bd15addfc2e51286db4c442cb12e25eb4f8d296c394c2280240/siphash24-1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8ff44ce166452993fea267ea1b2fd089d8e7f103b13d360da441f12b0df121d", size = 100567, upload-time = "2024-10-15T13:41:17.435Z" }, - { url = "https://files.pythonhosted.org/packages/f0/aa/736a0a2efae9a6f69ac1ee4d28c2274fcad2150349fac752d6c525c4e06e/siphash24-1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4062548dcb1eef13bbe0356d6f8675bfe4571ef38d7103445daa82ba167240d1", size = 105630, upload-time = "2024-10-15T13:41:18.578Z" }, - { url = "https://files.pythonhosted.org/packages/79/52/1afbd70142d3db093d49197e3abe15ca2f1a14678299327ba776944b4771/siphash24-1.7-cp311-cp311-win32.whl", hash = "sha256:7b4ea29376b688fbcc3d25707c15a9dfe7b4ebbc4322878d75bb77e199210a39", size = 67648, upload-time = "2024-10-15T13:41:19.606Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1d/bedcd04c2d1d199c9f6b3e61a6caae0e17257696c9f49594e49856b17a99/siphash24-1.7-cp311-cp311-win_amd64.whl", hash = "sha256:ec06104e6ef1e512ee30f1b8aeae2b83c0f55f12a94042f0df5a87d43a1f4c52", size = 80046, upload-time = "2024-10-15T13:41:20.654Z" }, - { url = "https://files.pythonhosted.org/packages/3e/62/93e552af9535a416f684327f870143ee42fc9e816091672467cdfd62cce6/siphash24-1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:76a64ff0cdd192e4d0a391956d9b121c56ed56e773c5ab7eb7c3e035fd16e8cb", size = 82084, upload-time = "2024-10-15T13:41:21.776Z" }, - { url = "https://files.pythonhosted.org/packages/59/3e/b0791ab53aa9ac191b71a021eab2e75baa7c27d7feb7ec148d7961d148ba/siphash24-1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ca649bc7437d614f758891deade3b187832792a853269219e77f10509f82fe", size = 76233, upload-time = "2024-10-15T13:41:22.787Z" }, - { url = "https://files.pythonhosted.org/packages/29/4c/4c1b809bf302e9b60f3ec09ba115b2a4ac1ff6755735ee8884924fcdb45e/siphash24-1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc37dd0aed23f76bd257fbd2953fd5d954b329d7463c6ff57263a2699c52dde6", size = 98188, upload-time = "2024-10-15T13:41:24.327Z" }, - { url = "https://files.pythonhosted.org/packages/96/bf/e6b49f8ff88130bd224f291ea77d30fdde4df5f6572c519aca5d8fc8a27c/siphash24-1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eea490a200891905856b6ad0f9c56d4ec787876220bcb34c49441b2566b97887", size = 102946, upload-time = "2024-10-15T13:41:25.633Z" }, - { url = "https://files.pythonhosted.org/packages/3d/75/45c831626013950fb2ea715c218c3397e5cf2328a67208bf5d8ff69aa9e6/siphash24-1.7-cp312-cp312-win32.whl", hash = "sha256:69eb8c2c112a738875bb283cd53ef5e86874bc5aed17f3020b38e9174208fb79", size = 68323, upload-time = "2024-10-15T13:41:27.349Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d3/39190c40a68defd19b99c1082dd7455543a52283803bfa111b0e45fae968/siphash24-1.7-cp312-cp312-win_amd64.whl", hash = "sha256:7459569ea4669b6feeaf7d299fc5157cc5c69ca1231dc0decb7a7da2397c782e", size = 81000, upload-time = "2024-10-15T13:41:28.364Z" }, + { url = "https://files.pythonhosted.org/packages/82/23/f53f5bd8866c6ea3abe434c9f208e76ea027210d8b75cd0e0dc849661c7a/siphash24-1.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4662ac616bce4d3c9d6003a0d398e56f8be408fc53a166b79fad08d4f34268e", size = 76930, upload-time = "2025-09-02T20:41:00.869Z" }, + { url = "https://files.pythonhosted.org/packages/0b/25/aebf246904424a06e7ffb7a40cfa9ea9e590ea0fac82e182e0f5d1f1d7ef/siphash24-1.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:53d6bed0951a99c6d2891fa6f8acfd5ca80c3e96c60bcee99f6fa01a04773b1c", size = 74315, upload-time = "2025-09-02T20:41:02.38Z" }, + { url = "https://files.pythonhosted.org/packages/59/3f/7010407c3416ef052d46550d54afb2581fb247018fc6500af8c66669eff2/siphash24-1.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d114c03648630e9e07dac2fe95442404e4607adca91640d274ece1a4fa71123e", size = 99756, upload-time = "2025-09-02T20:41:03.902Z" }, + { url = "https://files.pythonhosted.org/packages/d4/9f/09c734833e69badd7e3faed806b4372bd6564ae0946bd250d5239885914f/siphash24-1.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88c1a55ff82b127c5d3b96927a430d8859e6a98846a5b979833ac790682dd91b", size = 104044, upload-time = "2025-09-02T20:41:05.505Z" }, + { url = "https://files.pythonhosted.org/packages/24/30/56a26d9141a34433da221f732599e2b23d2d70a966c249a9f00feb9a2915/siphash24-1.8-cp311-cp311-win32.whl", hash = "sha256:9430255e6a1313470f52c07c4a4643c451a5b2853f6d4008e4dda05cafb6ce7c", size = 62196, upload-time = "2025-09-02T20:41:07.299Z" }, + { url = "https://files.pythonhosted.org/packages/47/b2/11b0ae63fd374652544e1b12f72ba2cc3fe6c93c1483bd8ff6935b0a8a4b/siphash24-1.8-cp311-cp311-win_amd64.whl", hash = "sha256:1e4b37e4ef0b4496169adce2a58b6c3f230b5852dfa5f7ad0b2d664596409e47", size = 77162, upload-time = "2025-09-02T20:41:08.878Z" }, + { url = "https://files.pythonhosted.org/packages/7f/82/ce3545ce8052ac7ca104b183415a27ec3335e5ed51978fdd7b433f3cfe5b/siphash24-1.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:df5ed437c6e6cc96196b38728e57cd30b0427df45223475a90e173f5015ef5ba", size = 78136, upload-time = "2025-09-02T20:41:10.083Z" }, + { url = "https://files.pythonhosted.org/packages/15/88/896c3b91bc9deb78c415448b1db67343917f35971a9e23a5967a9d323b8a/siphash24-1.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f4ef78abdf811325c7089a35504df339c48c0007d4af428a044431d329721e56", size = 74588, upload-time = "2025-09-02T20:41:11.251Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/8dad3f5601db485ba862e1c1f91a5d77fb563650856a6708e9acb40ee53c/siphash24-1.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:065eff55c4fefb3a29fd26afb2c072abf7f668ffd53b91d41f92a1c485fcbe5c", size = 98655, upload-time = "2025-09-02T20:41:12.45Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cc/e0c352624c1f2faad270aeb5cce6e173977ef66b9b5e918aa6f32af896bf/siphash24-1.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6fa84ebfd47677262aa0bcb0f5a70f796f5fc5704b287ee1b65a3bd4fb7a5d", size = 103217, upload-time = "2025-09-02T20:41:13.746Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f6/0b1675bea4d40affcae642d9c7337702a4138b93c544230280712403e968/siphash24-1.8-cp312-cp312-win32.whl", hash = "sha256:6582f73615552ca055e51e03cb02a28e570a641a7f500222c86c2d811b5037eb", size = 63114, upload-time = "2025-09-02T20:41:14.972Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/afefef85d72ed8b5cf1aa9283f712e3cd43c9682fabbc809dec54baa8452/siphash24-1.8-cp312-cp312-win_amd64.whl", hash = "sha256:44ea6d794a7cbe184e1e1da2df81c5ebb672ab3867935c3e87c08bb0c2fa4879", size = 76232, upload-time = "2025-09-02T20:41:16.112Z" }, ] [[package]] @@ -4833,9 +4842,9 @@ wheels = [ [[package]] name = "spidev" -version = "3.7" +version = "3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/99/dd50af8200e224ce9412ad01cdbeeb5b39b2d61acd72138f2b92c4a6d619/spidev-3.7.tar.gz", hash = "sha256:ce628a5ff489f45132679879bff5f455a66abf9751af01843850155b06ae92f0", size = 11616, upload-time = "2025-05-06T14:23:30.783Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/87/039b6eeea781598015b538691bc174cc0bf77df9d4d2d3b8bf9245c0de8c/spidev-3.8.tar.gz", hash = "sha256:2bc02fb8c6312d519ebf1f4331067427c0921d3f77b8bcaf05189a2e8b8382c0", size = 13893, upload-time = "2025-09-15T18:56:20.672Z" } [[package]] name = "sympy" @@ -4872,14 +4881,14 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.4.20250809" +version = "2.32.4.20250913" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/b0/9355adb86ec84d057fea765e4c49cce592aaf3d5117ce5609a95a7fc3dac/types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3", size = 23027, upload-time = "2025-08-09T03:17:10.664Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/6f/ec0012be842b1d888d46884ac5558fd62aeae1f0ec4f7a581433d890d4b5/types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163", size = 20644, upload-time = "2025-08-09T03:17:09.716Z" }, + { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" }, ] [[package]] @@ -4976,7 +4985,7 @@ name = "yapf" version = "0.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "platformdirs" }, ] sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload-time = "2024-11-14T00:11:41.584Z" } wheels = [ @@ -5033,42 +5042,42 @@ wheels = [ [[package]] name = "zstandard" -version = "0.24.0" +version = "0.25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/1b/c20b2ef1d987627765dcd5bf1dadb8ef6564f00a87972635099bb76b7a05/zstandard-0.24.0.tar.gz", hash = "sha256:fe3198b81c00032326342d973e526803f183f97aa9e9a98e3f897ebafe21178f", size = 905681, upload-time = "2025-08-17T18:36:36.352Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/1f/5c72806f76043c0ef9191a2b65281dacdf3b65b0828eb13bb2c987c4fb90/zstandard-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:addfc23e3bd5f4b6787b9ca95b2d09a1a67ad5a3c318daaa783ff90b2d3a366e", size = 795228, upload-time = "2025-08-17T18:21:46.978Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ba/3059bd5cd834666a789251d14417621b5c61233bd46e7d9023ea8bc1043a/zstandard-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b005bcee4be9c3984b355336283afe77b2defa76ed6b89332eced7b6fa68b68", size = 640520, upload-time = "2025-08-17T18:21:48.162Z" }, - { url = "https://files.pythonhosted.org/packages/57/07/f0e632bf783f915c1fdd0bf68614c4764cae9dd46ba32cbae4dd659592c3/zstandard-0.24.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:3f96a9130171e01dbb6c3d4d9925d604e2131a97f540e223b88ba45daf56d6fb", size = 5347682, upload-time = "2025-08-17T18:21:50.266Z" }, - { url = "https://files.pythonhosted.org/packages/a6/4c/63523169fe84773a7462cd090b0989cb7c7a7f2a8b0a5fbf00009ba7d74d/zstandard-0.24.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd0d3d16e63873253bad22b413ec679cf6586e51b5772eb10733899832efec42", size = 5057650, upload-time = "2025-08-17T18:21:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/c6/16/49013f7ef80293f5cebf4c4229535a9f4c9416bbfd238560edc579815dbe/zstandard-0.24.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b7a8c30d9bf4bd5e4dcfe26900bef0fcd9749acde45cdf0b3c89e2052fda9a13", size = 5404893, upload-time = "2025-08-17T18:21:54.54Z" }, - { url = "https://files.pythonhosted.org/packages/4d/38/78e8bcb5fc32a63b055f2b99e0be49b506f2351d0180173674f516cf8a7a/zstandard-0.24.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:52cd7d9fa0a115c9446abb79b06a47171b7d916c35c10e0c3aa6f01d57561382", size = 5452389, upload-time = "2025-08-17T18:21:56.822Z" }, - { url = "https://files.pythonhosted.org/packages/55/8a/81671f05619edbacd49bd84ce6899a09fc8299be20c09ae92f6618ccb92d/zstandard-0.24.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0f6fc2ea6e07e20df48752e7700e02e1892c61f9a6bfbacaf2c5b24d5ad504b", size = 5558888, upload-time = "2025-08-17T18:21:58.68Z" }, - { url = "https://files.pythonhosted.org/packages/49/cc/e83feb2d7d22d1f88434defbaeb6e5e91f42a4f607b5d4d2d58912b69d67/zstandard-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e46eb6702691b24ddb3e31e88b4a499e31506991db3d3724a85bd1c5fc3cfe4e", size = 5048038, upload-time = "2025-08-17T18:22:00.642Z" }, - { url = "https://files.pythonhosted.org/packages/08/c3/7a5c57ff49ef8943877f85c23368c104c2aea510abb339a2dc31ad0a27c3/zstandard-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5e3b9310fd7f0d12edc75532cd9a56da6293840c84da90070d692e0bb15f186", size = 5573833, upload-time = "2025-08-17T18:22:02.402Z" }, - { url = "https://files.pythonhosted.org/packages/f9/00/64519983cd92535ba4bdd4ac26ac52db00040a52d6c4efb8d1764abcc343/zstandard-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76cdfe7f920738ea871f035568f82bad3328cbc8d98f1f6988264096b5264efd", size = 4961072, upload-time = "2025-08-17T18:22:04.384Z" }, - { url = "https://files.pythonhosted.org/packages/72/ab/3a08a43067387d22994fc87c3113636aa34ccd2914a4d2d188ce365c5d85/zstandard-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f2fe35ec84908dddf0fbf66b35d7c2878dbe349552dd52e005c755d3493d61c", size = 5268462, upload-time = "2025-08-17T18:22:06.095Z" }, - { url = "https://files.pythonhosted.org/packages/49/cf/2abb3a1ad85aebe18c53e7eca73223f1546ddfa3bf4d2fb83fc5a064c5ca/zstandard-0.24.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:aa705beb74ab116563f4ce784fa94771f230c05d09ab5de9c397793e725bb1db", size = 5443319, upload-time = "2025-08-17T18:22:08.572Z" }, - { url = "https://files.pythonhosted.org/packages/40/42/0dd59fc2f68f1664cda11c3b26abdf987f4e57cb6b6b0f329520cd074552/zstandard-0.24.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:aadf32c389bb7f02b8ec5c243c38302b92c006da565e120dfcb7bf0378f4f848", size = 5822355, upload-time = "2025-08-17T18:22:10.537Z" }, - { url = "https://files.pythonhosted.org/packages/99/c0/ea4e640fd4f7d58d6f87a1e7aca11fb886ac24db277fbbb879336c912f63/zstandard-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e40cd0fc734aa1d4bd0e7ad102fd2a1aefa50ce9ef570005ffc2273c5442ddc3", size = 5365257, upload-time = "2025-08-17T18:22:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/27/a9/92da42a5c4e7e4003271f2e1f0efd1f37cfd565d763ad3604e9597980a1c/zstandard-0.24.0-cp311-cp311-win32.whl", hash = "sha256:cda61c46343809ecda43dc620d1333dd7433a25d0a252f2dcc7667f6331c7b61", size = 435559, upload-time = "2025-08-17T18:22:17.29Z" }, - { url = "https://files.pythonhosted.org/packages/e2/8e/2c8e5c681ae4937c007938f954a060fa7c74f36273b289cabdb5ef0e9a7e/zstandard-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b95fc06489aa9388400d1aab01a83652bc040c9c087bd732eb214909d7fb0dd", size = 505070, upload-time = "2025-08-17T18:22:14.808Z" }, - { url = "https://files.pythonhosted.org/packages/52/10/a2f27a66bec75e236b575c9f7b0d7d37004a03aa2dcde8e2decbe9ed7b4d/zstandard-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad9fd176ff6800a0cf52bcf59c71e5de4fa25bf3ba62b58800e0f84885344d34", size = 461507, upload-time = "2025-08-17T18:22:15.964Z" }, - { url = "https://files.pythonhosted.org/packages/26/e9/0bd281d9154bba7fc421a291e263911e1d69d6951aa80955b992a48289f6/zstandard-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2bda8f2790add22773ee7a4e43c90ea05598bffc94c21c40ae0a9000b0133c3", size = 795710, upload-time = "2025-08-17T18:22:19.189Z" }, - { url = "https://files.pythonhosted.org/packages/36/26/b250a2eef515caf492e2d86732e75240cdac9d92b04383722b9753590c36/zstandard-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cc76de75300f65b8eb574d855c12518dc25a075dadb41dd18f6322bda3fe15d5", size = 640336, upload-time = "2025-08-17T18:22:20.466Z" }, - { url = "https://files.pythonhosted.org/packages/79/bf/3ba6b522306d9bf097aac8547556b98a4f753dc807a170becaf30dcd6f01/zstandard-0.24.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d2b3b4bda1a025b10fe0269369475f420177f2cb06e0f9d32c95b4873c9f80b8", size = 5342533, upload-time = "2025-08-17T18:22:22.326Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ec/22bc75bf054e25accdf8e928bc68ab36b4466809729c554ff3a1c1c8bce6/zstandard-0.24.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b84c6c210684286e504022d11ec294d2b7922d66c823e87575d8b23eba7c81f", size = 5062837, upload-time = "2025-08-17T18:22:24.416Z" }, - { url = "https://files.pythonhosted.org/packages/48/cc/33edfc9d286e517fb5b51d9c3210e5bcfce578d02a675f994308ca587ae1/zstandard-0.24.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c59740682a686bf835a1a4d8d0ed1eefe31ac07f1c5a7ed5f2e72cf577692b00", size = 5393855, upload-time = "2025-08-17T18:22:26.786Z" }, - { url = "https://files.pythonhosted.org/packages/73/36/59254e9b29da6215fb3a717812bf87192d89f190f23817d88cb8868c47ac/zstandard-0.24.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6324fde5cf5120fbf6541d5ff3c86011ec056e8d0f915d8e7822926a5377193a", size = 5451058, upload-time = "2025-08-17T18:22:28.885Z" }, - { url = "https://files.pythonhosted.org/packages/9a/c7/31674cb2168b741bbbe71ce37dd397c9c671e73349d88ad3bca9e9fae25b/zstandard-0.24.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51a86bd963de3f36688553926a84e550d45d7f9745bd1947d79472eca27fcc75", size = 5546619, upload-time = "2025-08-17T18:22:31.115Z" }, - { url = "https://files.pythonhosted.org/packages/e6/01/1a9f22239f08c00c156f2266db857545ece66a6fc0303d45c298564bc20b/zstandard-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d82ac87017b734f2fb70ff93818c66f0ad2c3810f61040f077ed38d924e19980", size = 5046676, upload-time = "2025-08-17T18:22:33.077Z" }, - { url = "https://files.pythonhosted.org/packages/a7/91/6c0cf8fa143a4988a0361380ac2ef0d7cb98a374704b389fbc38b5891712/zstandard-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92ea7855d5bcfb386c34557516c73753435fb2d4a014e2c9343b5f5ba148b5d8", size = 5576381, upload-time = "2025-08-17T18:22:35.391Z" }, - { url = "https://files.pythonhosted.org/packages/e2/77/1526080e22e78871e786ccf3c84bf5cec9ed25110a9585507d3c551da3d6/zstandard-0.24.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3adb4b5414febf074800d264ddf69ecade8c658837a83a19e8ab820e924c9933", size = 4953403, upload-time = "2025-08-17T18:22:37.266Z" }, - { url = "https://files.pythonhosted.org/packages/6e/d0/a3a833930bff01eab697eb8abeafb0ab068438771fa066558d96d7dafbf9/zstandard-0.24.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6374feaf347e6b83ec13cc5dcfa70076f06d8f7ecd46cc71d58fac798ff08b76", size = 5267396, upload-time = "2025-08-17T18:22:39.757Z" }, - { url = "https://files.pythonhosted.org/packages/f3/5e/90a0db9a61cd4769c06374297ecfcbbf66654f74cec89392519deba64d76/zstandard-0.24.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:13fc548e214df08d896ee5f29e1f91ee35db14f733fef8eabea8dca6e451d1e2", size = 5433269, upload-time = "2025-08-17T18:22:42.131Z" }, - { url = "https://files.pythonhosted.org/packages/ce/58/fc6a71060dd67c26a9c5566e0d7c99248cbe5abfda6b3b65b8f1a28d59f7/zstandard-0.24.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0a416814608610abf5488889c74e43ffa0343ca6cf43957c6b6ec526212422da", size = 5814203, upload-time = "2025-08-17T18:22:44.017Z" }, - { url = "https://files.pythonhosted.org/packages/5c/6a/89573d4393e3ecbfa425d9a4e391027f58d7810dec5cdb13a26e4cdeef5c/zstandard-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0d66da2649bb0af4471699aeb7a83d6f59ae30236fb9f6b5d20fb618ef6c6777", size = 5359622, upload-time = "2025-08-17T18:22:45.802Z" }, - { url = "https://files.pythonhosted.org/packages/60/ff/2cbab815d6f02a53a9d8d8703bc727d8408a2e508143ca9af6c3cca2054b/zstandard-0.24.0-cp312-cp312-win32.whl", hash = "sha256:ff19efaa33e7f136fe95f9bbcc90ab7fb60648453b03f95d1de3ab6997de0f32", size = 435968, upload-time = "2025-08-17T18:22:49.493Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a3/8f96b8ddb7ad12344218fbd0fd2805702dafd126ae9f8a1fb91eef7b33da/zstandard-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc05f8a875eb651d1cc62e12a4a0e6afa5cd0cc231381adb830d2e9c196ea895", size = 505195, upload-time = "2025-08-17T18:22:47.193Z" }, - { url = "https://files.pythonhosted.org/packages/a3/4a/bfca20679da63bfc236634ef2e4b1b4254203098b0170e3511fee781351f/zstandard-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:b04c94718f7a8ed7cdd01b162b6caa1954b3c9d486f00ecbbd300f149d2b2606", size = 461605, upload-time = "2025-08-17T18:22:48.317Z" }, + { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, + { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, + { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, + { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, + { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, + { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, + { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, + { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, + { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, + { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, + { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, + { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, + { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, ] From efbd0b9ea0d3892701651d35a2fd953ec26f3b56 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 19 Sep 2025 16:47:07 -0700 Subject: [PATCH 033/341] cabana typo --- tools/cabana/mainwin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 8361befb53..d65fc5b760 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -122,7 +122,7 @@ void MainWindow::createActions() { auto undo_act = UndoStack::instance()->createUndoAction(this, tr("&Undo")); undo_act->setShortcuts(QKeySequence::Undo); edit_menu->addAction(undo_act); - auto redo_act = UndoStack::instance()->createRedoAction(this, tr("&Rndo")); + auto redo_act = UndoStack::instance()->createRedoAction(this, tr("&Redo")); redo_act->setShortcuts(QKeySequence::Redo); edit_menu->addAction(redo_act); edit_menu->addSeparator(); From c7a37c06d879bf62f4eac957de281a6ceb7d39af Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 19 Sep 2025 17:18:08 -0700 Subject: [PATCH 034/341] Revert "Capnp memoryview (#36163)" This reverts commit 6ed8f07cb629a6cdcb71739b2022b1281525c211. bump --- opendbc_repo | 2 +- pyproject.toml | 4 +- system/loggerd/tests/test_loggerd.py | 2 +- system/webrtc/device/video.py | 2 +- system/webrtc/webrtcd.py | 11 +- uv.lock | 773 +++++++++++++-------------- 6 files changed, 388 insertions(+), 406 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 0c8676f74c..c70bd060c6 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 0c8676f74cf386ac914faa80a6b14303693ccde9 +Subproject commit c70bd060c6a410c1083186a1e4165e43a4eda0df diff --git a/pyproject.toml b/pyproject.toml index 3d2c2a6f74..489876f78c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ # core "cffi", "scons", - "pycapnp", + "pycapnp==2.1.0", "Cython", "setuptools", "numpy >=2.0", @@ -119,7 +119,7 @@ dev = [ "tabulate", "types-requests", "types-tabulate", - "raylib==5.5.0.2", # 5.5.0.3 has regression with gui_set_style + "raylib", ] tools = [ diff --git a/system/loggerd/tests/test_loggerd.py b/system/loggerd/tests/test_loggerd.py index d63d7d84eb..c6a4b12e63 100644 --- a/system/loggerd/tests/test_loggerd.py +++ b/system/loggerd/tests/test_loggerd.py @@ -178,7 +178,7 @@ class TestLoggerd: assert logged_params['AccessToken'] == b'', f"DONT_LOG param value was logged: {repr(logged_params['AccessToken'])}" for param_key, initData_key, v in fake_params: assert getattr(initData, initData_key) == v - assert logged_params[param_key].tobytes().decode() == v + assert logged_params[param_key].decode() == v @pytest.mark.xdist_group("camera_encoder_tests") # setting xdist group ensures tests are run in same worker, prevents encoderd from crashing def test_rotation(self): diff --git a/system/webrtc/device/video.py b/system/webrtc/device/video.py index ffa1565cbf..1bca909294 100644 --- a/system/webrtc/device/video.py +++ b/system/webrtc/device/video.py @@ -30,7 +30,7 @@ class LiveStreamVideoStreamTrack(TiciVideoStreamTrack): evta = getattr(msg, msg.which()) - packet = av.Packet(evta.header.tobytes() + evta.data.tobytes()) + packet = av.Packet(evta.header + evta.data) packet.time_base = self._time_base packet.pts = self._pts diff --git a/system/webrtc/webrtcd.py b/system/webrtc/webrtcd.py index 34abe8ab4b..fb93e565ff 100755 --- a/system/webrtc/webrtcd.py +++ b/system/webrtc/webrtcd.py @@ -22,13 +22,6 @@ from openpilot.system.webrtc.schema import generate_field from cereal import messaging, log -# pycapnp 2.2.0+ is using memoryview instead of bytes for data fields -def memoryview_fallback(obj): - if isinstance(obj, memoryview): - return obj.tobytes().decode() - return obj - - class CerealOutgoingMessageProxy: def __init__(self, sm: messaging.SubMaster): self.sm = sm @@ -44,8 +37,6 @@ class CerealOutgoingMessageProxy: msg_dict = [self.to_json(msg) for msg in msg_content] elif isinstance(msg_content, bytes): msg_dict = msg_content.decode() - elif isinstance(msg_content, memoryview): - msg_dict = msg_content.tobytes().decode() else: msg_dict = msg_content @@ -60,7 +51,7 @@ class CerealOutgoingMessageProxy: msg_dict = self.to_json(self.sm[service]) mono_time, valid = self.sm.logMonoTime[service], self.sm.valid[service] outgoing_msg = {"type": service, "logMonoTime": mono_time, "valid": valid, "data": msg_dict} - encoded_msg = json.dumps(outgoing_msg, default=memoryview_fallback).encode() + encoded_msg = json.dumps(outgoing_msg).encode() for channel in self.channels: channel.send(encoded_msg) diff --git a/uv.lock b/uv.lock index 0b2bca29d4..25d2b626cf 100644 --- a/uv.lock +++ b/uv.lock @@ -152,21 +152,21 @@ wheels = [ [[package]] name = "azure-core" -version = "1.35.1" +version = "1.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/6b/2653adc0f33adba8f11b1903701e6b1c10d34ce5d8e25dfa13a422f832b0/azure_core-1.35.1.tar.gz", hash = "sha256:435d05d6df0fff2f73fb3c15493bb4721ede14203f1ff1382aa6b6b2bdd7e562", size = 345290, upload-time = "2025-09-11T22:58:04.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/89/f53968635b1b2e53e4aad2dd641488929fef4ca9dfb0b97927fa7697ddf3/azure_core-1.35.0.tar.gz", hash = "sha256:c0be528489485e9ede59b6971eb63c1eaacf83ef53001bfe3904e475e972be5c", size = 339689, upload-time = "2025-07-03T00:55:23.496Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/52/805980aa1ba18282077c484dba634ef0ede1e84eec8be9c92b2e162d0ed6/azure_core-1.35.1-py3-none-any.whl", hash = "sha256:12da0c9e08e48e198f9158b56ddbe33b421477e1dc98c2e1c8f9e254d92c468b", size = 211800, upload-time = "2025-09-11T22:58:06.281Z" }, + { url = "https://files.pythonhosted.org/packages/d4/78/bf94897361fdd650850f0f2e405b2293e2f12808239046232bdedf554301/azure_core-1.35.0-py3-none-any.whl", hash = "sha256:8db78c72868a58f3de8991eb4d22c4d368fae226dac1002998d6c50437e7dad1", size = 210708, upload-time = "2025-07-03T00:55:25.238Z" }, ] [[package]] name = "azure-identity" -version = "1.25.0" +version = "1.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, @@ -175,9 +175,9 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/9e/4c9682a286c3c89e437579bd9f64f311020e5125c1321fd3a653166b5716/azure_identity-1.25.0.tar.gz", hash = "sha256:4177df34d684cddc026e6cf684e1abb57767aa9d84e7f2129b080ec45eee7733", size = 278507, upload-time = "2025-09-12T01:30:04.418Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/44/f3ee20bacb220b6b4a2b0a6cf7e742eecb383a5ccf604dd79ec27c286b7e/azure_identity-1.24.0.tar.gz", hash = "sha256:6c3a40b2a70af831e920b89e6421e8dcd4af78a0cb38b9642d86c67643d4930c", size = 271630, upload-time = "2025-08-07T22:27:36.258Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/54/81683b6756676a22e037b209695b08008258e603f7e47c56834029c5922a/azure_identity-1.25.0-py3-none-any.whl", hash = "sha256:becaec086bbdf8d1a6aa4fb080c2772a0f824a97d50c29637ec8cc4933f1e82d", size = 190861, upload-time = "2025-09-12T01:30:06.474Z" }, + { url = "https://files.pythonhosted.org/packages/a9/74/17428cb429e8d52f6d0d69ed685f4760a545cb0156594963a9337b53b6c9/azure_identity-1.24.0-py3-none-any.whl", hash = "sha256:9e04997cde0ab02ed66422c74748548e620b7b29361c72ce622acab0267ff7c4", size = 187890, upload-time = "2025-08-07T22:27:38.033Z" }, ] [[package]] @@ -197,25 +197,25 @@ wheels = [ [[package]] name = "casadi" -version = "3.7.2" +version = "3.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/62/1e98662024915ecb09c6894c26a3f497f4afa66570af3f53db4651fc45f1/casadi-3.7.2.tar.gz", hash = "sha256:b4d7bd8acdc4180306903ae1c9eddaf41be2a3ae2fa7154c57174ae64acdc60d", size = 6053600, upload-time = "2025-09-10T10:05:49.521Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/21/1b4ea75dbddfcdbc7cd70d83147dde72899667097c0c05b288fd7015ef10/casadi-3.7.1.tar.gz", hash = "sha256:12577155cd3cd79ba162381bfed6add1541bc770ba3f1f1334e4eb159d9b39ba", size = 6065586, upload-time = "2025-07-23T21:47:56.483Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/01/d5e3058775ec8e24a01eb74d36099493b872536ef9e39f1e49624b977778/casadi-3.7.2-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:f43b0562d05a5e6e81f1885fc4ae426c382e36eebfd8d27f1baff6052178a9b0", size = 47115880, upload-time = "2025-09-10T07:52:24.399Z" }, - { url = "https://files.pythonhosted.org/packages/0e/cf/4af27e010d599a5419129d34fdde41637029a1cca2a40bef0965d6d52228/casadi-3.7.2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:70add3334b437b60a9bc0f864d094350f1a4fcbf9e8bafec870b61aed64674df", size = 42293337, upload-time = "2025-09-10T08:03:32.556Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4c/d1a50cc840103e00effcbaf8e911b6b3fb6ba2c8f4025466f524854968ed/casadi-3.7.2-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:392d3367a4b33cf223013dad8122a0e549da40b1702a5375f82f85b563e5c0cf", size = 47277175, upload-time = "2025-09-10T08:04:08.811Z" }, - { url = "https://files.pythonhosted.org/packages/be/29/6e5714d124e6ddafbccc3ed774ca603081caa1175c7f0e1c52484184dfb3/casadi-3.7.2-cp311-none-manylinux2014_i686.whl", hash = "sha256:2ce09e0ced6df33048dccd582b5cfa2c9ff5193b12858b2584078afc17761905", size = 72438460, upload-time = "2025-09-10T08:05:02.769Z" }, - { url = "https://files.pythonhosted.org/packages/23/32/ac1f3999273aa4aae48516f6f4b7b267e0cc70d8527866989798cb81312f/casadi-3.7.2-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:5086799a46d10ba884b72fd02c21be09dae52cbc189272354a5d424791b55f37", size = 75574474, upload-time = "2025-09-10T08:06:00.709Z" }, - { url = "https://files.pythonhosted.org/packages/68/78/7fd10709504c1757f70db3893870a891fcb9f1ec9f05e8ef2e3f3b9d7e2f/casadi-3.7.2-cp311-none-win_amd64.whl", hash = "sha256:72aa5727417d781ed216f16b5e93c6ddca5db27d83b0015a729e8ad570cdc465", size = 50994144, upload-time = "2025-09-10T08:06:42.384Z" }, - { url = "https://files.pythonhosted.org/packages/65/c8/689d085447b1966f42bdb8aa4fbebef49a09697dbee32ab02a865c17ac1b/casadi-3.7.2-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:309ea41a69c9230390d349b0dd899c6a19504d1904c0756bef463e47fb5c8f9a", size = 47116756, upload-time = "2025-09-10T07:53:00.931Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c0/3c4704394a6fd4dfb2123a4fd71ba64a001f340670a3eba45be7a19ac736/casadi-3.7.2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6033381234db810b2247d16c6352e679a009ec4365d04008fc768866e011ed58", size = 42293718, upload-time = "2025-09-10T08:07:16.415Z" }, - { url = "https://files.pythonhosted.org/packages/f3/24/4cf05469ddf8544da5e92f359f96d716a97e7482999f085a632bc4ef344a/casadi-3.7.2-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:732f2804d0766454bb75596339e4f2da6662ffb669621da0f630ed4af9e83d6a", size = 47276175, upload-time = "2025-09-10T08:08:09.29Z" }, - { url = "https://files.pythonhosted.org/packages/82/08/b5f57fea03128efd5c860673b6ac44776352e6c1af862b8177f4c503fffe/casadi-3.7.2-cp312-none-manylinux2014_i686.whl", hash = "sha256:cf17298ff0c162735bdf9bf72b765c636ae732130604017a3b52e26e35402857", size = 72430454, upload-time = "2025-09-10T08:09:10.781Z" }, - { url = "https://files.pythonhosted.org/packages/24/ab/d7233c915b12c005655437c6c4cf0ae46cbbb2b20d743cb5e4881ad3104a/casadi-3.7.2-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:cde616930fa1440ad66f1850670399423edd37354eed9b12e74b3817b98d1187", size = 75568903, upload-time = "2025-09-10T08:10:07.108Z" }, - { url = "https://files.pythonhosted.org/packages/3e/b9/5b984124f539656efdf079f3d8f09d73667808ec8d0546e6bce6dc60ade6/casadi-3.7.2-cp312-none-win_amd64.whl", hash = "sha256:81d677d2b020c1307c1eb25eae15686e5de199bb066828c3eaabdfaaaf457ffd", size = 50991347, upload-time = "2025-09-10T08:10:46.629Z" }, + { url = "https://files.pythonhosted.org/packages/e6/4d/cfe092b2deb65dbf913d38148e30dfe41bbe9f289f892ea7a5d3526532d2/casadi-3.7.1-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:c13fafdff0cdf73392fe2002245befe7496b5ac99b70adb64babff0099068c8a", size = 47100775, upload-time = "2025-07-23T19:56:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/13/41/c4ad627e447a083fbfd2826f1f4684223f0072fdc49f58157ca6a6ce0a77/casadi-3.7.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:74d9c6bad3c7cb347494d52cc3e6faf3fbf3300046fab0e1ddc9439d3fd68486", size = 42282155, upload-time = "2025-07-23T19:59:11.191Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e2/53733933d454657e319cc8074e004ec46660c4797cbffcaf7695a874b0da/casadi-3.7.1-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:6fbc0803e6c7ffb9ba9a41ba38e7c1dadee73835edbacacce3530343f078f3f9", size = 47272638, upload-time = "2025-07-23T20:03:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/e1/72/8002d4fe64325842eb6a7f7ac1e4574c02584e85e5b9d93a08a789095505/casadi-3.7.1-cp311-none-manylinux2014_i686.whl", hash = "sha256:38a16b0c7caff5297df91c7e3a65091824979367e6fc8e6eb0e5ab9f1f832af8", size = 72418203, upload-time = "2025-07-23T20:07:18.681Z" }, + { url = "https://files.pythonhosted.org/packages/96/e5/065287b78c4f6a00a010d7c3f5316df1b95f7c5bda644f2151b54a79a964/casadi-3.7.1-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:2dd75cc1b5ca8009586186e4a63e0cdd83a672bf308edc75ef84f9c896cd971d", size = 75558461, upload-time = "2025-07-23T21:33:20.771Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4c/084d2f3bca33d0b3d509be17c3b678f6bcc03f3409d42e88750cb1e44966/casadi-3.7.1-cp311-none-win_amd64.whl", hash = "sha256:3ab68c6dd621b5f150635bf06aa8e1697cae2fbe0137e9d47270c54d305c334c", size = 50987255, upload-time = "2025-07-23T21:34:20.349Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a3/65942d3e0f7589a72e69dd9dee575963ecf51504e985f132bce8b0d24988/casadi-3.7.1-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:4a29ae3212b0ae6931d439ca3bfa2ac279826e99c2c9a443704e84021933ab76", size = 47102059, upload-time = "2025-07-23T20:36:47.174Z" }, + { url = "https://files.pythonhosted.org/packages/2e/f4/dc371199583f6b154ba0966c4132418737a2d2e019c9f6ad227ebd279684/casadi-3.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:70edf1df3fc83401eb5a2f7053ae8394f4e612e61c2ad6de088e3d76d0ec845e", size = 42282702, upload-time = "2025-07-23T20:39:41.499Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ff/5f8a9378cd7b322d1dcc8b8aa9fbc454ad57152b754c94b50ffdc67254a0/casadi-3.7.1-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:a1717bbd07a6eb98acddf5ea5da7e78a9b85b75dbabb55190cc6b0f2812ef39d", size = 47271636, upload-time = "2025-07-23T20:42:59.961Z" }, + { url = "https://files.pythonhosted.org/packages/e1/11/e1b6a76bf288fc86dc8919cbcd2d3a654e7ead4240d9928deb8ed4ee91da/casadi-3.7.1-cp312-none-manylinux2014_i686.whl", hash = "sha256:57ae29d16af0716ebc15d161bcf0bcd97d35631b10c0e80838290d249cac80d7", size = 72410197, upload-time = "2025-07-23T21:35:35.509Z" }, + { url = "https://files.pythonhosted.org/packages/c1/76/bb43ff625066b178132172d41db0c5c962b4af9db15c88b8a23e65376d18/casadi-3.7.1-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:756f3d07d3414dc54f05ebec883ac3db6e307bfe661719d188340504b6bed420", size = 75552890, upload-time = "2025-07-23T21:37:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/11/0c/8026b0b238e00a2e63c7142505cbcd0931b4db4140d55c83962f9d8e0b02/casadi-3.7.1-cp312-none-win_amd64.whl", hash = "sha256:b228fbcd1f41eb8d5405dbc1e0ecb780daf89808392706bb77fd2b240f8a437e", size = 50984453, upload-time = "2025-07-23T21:38:18.21Z" }, ] [[package]] @@ -229,38 +229,36 @@ wheels = [ [[package]] name = "cffi" -version = "2.0.0" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, + { name = "pycparser" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, ] [[package]] @@ -296,14 +294,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.0" +version = "8.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, ] [[package]] @@ -417,31 +415,31 @@ wheels = [ [[package]] name = "cython" -version = "3.1.4" +version = "3.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/f6/d762df1f436a0618455d37f4e4c4872a7cd0dcfc8dec3022ee99e4389c69/cython-3.1.4.tar.gz", hash = "sha256:9aefefe831331e2d66ab31799814eae4d0f8a2d246cbaaaa14d1be29ef777683", size = 3190778, upload-time = "2025-09-16T07:20:33.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/ab/915337fb39ab4f4539a313df38fc69938df3bf14141b90d61dfd5c2919de/cython-3.1.3.tar.gz", hash = "sha256:10ee785e42328924b78f75a74f66a813cb956b4a9bc91c44816d089d5934c089", size = 3186689, upload-time = "2025-08-13T06:19:13.619Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/ab/0a568bac7c4c052db4ae27edf01e16f3093cdfef04a2dfd313ef1b3c478a/cython-3.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d1d7013dba5fb0506794d4ef8947ff5ed021370614950a8d8d04e57c8c84499e", size = 3026389, upload-time = "2025-09-16T07:22:02.212Z" }, - { url = "https://files.pythonhosted.org/packages/cb/b7/51f5566e1309215a7fef744975b2fabb56d3fdc5fa1922fd7e306c14f523/cython-3.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eed989f5c139d6550ef2665b783d86fab99372590c97f10a3c26c4523c5fce9e", size = 2955954, upload-time = "2025-09-16T07:22:03.782Z" }, - { url = "https://files.pythonhosted.org/packages/28/fd/ad8314520000fe96292fb8208c640fa862baa3053d2f3453a2acb50cafb8/cython-3.1.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3df3beb8b024dfd73cfddb7f2f7456751cebf6e31655eed3189c209b634bc2f2", size = 3412005, upload-time = "2025-09-16T07:22:05.483Z" }, - { url = "https://files.pythonhosted.org/packages/0c/3b/e570f8bcb392e7943fc9a25d1b2d1646ef0148ff017d3681511acf6bbfdc/cython-3.1.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8354703f1168e1aaa01348940f719734c1f11298be333bdb5b94101d49677c0", size = 3191100, upload-time = "2025-09-16T07:22:07.144Z" }, - { url = "https://files.pythonhosted.org/packages/78/81/f1ea09f563ebab732542cb11bf363710e53f3842458159ea2c160788bc8e/cython-3.1.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a928bd7d446247855f54f359057ab4a32c465219c8c1e299906a483393a59a9e", size = 3313786, upload-time = "2025-09-16T07:22:09.15Z" }, - { url = "https://files.pythonhosted.org/packages/ca/17/06575eb6175a926523bada7dac1cd05cc74add96cebbf2e8b492a2494291/cython-3.1.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c233bfff4cc7b9d629eecb7345f9b733437f76dc4441951ec393b0a6e29919fc", size = 3205775, upload-time = "2025-09-16T07:22:10.745Z" }, - { url = "https://files.pythonhosted.org/packages/10/ba/61a8cf56a76ab21ddf6476b70884feff2a2e56b6d9010e1e1b1e06c46f70/cython-3.1.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e9691a2cbc2faf0cd819108bceccf9bfc56c15a06d172eafe74157388c44a601", size = 3428423, upload-time = "2025-09-16T07:22:12.404Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c2/42cf9239088d6b4b62c1c017c36e0e839f64c8d68674ce4172d0e0168d3b/cython-3.1.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ada319207432ea7c6691c70b5c112d261637d79d21ba086ae3726fedde79bfbf", size = 3330489, upload-time = "2025-09-16T07:22:14.576Z" }, - { url = "https://files.pythonhosted.org/packages/b5/08/36a619d6b1fc671a11744998e5cdd31790589e3cb4542927c97f3f351043/cython-3.1.4-cp311-cp311-win32.whl", hash = "sha256:dae81313c28222bf7be695f85ae1d16625aac35a0973a3af1e001f63379440c5", size = 2482410, upload-time = "2025-09-16T07:22:17.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/58/7d9ae7944bcd32e6f02d1a8d5d0c3875125227d050e235584127f2c64ffd/cython-3.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:60d2f192059ac34c5c26527f2beac823d34aaa766ef06792a3b7f290c18ac5e2", size = 2713755, upload-time = "2025-09-16T07:22:18.949Z" }, - { url = "https://files.pythonhosted.org/packages/f0/51/2939c739cfdc67ab94935a2c4fcc75638afd15e1954552655503a4112e92/cython-3.1.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0d26af46505d0e54fe0f05e7ad089fd0eed8fa04f385f3ab88796f554467bcb9", size = 3062976, upload-time = "2025-09-16T07:22:20.517Z" }, - { url = "https://files.pythonhosted.org/packages/eb/bd/a84de57fd01017bf5dba84a49aeee826db21112282bf8d76ab97567ee15d/cython-3.1.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66ac8bb5068156c92359e3f0eefa138c177d59d1a2e8a89467881fa7d06aba3b", size = 2970701, upload-time = "2025-09-16T07:22:22.644Z" }, - { url = "https://files.pythonhosted.org/packages/71/79/a09004c8e42f5be188c7636b1be479cdb244a6d8837e1878d062e4e20139/cython-3.1.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2e42714faec723d2305607a04bafb49a48a8d8f25dd39368d884c058dbcfbc", size = 3387730, upload-time = "2025-09-16T07:22:24.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/bd/979f8c59e247f562642f3eb98a1b453530e1f7954ef071835c08ed2bf6ba/cython-3.1.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0fd655b27997a209a574873304ded9629de588f021154009e8f923475e2c677", size = 3167289, upload-time = "2025-09-16T07:22:26.35Z" }, - { url = "https://files.pythonhosted.org/packages/34/f8/0b98537f0b4e8c01f76d2a6cf75389987538e4d4ac9faf25836fd18c9689/cython-3.1.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9def7c41f4dc339003b1e6875f84edf059989b9c7f5e9a245d3ce12c190742d9", size = 3321099, upload-time = "2025-09-16T07:22:27.957Z" }, - { url = "https://files.pythonhosted.org/packages/f3/39/437968a2e7c7f57eb6e1144f6aca968aa15fbbf169b2d4da5d1ff6c21442/cython-3.1.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:196555584a8716bf7e017e23ca53e9f632ed493f9faa327d0718e7551588f55d", size = 3179897, upload-time = "2025-09-16T07:22:30.014Z" }, - { url = "https://files.pythonhosted.org/packages/2c/04/b3f42915f034d133f1a34e74a2270bc2def02786f9b40dc9028fbb968814/cython-3.1.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7fff0e739e07a20726484b8898b8628a7b87acb960d0fc5486013c6b77b7bb97", size = 3400936, upload-time = "2025-09-16T07:22:31.705Z" }, - { url = "https://files.pythonhosted.org/packages/21/eb/2ad9fa0896ab6cf29875a09a9f4aaea37c28b79b869a013bf9b58e4e652e/cython-3.1.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2754034fa10f95052949cd6b07eb2f61d654c1b9cfa0b17ea53a269389422e8", size = 3332131, upload-time = "2025-09-16T07:22:33.32Z" }, - { url = "https://files.pythonhosted.org/packages/3c/bf/f19283f8405e7e564c3353302a8665ea2c589be63a8e1be1b503043366a9/cython-3.1.4-cp312-cp312-win32.whl", hash = "sha256:2e0808ff3614a1dbfd1adfcbff9b2b8119292f1824b3535b4a173205109509f8", size = 2487672, upload-time = "2025-09-16T07:22:35.227Z" }, - { url = "https://files.pythonhosted.org/packages/30/bf/32150a2e6c7b50b81c5dc9e942d41969400223a9c49d04e2ed955709894c/cython-3.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:f262b32327b6bce340cce5d45bbfe3972cb62543a4930460d8564a489f3aea12", size = 2705348, upload-time = "2025-09-16T07:22:37.922Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/f7351052cf9db771fe4f32fca47fd66e6d9b53d8613b17faf7d130a9d553/cython-3.1.4-py3-none-any.whl", hash = "sha256:d194d95e4fa029a3f6c7d46bdd16d973808c7ea4797586911fdb67cb98b1a2c6", size = 1227541, upload-time = "2025-09-16T07:20:29.595Z" }, + { url = "https://files.pythonhosted.org/packages/b5/51/54f5d1bed7b7d003d0584996a030542497aeb05b9241782c217ea71061f5/cython-3.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b3b2f6587b42efdece2d174a2aa4234da4524cc6673f3955c2e62b60c6d11fd", size = 3002973, upload-time = "2025-08-13T06:19:50.777Z" }, + { url = "https://files.pythonhosted.org/packages/05/07/b4043fed60070ee21b0d9ff3a877d2ecdc79231e55119ce852b79b690306/cython-3.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:963cf640d049fcca1cefd62d1653f859892d6dc8e4d958eb49a5babc491de6a1", size = 2865389, upload-time = "2025-08-13T06:19:52.675Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e3/67d349b5310a40f281e153e826ca24d9f7ba2997c06800eda781253dccfd/cython-3.1.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2ab05d1bf2d5522ecff35d94ca233b77db2300413597c3ca0b6448377fa4bd7c", size = 3410316, upload-time = "2025-08-13T06:19:55.217Z" }, + { url = "https://files.pythonhosted.org/packages/49/c4/84aae921135174111091547fa726ea5f8bba718cd12b2589a70c2aab8d5c/cython-3.1.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd517e3be052fb49443585b01f02f46080b3408e32c1108a0fdc4cc25b3c9d30", size = 3189568, upload-time = "2025-08-13T06:19:57.503Z" }, + { url = "https://files.pythonhosted.org/packages/e2/85/1bf18883f1a1f656ad83a671e54553caedb1ee2f39a3e792a14aa82557a2/cython-3.1.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a48e2180d74e3c528561d85b48f9a939a429537f9ea8aac7fb16180e7bff47e2", size = 3312649, upload-time = "2025-08-13T06:19:59.894Z" }, + { url = "https://files.pythonhosted.org/packages/68/da/cc1373decc0d14a25f1b434f47de5e27696f3092319aa5e19fcf84157415/cython-3.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7c9daa90b15f59aa2a0d638ac1b36777a7e80122099952a0295c71190ce14bc", size = 3203821, upload-time = "2025-08-13T06:20:02.124Z" }, + { url = "https://files.pythonhosted.org/packages/0a/32/e10582d6f7b02ee63607f388dbbd34e329c54559c85961ddc5c655266519/cython-3.1.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:08ac646ff42781827f23b7a9b61669cdb92055f52724cd8cbe0e1defc56fce2e", size = 3426853, upload-time = "2025-08-13T06:20:04.474Z" }, + { url = "https://files.pythonhosted.org/packages/e0/da/5b0d1b5a4c7622ec812ad9374c9c6b426d69818f13f51e36f4f25ca01c86/cython-3.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bb0e0e7fceaffa22e4dc9600f7f75998eef5cc6ac5a8c0733b482851ba765ef2", size = 3328872, upload-time = "2025-08-13T06:20:06.834Z" }, + { url = "https://files.pythonhosted.org/packages/b0/5f/f102f6c8d27338f0baf094754c67f920828a19612053abc903e66f84506f/cython-3.1.3-cp311-cp311-win32.whl", hash = "sha256:42b1c3ebe36a52e2a8e939c0651e9ca5d30b81d03f800bbf0499deb0503ab565", size = 2480850, upload-time = "2025-08-13T06:20:08.988Z" }, + { url = "https://files.pythonhosted.org/packages/b7/60/415d0f0f7c2e227707baec11c968387d33507d2eb7f26a2950a323c705e5/cython-3.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:34a973844998281951bf54cdd0b6a9946ba03ba94580820738583a00da167d8f", size = 2712560, upload-time = "2025-08-13T06:20:11.877Z" }, + { url = "https://files.pythonhosted.org/packages/79/26/f433fdfd5182b9231819a99acc49a1f856669532306e7a38dce63ba7297e/cython-3.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:849ef3d15d4354e5f74cdb6d3c80d80b03209b3bf1f4ff93315890b19da18944", size = 3014237, upload-time = "2025-08-13T06:20:13.77Z" }, + { url = "https://files.pythonhosted.org/packages/e5/6c/1bebf44f5f177f8c750e608f82c08cd699b8f28cc233e799379bfcf2a2c2/cython-3.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93dd0f62a3f8e93166d8584f8b243180d681ba8fed1f530b55d5f70c348c5797", size = 2844261, upload-time = "2025-08-13T06:20:15.619Z" }, + { url = "https://files.pythonhosted.org/packages/c7/74/983005ce5954f6dc8360b105a561e51a60ea619c53296afac98253d1c9d7/cython-3.1.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ff4a2cb84798faffb3988bd94636c3ad31a95ff44ef017f09121abffc56f84cf", size = 3388846, upload-time = "2025-08-13T06:20:17.679Z" }, + { url = "https://files.pythonhosted.org/packages/68/50/dbe7edefe9b652bc72d49da07785173e89197b9a2d64a2ac45da9795eccf/cython-3.1.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b05319e36f34d5388deea5cc2bcfd65f9ebf76f4ea050829421a69625dbba4a", size = 3167022, upload-time = "2025-08-13T06:20:19.863Z" }, + { url = "https://files.pythonhosted.org/packages/4a/55/b50548b77203e22262002feae23a172f95282b4b8deb5ad104f08e3dc20d/cython-3.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac902a17934a6da46f80755f49413bc4c03a569ae3c834f5d66da7288ba7e6c", size = 3317782, upload-time = "2025-08-13T06:20:21.962Z" }, + { url = "https://files.pythonhosted.org/packages/f6/1b/20a97507d528dc330d67be4fbad6bc767c681d56192bce8f7117a187b2ad/cython-3.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7d7a555a864b1b08576f9e8a67f3789796a065837544f9f683ebf3188012fdbd", size = 3179818, upload-time = "2025-08-13T06:20:24.419Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/7b32a19c4c6bb0e2cc7ae52269b6b23a42f67f730e4abd4f8dca63660f7a/cython-3.1.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b827ce7d97ef8624adcf2bdda594b3dcb6c9b4f124d8f72001d8aea27d69dc1c", size = 3403206, upload-time = "2025-08-13T06:20:26.846Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e1/08cfd4c5e99f79e62d4a7b0009bbd906748633270935c8a55eee51fead76/cython-3.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7851107204085f4f02d0eb6b660ddcad2ce4846e8b7a1eaba724a0bd3cd68a6b", size = 3327837, upload-time = "2025-08-13T06:20:28.946Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/f3d384be86fa2a6e110b42f42bf97295a513197aaa5532f451c73bf5b48e/cython-3.1.3-cp312-cp312-win32.whl", hash = "sha256:ed20f1b45b2da5a4f8e71a80025bca1cdc96ba35820b0b17658a4a025be920b0", size = 2485990, upload-time = "2025-08-13T06:20:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/de/f0/17cff75e3c141bda73d770f7412632f53e55778d3bfdadfeec4dd3a7d1d6/cython-3.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:dc4ca0f4dec55124cd79ddcfc555be1cbe0092cc99bcf1403621d17b9c6218bb", size = 2704002, upload-time = "2025-08-13T06:20:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/56/c8/46ac27096684f33e27dab749ef43c6b0119c6a0d852971eaefb73256dc4c/cython-3.1.3-py3-none-any.whl", hash = "sha256:d13025b34f72f77bf7f65c1cd628914763e6c285f4deb934314c922b91e6be5a", size = 1225725, upload-time = "2025-08-13T06:19:09.593Z" }, ] [[package]] @@ -479,11 +477,11 @@ wheels = [ [[package]] name = "dnspython" -version = "2.8.0" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, + { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, ] [[package]] @@ -527,27 +525,27 @@ wheels = [ [[package]] name = "fonttools" -version = "4.60.0" +version = "4.59.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/d9/4eabd956fe123651a1f0efe29d9758b3837b5ae9a98934bdb571117033bb/fonttools-4.60.0.tar.gz", hash = "sha256:8f5927f049091a0ca74d35cce7f78e8f7775c83a6901a8fbe899babcc297146a", size = 3553671, upload-time = "2025-09-17T11:34:01.504Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/a5/fba25f9fbdab96e26dedcaeeba125e5f05a09043bf888e0305326e55685b/fonttools-4.59.2.tar.gz", hash = "sha256:e72c0749b06113f50bcb80332364c6be83a9582d6e3db3fe0b280f996dc2ef22", size = 3540889, upload-time = "2025-08-27T16:40:30.97Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/3d/c57731fbbf204ef1045caca28d5176430161ead73cd9feac3e9d9ef77ee6/fonttools-4.60.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a9106c202d68ff5f9b4a0094c4d7ad2eaa7e9280f06427b09643215e706eb016", size = 2830883, upload-time = "2025-09-17T11:32:10.552Z" }, - { url = "https://files.pythonhosted.org/packages/cc/2d/b7a6ebaed464ce441c755252cc222af11edc651d17c8f26482f429cc2c0e/fonttools-4.60.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9da3a4a3f2485b156bb429b4f8faa972480fc01f553f7c8c80d05d48f17eec89", size = 2356005, upload-time = "2025-09-17T11:32:13.248Z" }, - { url = "https://files.pythonhosted.org/packages/ee/c2/ea834e921324e2051403e125c1fe0bfbdde4951a7c1784e4ae6bdbd286cc/fonttools-4.60.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f84de764c6057b2ffd4feb50ddef481d92e348f0c70f2c849b723118d352bf3", size = 5041201, upload-time = "2025-09-17T11:32:15.373Z" }, - { url = "https://files.pythonhosted.org/packages/93/3c/1c64a338e9aa410d2d0728827d5bb1301463078cb225b94589f27558b427/fonttools-4.60.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800b3fa0d5c12ddff02179d45b035a23989a6c597a71c8035c010fff3b2ef1bb", size = 4977696, upload-time = "2025-09-17T11:32:17.674Z" }, - { url = "https://files.pythonhosted.org/packages/07/cc/c8c411a0d9732bb886b870e052f20658fec9cf91118314f253950d2c1d65/fonttools-4.60.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd68f60b030277f292a582d31c374edfadc60bb33d51ec7b6cd4304531819ba", size = 5020386, upload-time = "2025-09-17T11:32:20.089Z" }, - { url = "https://files.pythonhosted.org/packages/13/01/1d3bc07cf92e7f4fc27f06d4494bf6078dc595b2e01b959157a4fd23df12/fonttools-4.60.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:53328e3ca9e5c8660ef6de07c35f8f312c189b757535e12141be7a8ec942de6e", size = 5131575, upload-time = "2025-09-17T11:32:22.582Z" }, - { url = "https://files.pythonhosted.org/packages/5a/16/08db3917ee19e89d2eb0ee637d37cd4136c849dc421ff63f406b9165c1a1/fonttools-4.60.0-cp311-cp311-win32.whl", hash = "sha256:d493c175ddd0b88a5376e61163e3e6fde3be8b8987db9b092e0a84650709c9e7", size = 2229297, upload-time = "2025-09-17T11:32:24.834Z" }, - { url = "https://files.pythonhosted.org/packages/d2/0b/76764da82c0dfcea144861f568d9e83f4b921e84f2be617b451257bb25a7/fonttools-4.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc2770c9dc49c2d0366e9683f4d03beb46c98042d7ccc8ddbadf3459ecb051a7", size = 2277193, upload-time = "2025-09-17T11:32:27.094Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9b/706ebf84b55ab03439c1f3a94d6915123c0d96099f4238b254fdacffe03a/fonttools-4.60.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8c68928a438d60dfde90e2f09aa7f848ed201176ca6652341744ceec4215859f", size = 2831953, upload-time = "2025-09-17T11:32:29.39Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/782f485be450846e4f3aecff1f10e42af414fc6e19d235c70020f64278e1/fonttools-4.60.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b7133821249097cffabf0624eafd37f5a3358d5ce814febe9db688e3673e724e", size = 2351716, upload-time = "2025-09-17T11:32:31.46Z" }, - { url = "https://files.pythonhosted.org/packages/39/77/ad8d2a6ecc19716eb488c8cf118de10f7802e14bdf61d136d7b52358d6b1/fonttools-4.60.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3638905d3d77ac8791127ce181f7cb434f37e4204d8b2e31b8f1e154320b41f", size = 4922729, upload-time = "2025-09-17T11:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/6b/48/aa543037c6e7788e1bc36b3f858ac70a59d32d0f45915263d0b330a35140/fonttools-4.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7968a26ef010ae89aabbb2f8e9dec1e2709a2541bb8620790451ee8aeb4f6fbf", size = 4967188, upload-time = "2025-09-17T11:32:35.74Z" }, - { url = "https://files.pythonhosted.org/packages/ac/58/e407d2028adc6387947eff8f2940b31f4ed40b9a83c2c7bbc8b9255126e2/fonttools-4.60.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ef01ca7847c356b0fe026b7b92304bc31dc60a4218689ee0acc66652c1a36b2", size = 4910043, upload-time = "2025-09-17T11:32:38.054Z" }, - { url = "https://files.pythonhosted.org/packages/16/ef/e78519b3c296ef757a21b792fc6a785aa2ef9a2efb098083d8ed5f6ee2ba/fonttools-4.60.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3482d7ed7867edfcf785f77c1dffc876c4b2ddac19539c075712ff2a0703cf5", size = 5061980, upload-time = "2025-09-17T11:32:40.457Z" }, - { url = "https://files.pythonhosted.org/packages/00/4c/ad72444d1e3ef704ee90af8d5abf198016a39908d322bf41235562fb01a0/fonttools-4.60.0-cp312-cp312-win32.whl", hash = "sha256:8c937c4fe8addff575a984c9519433391180bf52cf35895524a07b520f376067", size = 2217750, upload-time = "2025-09-17T11:32:42.586Z" }, - { url = "https://files.pythonhosted.org/packages/46/55/3e8ac21963e130242f5a9ea2ebc57f5726d704bf4dcca89088b5b637b2d3/fonttools-4.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:99b06d5d6f29f32e312adaed0367112f5ff2d300ea24363d377ec917daf9e8c5", size = 2266025, upload-time = "2025-09-17T11:32:44.8Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a4/247d3e54eb5ed59e94e09866cfc4f9567e274fbf310ba390711851f63b3b/fonttools-4.60.0-py3-none-any.whl", hash = "sha256:496d26e4d14dcccdd6ada2e937e4d174d3138e3d73f5c9b6ec6eb2fd1dab4f66", size = 1142186, upload-time = "2025-09-17T11:33:59.287Z" }, + { url = "https://files.pythonhosted.org/packages/f8/53/742fcd750ae0bdc74de4c0ff923111199cc2f90a4ee87aaddad505b6f477/fonttools-4.59.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:511946e8d7ea5c0d6c7a53c4cb3ee48eda9ab9797cd9bf5d95829a398400354f", size = 2774961, upload-time = "2025-08-27T16:38:47.536Z" }, + { url = "https://files.pythonhosted.org/packages/57/2a/976f5f9fa3b4dd911dc58d07358467bec20e813d933bc5d3db1a955dd456/fonttools-4.59.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5e2682cf7be766d84f462ba8828d01e00c8751a8e8e7ce12d7784ccb69a30d", size = 2344690, upload-time = "2025-08-27T16:38:49.723Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/b7eefc274fcf370911e292e95565c8253b0b87c82a53919ab3c795a4f50e/fonttools-4.59.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5729e12a982dba3eeae650de48b06f3b9ddb51e9aee2fcaf195b7d09a96250e2", size = 5026910, upload-time = "2025-08-27T16:38:51.904Z" }, + { url = "https://files.pythonhosted.org/packages/69/95/864726eaa8f9d4e053d0c462e64d5830ec7c599cbdf1db9e40f25ca3972e/fonttools-4.59.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c52694eae5d652361d59ecdb5a2246bff7cff13b6367a12da8499e9df56d148d", size = 4971031, upload-time = "2025-08-27T16:38:53.676Z" }, + { url = "https://files.pythonhosted.org/packages/24/4c/b8c4735ebdea20696277c70c79e0de615dbe477834e5a7c2569aa1db4033/fonttools-4.59.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f1bbc23ba1312bd8959896f46f667753b90216852d2a8cfa2d07e0cb234144", size = 5006112, upload-time = "2025-08-27T16:38:55.69Z" }, + { url = "https://files.pythonhosted.org/packages/3b/23/f9ea29c292aa2fc1ea381b2e5621ac436d5e3e0a5dee24ffe5404e58eae8/fonttools-4.59.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1a1bfe5378962825dabe741720885e8b9ae9745ec7ecc4a5ec1f1ce59a6062bf", size = 5117671, upload-time = "2025-08-27T16:38:58.984Z" }, + { url = "https://files.pythonhosted.org/packages/ba/07/cfea304c555bf06e86071ff2a3916bc90f7c07ec85b23bab758d4908c33d/fonttools-4.59.2-cp311-cp311-win32.whl", hash = "sha256:e937790f3c2c18a1cbc7da101550a84319eb48023a715914477d2e7faeaba570", size = 2218157, upload-time = "2025-08-27T16:39:00.75Z" }, + { url = "https://files.pythonhosted.org/packages/d7/de/35d839aa69db737a3f9f3a45000ca24721834d40118652a5775d5eca8ebb/fonttools-4.59.2-cp311-cp311-win_amd64.whl", hash = "sha256:9836394e2f4ce5f9c0a7690ee93bd90aa1adc6b054f1a57b562c5d242c903104", size = 2265846, upload-time = "2025-08-27T16:39:02.453Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3d/1f45db2df51e7bfa55492e8f23f383d372200be3a0ded4bf56a92753dd1f/fonttools-4.59.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82906d002c349cad647a7634b004825a7335f8159d0d035ae89253b4abf6f3ea", size = 2769711, upload-time = "2025-08-27T16:39:04.423Z" }, + { url = "https://files.pythonhosted.org/packages/29/df/cd236ab32a8abfd11558f296e064424258db5edefd1279ffdbcfd4fd8b76/fonttools-4.59.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a10c1bd7644dc58f8862d8ba0cf9fb7fef0af01ea184ba6ce3f50ab7dfe74d5a", size = 2340225, upload-time = "2025-08-27T16:39:06.143Z" }, + { url = "https://files.pythonhosted.org/packages/98/12/b6f9f964fe6d4b4dd4406bcbd3328821c3de1f909ffc3ffa558fe72af48c/fonttools-4.59.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:738f31f23e0339785fd67652a94bc69ea49e413dfdb14dcb8c8ff383d249464e", size = 4912766, upload-time = "2025-08-27T16:39:08.138Z" }, + { url = "https://files.pythonhosted.org/packages/73/78/82bde2f2d2c306ef3909b927363170b83df96171f74e0ccb47ad344563cd/fonttools-4.59.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ec99f9bdfee9cdb4a9172f9e8fd578cce5feb231f598909e0aecf5418da4f25", size = 4955178, upload-time = "2025-08-27T16:39:10.094Z" }, + { url = "https://files.pythonhosted.org/packages/92/77/7de766afe2d31dda8ee46d7e479f35c7d48747e558961489a2d6e3a02bd4/fonttools-4.59.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0476ea74161322e08c7a982f83558a2b81b491509984523a1a540baf8611cc31", size = 4897898, upload-time = "2025-08-27T16:39:12.087Z" }, + { url = "https://files.pythonhosted.org/packages/c5/77/ce0e0b905d62a06415fda9f2b2e109a24a5db54a59502b769e9e297d2242/fonttools-4.59.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:95922a922daa1f77cc72611747c156cfb38030ead72436a2c551d30ecef519b9", size = 5049144, upload-time = "2025-08-27T16:39:13.84Z" }, + { url = "https://files.pythonhosted.org/packages/d9/ea/870d93aefd23fff2e07cbeebdc332527868422a433c64062c09d4d5e7fe6/fonttools-4.59.2-cp312-cp312-win32.whl", hash = "sha256:39ad9612c6a622726a6a130e8ab15794558591f999673f1ee7d2f3d30f6a3e1c", size = 2206473, upload-time = "2025-08-27T16:39:15.854Z" }, + { url = "https://files.pythonhosted.org/packages/61/c4/e44bad000c4a4bb2e9ca11491d266e857df98ab6d7428441b173f0fe2517/fonttools-4.59.2-cp312-cp312-win_amd64.whl", hash = "sha256:980fd7388e461b19a881d35013fec32c713ffea1fc37aef2f77d11f332dfd7da", size = 2254706, upload-time = "2025-08-27T16:39:17.893Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/d2f7be3c86708912c02571db0b550121caab8cd88a3c0aacb9cfa15ea66e/fonttools-4.59.2-py3-none-any.whl", hash = "sha256:8bd0f759020e87bb5d323e6283914d9bf4ae35a7307dafb2cbd1e379e720ad37", size = 1132315, upload-time = "2025-08-27T16:40:28.984Z" }, ] [[package]] @@ -639,10 +637,10 @@ name = "gymnasium" version = "1.2.0" 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/fd/17/c2a0e15c2cd5a8e788389b280996db927b923410de676ec5c7b2695e9261/gymnasium-1.2.0.tar.gz", hash = "sha256:344e87561012558f603880baf264ebc97f8a5c997a957b0c9f910281145534b0", size = 821142, upload-time = "2025-06-27T08:21:20.262Z" } wheels = [ @@ -739,11 +737,11 @@ wheels = [ [[package]] name = "kaitaistruct" -version = "0.11" +version = "0.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/b8/ca7319556912f68832daa4b81425314857ec08dfccd8dbc8c0f65c992108/kaitaistruct-0.11.tar.gz", hash = "sha256:053ee764288e78b8e53acf748e9733268acbd579b8d82a427b1805453625d74b", size = 11519, upload-time = "2025-09-08T15:46:25.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/04/dd60b9cb65d580ef6cb6eaee975ad1bdd22d46a3f51b07a1e0606710ea88/kaitaistruct-0.10.tar.gz", hash = "sha256:a044dee29173d6afbacf27bcac39daf89b654dd418cfa009ab82d9178a9ae52a", size = 7061, upload-time = "2022-07-09T00:34:06.729Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/4a/cf14bf3b1f5ffb13c69cf5f0ea78031247790558ee88984a8bdd22fae60d/kaitaistruct-0.11-py2.py3-none-any.whl", hash = "sha256:5c6ce79177b4e193a577ecd359e26516d1d6d000a0bffd6e1010f2a46a62a561", size = 11372, upload-time = "2025-09-08T15:46:23.635Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bf/88ad23efc08708bda9a2647169828e3553bb2093a473801db61f75356395/kaitaistruct-0.10-py2.py3-none-any.whl", hash = "sha256:a97350919adbf37fda881f75e9365e2fb88d04832b7a4e57106ec70119efb235", size = 7013, upload-time = "2022-07-09T00:34:03.905Z" }, ] [[package]] @@ -844,11 +842,11 @@ wheels = [ [[package]] name = "markdown" -version = "3.9" +version = "3.8.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8d/37/02347f6d6d8279247a5837082ebc26fc0d5aaeaf75aa013fcbb433c777ab/markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a", size = 364585, upload-time = "2025-09-04T20:25:22.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071, upload-time = "2025-06-19T17:12:44.483Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/ae/44c4a6a4cbb496d93c6257954260fe3a6e91b7bed2240e5dad2a717f5111/markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280", size = 107441, upload-time = "2025-09-04T20:25:21.784Z" }, + { url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827, upload-time = "2025-06-19T17:12:42.994Z" }, ] [[package]] @@ -929,22 +927,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" }, @@ -1130,28 +1128,28 @@ wheels = [ [[package]] name = "mypy" -version = "1.18.2" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/87/cafd3ae563f88f94eec33f35ff722d043e09832ea8530ef149ec1efbaf08/mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f", size = 12731198, upload-time = "2025-09-19T00:09:44.857Z" }, - { url = "https://files.pythonhosted.org/packages/0f/e0/1e96c3d4266a06d4b0197ace5356d67d937d8358e2ee3ffac71faa843724/mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341", size = 11817879, upload-time = "2025-09-19T00:09:47.131Z" }, - { url = "https://files.pythonhosted.org/packages/72/ef/0c9ba89eb03453e76bdac5a78b08260a848c7bfc5d6603634774d9cd9525/mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d", size = 12427292, upload-time = "2025-09-19T00:10:22.472Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/ec4a061dd599eb8179d5411d99775bec2a20542505988f40fc2fee781068/mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86", size = 13163750, upload-time = "2025-09-19T00:09:51.472Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5f/2cf2ceb3b36372d51568f2208c021870fe7834cf3186b653ac6446511839/mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37", size = 13351827, upload-time = "2025-09-19T00:09:58.311Z" }, - { url = "https://files.pythonhosted.org/packages/c8/7d/2697b930179e7277529eaaec1513f8de622818696857f689e4a5432e5e27/mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8", size = 9757983, upload-time = "2025-09-19T00:10:09.071Z" }, - { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" }, - { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" }, - { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" }, - { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" }, - { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" }, - { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, + { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, ] [[package]] @@ -1174,39 +1172,39 @@ wheels = [ [[package]] name = "numpy" -version = "2.3.3" +version = "2.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/7d/3fec4199c5ffb892bed55cff901e4f39a58c81df9c44c280499e92cad264/numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48", size = 20489306, upload-time = "2025-07-24T21:32:07.553Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d", size = 21259253, upload-time = "2025-09-09T15:56:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569", size = 14450980, upload-time = "2025-09-09T15:56:05.926Z" }, - { url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f", size = 5379709, upload-time = "2025-09-09T15:56:07.95Z" }, - { url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125", size = 6913923, upload-time = "2025-09-09T15:56:09.443Z" }, - { url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48", size = 14589591, upload-time = "2025-09-09T15:56:11.234Z" }, - { url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6", size = 16938714, upload-time = "2025-09-09T15:56:14.637Z" }, - { url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa", size = 16370592, upload-time = "2025-09-09T15:56:17.285Z" }, - { url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30", size = 18884474, upload-time = "2025-09-09T15:56:20.943Z" }, - { url = "https://files.pythonhosted.org/packages/45/fa/7f43ba10c77575e8be7b0138d107e4f44ca4a1ef322cd16980ea3e8b8222/numpy-2.3.3-cp311-cp311-win32.whl", hash = "sha256:eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57", size = 6599794, upload-time = "2025-09-09T15:56:23.258Z" }, - { url = "https://files.pythonhosted.org/packages/0a/a2/a4f78cb2241fe5664a22a10332f2be886dcdea8784c9f6a01c272da9b426/numpy-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa", size = 13088104, upload-time = "2025-09-09T15:56:25.476Z" }, - { url = "https://files.pythonhosted.org/packages/79/64/e424e975adbd38282ebcd4891661965b78783de893b381cbc4832fb9beb2/numpy-2.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7", size = 10460772, upload-time = "2025-09-09T15:56:27.679Z" }, - { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, - { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, - { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, - { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, - { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, - { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, - { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, - { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" }, - { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e", size = 21131019, upload-time = "2025-09-09T15:58:42.838Z" }, - { url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150", size = 14376288, upload-time = "2025-09-09T15:58:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3", size = 5305425, upload-time = "2025-09-09T15:58:48.6Z" }, - { url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0", size = 6819053, upload-time = "2025-09-09T15:58:50.401Z" }, - { url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e", size = 14420354, upload-time = "2025-09-09T15:58:52.704Z" }, - { url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db", size = 16760413, upload-time = "2025-09-09T15:58:55.027Z" }, - { url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844, upload-time = "2025-09-09T15:58:57.359Z" }, + { url = "https://files.pythonhosted.org/packages/96/26/1320083986108998bd487e2931eed2aeedf914b6e8905431487543ec911d/numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9", size = 21259016, upload-time = "2025-07-24T20:24:35.214Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2b/792b341463fa93fc7e55abbdbe87dac316c5b8cb5e94fb7a59fb6fa0cda5/numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168", size = 14451158, upload-time = "2025-07-24T20:24:58.397Z" }, + { url = "https://files.pythonhosted.org/packages/b7/13/e792d7209261afb0c9f4759ffef6135b35c77c6349a151f488f531d13595/numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b", size = 5379817, upload-time = "2025-07-24T20:25:07.746Z" }, + { url = "https://files.pythonhosted.org/packages/49/ce/055274fcba4107c022b2113a213c7287346563f48d62e8d2a5176ad93217/numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8", size = 6913606, upload-time = "2025-07-24T20:25:18.84Z" }, + { url = "https://files.pythonhosted.org/packages/17/f2/e4d72e6bc5ff01e2ab613dc198d560714971900c03674b41947e38606502/numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d", size = 14589652, upload-time = "2025-07-24T20:25:40.356Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b0/fbeee3000a51ebf7222016e2939b5c5ecf8000a19555d04a18f1e02521b8/numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3", size = 16938816, upload-time = "2025-07-24T20:26:05.721Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ec/2f6c45c3484cc159621ea8fc000ac5a86f1575f090cac78ac27193ce82cd/numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f", size = 16370512, upload-time = "2025-07-24T20:26:30.545Z" }, + { url = "https://files.pythonhosted.org/packages/b5/01/dd67cf511850bd7aefd6347aaae0956ed415abea741ae107834aae7d6d4e/numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097", size = 18884947, upload-time = "2025-07-24T20:26:58.24Z" }, + { url = "https://files.pythonhosted.org/packages/a7/17/2cf60fd3e6a61d006778735edf67a222787a8c1a7842aed43ef96d777446/numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220", size = 6599494, upload-time = "2025-07-24T20:27:09.786Z" }, + { url = "https://files.pythonhosted.org/packages/d5/03/0eade211c504bda872a594f045f98ddcc6caef2b7c63610946845e304d3f/numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170", size = 13087889, upload-time = "2025-07-24T20:27:29.558Z" }, + { url = "https://files.pythonhosted.org/packages/13/32/2c7979d39dafb2a25087e12310fc7f3b9d3c7d960df4f4bc97955ae0ce1d/numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89", size = 10459560, upload-time = "2025-07-24T20:27:46.803Z" }, + { url = "https://files.pythonhosted.org/packages/00/6d/745dd1c1c5c284d17725e5c802ca4d45cfc6803519d777f087b71c9f4069/numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b", size = 20956420, upload-time = "2025-07-24T20:28:18.002Z" }, + { url = "https://files.pythonhosted.org/packages/bc/96/e7b533ea5740641dd62b07a790af5d9d8fec36000b8e2d0472bd7574105f/numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f", size = 14184660, upload-time = "2025-07-24T20:28:39.522Z" }, + { url = "https://files.pythonhosted.org/packages/2b/53/102c6122db45a62aa20d1b18c9986f67e6b97e0d6fbc1ae13e3e4c84430c/numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0", size = 5113382, upload-time = "2025-07-24T20:28:48.544Z" }, + { url = "https://files.pythonhosted.org/packages/2b/21/376257efcbf63e624250717e82b4fae93d60178f09eb03ed766dbb48ec9c/numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b", size = 6647258, upload-time = "2025-07-24T20:28:59.104Z" }, + { url = "https://files.pythonhosted.org/packages/91/ba/f4ebf257f08affa464fe6036e13f2bf9d4642a40228781dc1235da81be9f/numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370", size = 14281409, upload-time = "2025-07-24T20:40:30.298Z" }, + { url = "https://files.pythonhosted.org/packages/59/ef/f96536f1df42c668cbacb727a8c6da7afc9c05ece6d558927fb1722693e1/numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73", size = 16641317, upload-time = "2025-07-24T20:40:56.625Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a7/af813a7b4f9a42f498dde8a4c6fcbff8100eed00182cc91dbaf095645f38/numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc", size = 16056262, upload-time = "2025-07-24T20:41:20.797Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5d/41c4ef8404caaa7f05ed1cfb06afe16a25895260eacbd29b4d84dff2920b/numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be", size = 18579342, upload-time = "2025-07-24T20:41:50.753Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4f/9950e44c5a11636f4a3af6e825ec23003475cc9a466edb7a759ed3ea63bd/numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036", size = 6320610, upload-time = "2025-07-24T20:42:01.551Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2f/244643a5ce54a94f0a9a2ab578189c061e4a87c002e037b0829dd77293b6/numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f", size = 12786292, upload-time = "2025-07-24T20:42:20.738Z" }, + { url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" }, + { url = "https://files.pythonhosted.org/packages/cf/ea/50ebc91d28b275b23b7128ef25c3d08152bc4068f42742867e07a870a42a/numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15", size = 21130338, upload-time = "2025-07-24T20:57:54.37Z" }, + { url = "https://files.pythonhosted.org/packages/9f/57/cdd5eac00dd5f137277355c318a955c0d8fb8aa486020c22afd305f8b88f/numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec", size = 14375776, upload-time = "2025-07-24T20:58:16.303Z" }, + { url = "https://files.pythonhosted.org/packages/83/85/27280c7f34fcd305c2209c0cdca4d70775e4859a9eaa92f850087f8dea50/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712", size = 5304882, upload-time = "2025-07-24T20:58:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/48/b4/6500b24d278e15dd796f43824e69939d00981d37d9779e32499e823aa0aa/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c", size = 6818405, upload-time = "2025-07-24T20:58:37.341Z" }, + { url = "https://files.pythonhosted.org/packages/9b/c9/142c1e03f199d202da8e980c2496213509291b6024fd2735ad28ae7065c7/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296", size = 14419651, upload-time = "2025-07-24T20:58:59.048Z" }, + { url = "https://files.pythonhosted.org/packages/8b/95/8023e87cbea31a750a6c00ff9427d65ebc5fef104a136bfa69f76266d614/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981", size = 16760166, upload-time = "2025-07-24T21:28:56.38Z" }, + { url = "https://files.pythonhosted.org/packages/78/e3/6690b3f85a05506733c7e90b577e4762517404ea78bab2ca3a5cb1aeb78d/numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619", size = 12977811, upload-time = "2025-07-24T21:29:18.234Z" }, ] [[package]] @@ -1263,6 +1261,7 @@ dependencies = [ { name = "cffi" }, { name = "crcmod" }, { name = "cython" }, + { name = "dearpygui" }, { name = "future-fstrings" }, { name = "inputs" }, { name = "json-rpc" }, @@ -1338,7 +1337,6 @@ testing = [ { name = "ruff" }, ] tools = [ - { name = "dearpygui" }, { name = "metadrive-simulator", marker = "platform_machine != 'aarch64'" }, ] @@ -1355,7 +1353,7 @@ requires-dist = [ { name = "crcmod" }, { name = "cython" }, { name = "dbus-next", marker = "extra == 'dev'" }, - { name = "dearpygui", marker = "extra == 'tools'", specifier = ">=2.1.0" }, + { name = "dearpygui", specifier = ">=2.1.0" }, { name = "dictdiffer", marker = "extra == 'dev'" }, { name = "future-fstrings" }, { name = "hypothesis", marker = "extra == 'testing'", specifier = "==6.47.*" }, @@ -1399,7 +1397,7 @@ requires-dist = [ { name = "pywinctl", marker = "extra == 'dev'" }, { name = "pyzmq" }, { name = "qrcode" }, - { name = "raylib", marker = "extra == 'dev'", specifier = "==5.5.0.2" }, + { name = "raylib", marker = "extra == 'dev'" }, { name = "requests" }, { name = "ruff", marker = "extra == 'testing'" }, { name = "scons" }, @@ -1452,8 +1450,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, upload-time = "2021-05-21T05:46:32.738Z" } wheels = [ @@ -1465,8 +1463,8 @@ name = "panda3d-simplepbr" version = "0.13.1" 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/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload-time = "2025-03-30T16:57:41.087Z" } wheels = [ @@ -1607,32 +1605,31 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.1" +version = "6.32.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, - { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, - { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, - { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, - { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, + { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, + { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, + { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, + { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, ] [[package]] name = "psutil" -version = "7.1.0" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/31/4723d756b59344b643542936e37a31d1d3204bcdc42a7daa8ee9eb06fb50/psutil-7.1.0.tar.gz", hash = "sha256:655708b3c069387c8b77b072fc429a57d0e214221d01c0a772df7dfedcb3bcd2", size = 497660, upload-time = "2025-09-17T20:14:52.902Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/62/ce4051019ee20ce0ed74432dd73a5bb087a6704284a470bb8adff69a0932/psutil-7.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76168cef4397494250e9f4e73eb3752b146de1dd950040b29186d0cce1d5ca13", size = 245242, upload-time = "2025-09-17T20:14:56.126Z" }, - { url = "https://files.pythonhosted.org/packages/38/61/f76959fba841bf5b61123fbf4b650886dc4094c6858008b5bf73d9057216/psutil-7.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:5d007560c8c372efdff9e4579c2846d71de737e4605f611437255e81efcca2c5", size = 246682, upload-time = "2025-09-17T20:14:58.25Z" }, - { url = "https://files.pythonhosted.org/packages/88/7a/37c99d2e77ec30d63398ffa6a660450b8a62517cabe44b3e9bae97696e8d/psutil-7.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22e4454970b32472ce7deaa45d045b34d3648ce478e26a04c7e858a0a6e75ff3", size = 287994, upload-time = "2025-09-17T20:14:59.901Z" }, - { url = "https://files.pythonhosted.org/packages/9d/de/04c8c61232f7244aa0a4b9a9fbd63a89d5aeaf94b2fc9d1d16e2faa5cbb0/psutil-7.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c70e113920d51e89f212dd7be06219a9b88014e63a4cec69b684c327bc474e3", size = 291163, upload-time = "2025-09-17T20:15:01.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/58/c4f976234bf6d4737bc8c02a81192f045c307b72cf39c9e5c5a2d78927f6/psutil-7.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d4a113425c037300de3ac8b331637293da9be9713855c4fc9d2d97436d7259d", size = 293625, upload-time = "2025-09-17T20:15:04.492Z" }, - { url = "https://files.pythonhosted.org/packages/79/87/157c8e7959ec39ced1b11cc93c730c4fb7f9d408569a6c59dbd92ceb35db/psutil-7.1.0-cp37-abi3-win32.whl", hash = "sha256:09ad740870c8d219ed8daae0ad3b726d3bf9a028a198e7f3080f6a1888b99bca", size = 244812, upload-time = "2025-09-17T20:15:07.462Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e9/b44c4f697276a7a95b8e94d0e320a7bf7f3318521b23de69035540b39838/psutil-7.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:57f5e987c36d3146c0dd2528cd42151cf96cd359b9d67cfff836995cc5df9a3d", size = 247965, upload-time = "2025-09-17T20:15:09.673Z" }, - { url = "https://files.pythonhosted.org/packages/26/65/1070a6e3c036f39142c2820c4b52e9243246fcfc3f96239ac84472ba361e/psutil-7.1.0-cp37-abi3-win_arm64.whl", hash = "sha256:6937cb68133e7c97b6cc9649a570c9a18ba0efebed46d8c5dae4c07fa1b67a07", size = 244971, upload-time = "2025-09-17T20:15:12.262Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, ] [[package]] @@ -1665,47 +1662,47 @@ sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de7 [[package]] name = "pycapnp" -version = "2.2.0" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/96/0b1696ed89950b34506594a2c1f0c19c31d91385dfee81295113e9f22442/pycapnp-2.2.0.tar.gz", hash = "sha256:06466a74e44858b38f920784a8fe74172463a8e73e8cb8298dfe02a95b8997d6", size = 707734, upload-time = "2025-09-13T16:42:04.868Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/fb/54b46b52c1fa2acd9afd81bd05810c61bb1b05c6084c9625b64bc6d41843/pycapnp-2.0.0.tar.gz", hash = "sha256:503ab9b7b16773590ee226f2460408972c6b1c2cb2d819037115b919bef682be", size = 574848, upload-time = "2024-04-12T15:35:44.019Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/8d/11dd189691d78e2ca2200bf2a7677bd6bf7594f74ccc7b10ccafafd6761b/pycapnp-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e000956e08cb75070aa35123cbeae73d045e0e6c6aa347a09d53769dc675fa9", size = 1645501, upload-time = "2025-09-13T16:39:36.828Z" }, - { url = "https://files.pythonhosted.org/packages/05/9f/321878b9aeea9eeac1977504a6daf46625a636ec99b687242528263fbbe3/pycapnp-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a45081bde2adaca6a5bf98ff949962d466f94114f6e98a0eab8f55d424786f24", size = 1509785, upload-time = "2025-09-13T16:39:38.623Z" }, - { url = "https://files.pythonhosted.org/packages/79/ec/7d6085792c75855785f8e7e97a35ea8746058124238479d4509f0a25852a/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a1b10ab77d2a61bc033e74e0c90669222ede6f6b462eb8f1f97f592e6b547500", size = 5338620, upload-time = "2025-09-13T16:39:40.05Z" }, - { url = "https://files.pythonhosted.org/packages/3a/13/4244ffd7d513fca5e5c086251649586c05734923ee16b754cd22a85d7bda/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:f325fb69a8a4181ca3591ec311f1ae854dd71a199f6f55761008fdab96544fbb", size = 5420575, upload-time = "2025-09-13T16:39:42.255Z" }, - { url = "https://files.pythonhosted.org/packages/c5/7f/b2f592836d870e1e35a2cc0d4a8ece4c877dbeb2e55b040e0c00b95a01c8/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:e61420b11178a856b962e5e4a89c1755e3b1677762e976a98aa628ad96b70776", size = 5835575, upload-time = "2025-09-13T16:39:43.85Z" }, - { url = "https://files.pythonhosted.org/packages/e4/36/76ce7c4da4141f7e75db0fd4e95d51328f1f583e3d30eb52ed7dcde91bcf/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:6b7b6a5f96ee95a8b33b85f7f87d18129bdba4e3c22cad3b6485b675607b1aeb", size = 5868584, upload-time = "2025-09-13T16:39:45.893Z" }, - { url = "https://files.pythonhosted.org/packages/4b/20/c83deb8734b2b0cc083344ede75b81f5af6466a7d95868adbaf14180c21d/pycapnp-2.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d109f8e9d3b46fb28c64b1a87cae7f26447bf07253de1e7dfd057ee38b0654a4", size = 5530789, upload-time = "2025-09-13T16:39:47.63Z" }, - { url = "https://files.pythonhosted.org/packages/4c/5d/a2600534c492196cb01f102c515bdea3271352bb7d37c6910603b248ddb0/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:93db5b840a400c6c22ceb93b0494182d087a5a40f81cf179881f0f5bbcd6e439", size = 6228635, upload-time = "2025-09-13T16:39:49.366Z" }, - { url = "https://files.pythonhosted.org/packages/27/5b/2e21278f7746a36b29e2cde28b21ffc479dd09c2cb1ed963cf0f89e8caf8/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab4fd171102c2d7222fd1e80fa190652a95a58e56dd731f6f7e81806ed5bc012", size = 6560947, upload-time = "2025-09-13T16:39:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/74/4e/87bf0adb4644d45658e10eb97e08d16992a7864ff9816061a002b121da23/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:da4ea4932569279c1901e97cfa761754a7666389dfef5ae5df90ffa20de7566d", size = 6748953, upload-time = "2025-09-13T16:39:53.859Z" }, - { url = "https://files.pythonhosted.org/packages/14/09/8e2c007a9514793fb7a2f675212e4ebc31ae7e59d131c3d561f123793d0a/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:289da6a5329ec7ff323aa8d10f06709ea086111c3c4558774370907b8f43c7ba", size = 6779013, upload-time = "2025-09-13T16:39:55.931Z" }, - { url = "https://files.pythonhosted.org/packages/33/6f/747705eacdf75797feef5429ad42756ba6afcc51a96bc3f1627c3f2b11a2/pycapnp-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:16b7670c6a5720277823c7299401fc1009b62dffb5777e3b121ae66997a6674b", size = 6496769, upload-time = "2025-09-13T16:39:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/5d/4a/d4c65b305a2156f4fe073e84121938780eacd9528d7c977cbc6f1659787e/pycapnp-2.2.0-cp311-cp311-win32.whl", hash = "sha256:ec226235a0677e9cfb020a847fd5ff553ef2de0d67d55e19c1ea4b313263a594", size = 1064932, upload-time = "2025-09-13T16:39:58.967Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3f/66cc7cae9d02462ab633c443ca408003328156218480ab4a4c61272fbab2/pycapnp-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:402ec2aacf6256a0969eff6b8b28ab9f5ec5ba92b9cc6b7dffc41756043e15d0", size = 1220053, upload-time = "2025-09-13T16:40:00.89Z" }, - { url = "https://files.pythonhosted.org/packages/3e/79/3d82be14faf0ec38817e5b2e838905356ef801b8d45c40dcaf685d742142/pycapnp-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a4a96ebe3f854f5e0f57b4d3571a0fe802686f7fd8cfa603973ec692fb47abce", size = 1637264, upload-time = "2025-09-13T16:40:02.557Z" }, - { url = "https://files.pythonhosted.org/packages/20/b5/a31b28de6093a3907e33a3604e0a24d27c9a24ad420261d23dea519e4559/pycapnp-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:70426b32474f29101293dd4271ca9b309ae549422ffb3ba98eb0483897af5cbe", size = 1501144, upload-time = "2025-09-13T16:40:04.376Z" }, - { url = "https://files.pythonhosted.org/packages/cc/76/e2b2702fadeb2d9492ed6ef7df9f5d57d5bac1709108a73c072b279fb7db/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fc9eff6b76a2414c377a77c927f187282bc7b83005adfc3df63b43e7cd676d39", size = 5295044, upload-time = "2025-09-13T16:40:06.283Z" }, - { url = "https://files.pythonhosted.org/packages/99/4d/16e8d8ccb4b8b63d4d2c0fd745ed88287de0323e596b091fa515e775fe4f/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:a7af220f446e84373764c701197c25c1eabcc13832a7532911c191d81a61387f", size = 5343558, upload-time = "2025-09-13T16:40:07.85Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ae/a570f5cc825b71510877ebe5b8041c437d8e0e9993d5b134efbc31daf844/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c5caf1e6fcd9a068c7fafb286a915e75fe7585c9525eeac9e8a641cbd92f1982", size = 5667031, upload-time = "2025-09-13T16:40:09.489Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ee/d03a871153e90846fe8ea43274f7e161294f0a443fcf6c784c78edf2ceb0/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4456362dcc6acec2c801d817ad6e62a798a08aacb5e725a25f013d8318b6ce42", size = 5804310, upload-time = "2025-09-13T16:40:11.097Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8e/fc827b28128dc3921462d563ae8981a8a9ae25bd8f49bc4fe83c0793f804/pycapnp-2.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c66ba31fac33a1707d38eb0fe53aa29fd27ce6a2a4f71f415a1229c30706a75e", size = 5522994, upload-time = "2025-09-13T16:40:12.788Z" }, - { url = "https://files.pythonhosted.org/packages/4f/4d/4bc662dd058b9f79d5a8a691a56bf9941fbe4e340098cf7f9ae49c6c8761/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:33d0d1a286e1135e1913d4a3cb0a2c972a206b1984c3d23e87cdf11e1d523b70", size = 6145416, upload-time = "2025-09-13T16:40:14.444Z" }, - { url = "https://files.pythonhosted.org/packages/97/63/44cc5dc368ed833ecc0f20be8523842b965177f4ac16ff81a3663d2f46c3/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:63aeb83c042c84e5bd9bce9f699be30279d925ea00c3a92b3a4cb8e3f2cbe957", size = 6474037, upload-time = "2025-09-13T16:40:16.104Z" }, - { url = "https://files.pythonhosted.org/packages/95/39/52753dd2dbfc4874b0ac2321a629ccd1355d9470323632701d3b7c1ab0cc/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e9d025d04115d7341148ca654e144ddecbc9af7d215426e69fa6ec8036b1467f", size = 6604316, upload-time = "2025-09-13T16:40:17.868Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a6/3f178b23a5b9f2afe2d12614f2eea740defa3dd0797397870c6f62952e69/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:51c658e633fa41fae616fcddebf048a505e3080378853b7e3e267858c2fd20f7", size = 6710383, upload-time = "2025-09-13T16:40:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/ad/ed/934f2f2c8d678c76b5616b165a0277d49d0d40dbe8e3d62a7eb1bf9bac95/pycapnp-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5802b63303f66066e32b704d094e2629eff1d40add9ae7c4317753ab601456b4", size = 6445575, upload-time = "2025-09-13T16:40:21.826Z" }, - { url = "https://files.pythonhosted.org/packages/c9/42/cef707de9fbc2e70aa47a26e3c17444846bb7e19cd9ea51a6ed9dce4eced/pycapnp-2.2.0-cp312-cp312-win32.whl", hash = "sha256:e21f69ce0deac8dbd68424067560bfe31102ab6728d410bd66a34c9b7aaceba5", size = 1049436, upload-time = "2025-09-13T16:40:23.425Z" }, - { url = "https://files.pythonhosted.org/packages/17/c9/82dee36033c30c39e4860df82fc475ccc6323b075dfaa518092f1cc722f6/pycapnp-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ddf0364c26028bff6e0f90da41ac28fae96703a74279a7ba6c179eaff794ecbf", size = 1188941, upload-time = "2025-09-13T16:40:24.766Z" }, + { url = "https://files.pythonhosted.org/packages/cb/82/cf311b1a9800b605759a38a0c337a55a639b685427364294e98a0f9b7306/pycapnp-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:829c7eb4e5f23dbcac25466110faf72a691035cf87c5d46e5053da15790e428d", size = 1673673, upload-time = "2024-04-12T15:33:32.211Z" }, + { url = "https://files.pythonhosted.org/packages/ae/55/4c03ca95c568776a1f637db9ffdcf302fb63f46e4d2a4f13edd8cb1a5f90/pycapnp-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab60fbe3e4eaf99ec97918a0a776216c6c149b6d49261383d91c2201adb475d", size = 1513351, upload-time = "2024-04-12T15:33:35.156Z" }, + { url = "https://files.pythonhosted.org/packages/55/98/e4b2dea076f8a2575abc45cd879a91bc9aa975c69ae2ac1cab61d83c5087/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c48a0582078bb74d7326d28571db0b8e6919563365537a5a13e8f5360c12bfc", size = 4910666, upload-time = "2024-04-12T15:33:37.798Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ee/3b5a182588f89074f4002fa6247e3f963bb85bc808acd1ac8deed91f1fa7/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb5ab54aff857e3711d2c0cc934194aaffacdeb3481daa56863daef07d27941", size = 5007434, upload-time = "2024-04-12T15:33:40.362Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e9/515a2ca7fdc84d57c654280d0b71dfd782fd1773d384c0ec0d56dc6fc35b/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9600778036e6fe9dbea68f0c37678c5f4d561d2f2306b3cb741de5e1670ef2ae", size = 5188923, upload-time = "2024-04-12T15:33:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/5db346e238985a526ba7589ed24f92195dad39e7dec9d85b17a567600b6f/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7278ba0262fab8c398e77d634ae7ba026866d44b52cbfc27262be8d396ecacd1", size = 5048105, upload-time = "2024-04-12T15:33:44.294Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ff/02b4a87c9ff9793f26d8f3d95312d902d260c094f216d84e19528a506606/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b2458d43c82302980a96518c96df257429204d2cc02bfff0c8cb6ebb371e01", size = 5063172, upload-time = "2024-04-12T15:33:46.954Z" }, + { url = "https://files.pythonhosted.org/packages/10/a1/35a7e14d765f99cfdcdfdcebc69bdf382f27016944470daa7a03c557f681/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd7755cc3fedc2ad8cc7864a0729471ddeff10c184963fe0f3689e295130f1b2", size = 5408718, upload-time = "2024-04-12T15:33:49.587Z" }, + { url = "https://files.pythonhosted.org/packages/5c/59/8bc8a993c38808c6fd90b10becba8de4a54543e8441bd87ce44ef3b7eee4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d47baf6b3db9981625ffc5ff188e089f2ebca8e7e1afb97aa5eb7bebb7bf3650", size = 5596714, upload-time = "2024-04-12T15:33:51.518Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7d/79c481ef77f29e81355e92bb250f0d2a37a76f5fe0ba9433bf6c6c88b6e4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b375be92d93fdb6f7ac127ea9390bcec0fed4e485db137b084f9e7114dde7c83", size = 5709896, upload-time = "2024-04-12T15:33:53.716Z" }, + { url = "https://files.pythonhosted.org/packages/59/8d/f2eceeea1e8cae8b8a70a4752af5b772916f455e2ed388d0887e2b57d080/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:959bfdf1cddb3e5528e2293c4a375382be9a1bf044b073bc2e7eca1eb6b3a9a2", size = 5594823, upload-time = "2024-04-12T15:33:55.573Z" }, + { url = "https://files.pythonhosted.org/packages/2c/86/f8284637b61f83232e5618dd561a66080dd98ce2272d7e3ae89335d4fd97/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d873af167cf5cc7578ce5432eefcb442f866c8f7a6c57d188baf8c5e709fa39d", size = 5572564, upload-time = "2024-04-12T15:33:59.112Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b2/7f99d28a9935d1e37ec6955922c57b2be24fe0b74fe25929643686cc11e5/pycapnp-2.0.0-cp311-cp311-win32.whl", hash = "sha256:40ca8018e0b7686d549b920f087049b92a3e6f06976d9f5a8112603fc560cac4", size = 1040268, upload-time = "2024-04-12T15:34:00.933Z" }, + { url = "https://files.pythonhosted.org/packages/1d/37/89ab98961f18cffeae20d98cfc24afcfa85024bc014ecc48b0c4ac264fe0/pycapnp-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d15cd8e46d541a899c84809095d7d7b3951f43642d1859e7a39bd91910778479", size = 1141758, upload-time = "2024-04-12T15:34:02.607Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d5/0ee84de3ce34a86c373b6cfbea17d5486c2ca942d51efa99a0069723c1e3/pycapnp-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0c111ef96676df25b8afef98f369d45f838ad4434e2898e48199eb43ef704efe", size = 1645816, upload-time = "2024-04-12T15:34:04.428Z" }, + { url = "https://files.pythonhosted.org/packages/35/1e/580572083165ba791fac5ae2d8917facb94db6e3f0500421673f55165dac/pycapnp-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d18906eb1fd1b9f206d93a9591ceedce1d52e7766b66e68f271453f104e9dca", size = 1507892, upload-time = "2024-04-12T15:34:06.933Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ed/46b3cc5d32c525b6a3acb67eb43de2cec692a62775ec1ab66dafe2b7d6ad/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5d1ed365ab1beabb8838068907a7190cc0b6f16de3499d783627e670fcc0eb2", size = 4707960, upload-time = "2024-04-12T15:34:08.771Z" }, + { url = "https://files.pythonhosted.org/packages/8e/51/0a0a4d4e44138adb84959478ea4966196c5ad32022f768b9b64d1590cb3e/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:495b39a7aa2629931bbca27ad743ce591c6c41e8f81792276be424742d9cd1c1", size = 4791780, upload-time = "2024-04-12T15:34:10.863Z" }, + { url = "https://files.pythonhosted.org/packages/28/71/2b59c6ddb253b25b3d01ee6f7b32b0297ac205c7272beeb6d13399054430/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50e814fbde072dcc3d868b5b5cbb9b7a66a70bff9ad03942f3be9baf3ca1cfc6", size = 4961068, upload-time = "2024-04-12T15:34:13.543Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b8/b64fdefa59d6d2802b5ee0a9439396c23a3e5954da6909be81f2722a234c/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:920fdda62d5fdef7a48339104dff0ceb9dcc21b138491f854457ba3a3d4d63ec", size = 4872917, upload-time = "2024-04-12T15:34:15.636Z" }, + { url = "https://files.pythonhosted.org/packages/c8/55/867595f575eb6cb3662e9a0b50a24b4be42df86f2938003e586f6c81606f/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f9142eb4714c152b09dda0b055ea9dd43fd8fd894132e7eb4fa235fb4915edd", size = 4912169, upload-time = "2024-04-12T15:34:17.758Z" }, + { url = "https://files.pythonhosted.org/packages/e4/11/0d36b45e5005ecdf8510081d16c6fb7b22b49651f64af36d138df97980cc/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c98f1d0c4d32109d03e42828ce3c65236afc895033633cbed3ca092993702e7b", size = 5201744, upload-time = "2024-04-12T15:34:20.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/29/ad1357998656b7141939e55bb3aea727c7a5478026feed7f8ee8cf52c935/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4d3250c1875a309d67551843cd8bf3c5e7fccf159b7f5c118a92aee36c0e871c", size = 5351113, upload-time = "2024-04-12T15:34:23.173Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b1/f4c442907948a29b6427dd7436f31d3732bb0d77f5c1dbcad749ba56dac0/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:174e6babe01f5507111c0ed226cd0b5e9325a9d2850751cfe4a57c1670f13881", size = 5472055, upload-time = "2024-04-12T15:34:25.799Z" }, + { url = "https://files.pythonhosted.org/packages/c1/06/a6eceb8b8015f518c0ccae1de5d1a6e18ed73b62b4b111aff54ce5f4f566/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:ed38ece414341285695526792e020f391f29f5064b2126d0367c8bdeef28e3e9", size = 5395743, upload-time = "2024-04-12T15:34:28.134Z" }, + { url = "https://files.pythonhosted.org/packages/e7/b0/63f2b0327853ae08158de61b4dfc7fa43ae5a5c00f1d28f769e7c30cdf55/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a20b7dc55ef83a1fa446bf12680bce25caeb8f81788b623b072c3ec820db50d", size = 5405076, upload-time = "2024-04-12T15:34:30.305Z" }, + { url = "https://files.pythonhosted.org/packages/7d/24/e025dd95f1abf34e373fbab8841ac8e5fa62afe3af4a4b0c61bd01354400/pycapnp-2.0.0-cp312-cp312-win32.whl", hash = "sha256:145eea66233fb5ac9152cd1c06b999ddb691815126f87f5cc37b9cda5d569f8a", size = 1030361, upload-time = "2024-04-12T15:34:32.25Z" }, + { url = "https://files.pythonhosted.org/packages/3f/70/a71108ee9d4db9a027b665a2c383202407207174f1956195d5be45aca705/pycapnp-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:b8b03000769b29b36a8810f458b931f0f706f42027ee6676821eff28092d7734", size = 1135121, upload-time = "2024-04-12T15:34:34.208Z" }, ] [[package]] name = "pycparser" -version = "2.23" +version = "2.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, ] [[package]] @@ -1831,12 +1828,9 @@ wheels = [ [[package]] name = "pymsgbox" -version = "2.0.1" +version = "1.0.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/6a/e80da7594ee598a776972d09e2813df2b06b3bc29218f440631dfa7c78a8/pymsgbox-2.0.1.tar.gz", hash = "sha256:98d055c49a511dcc10fa08c3043e7102d468f5e4b3a83c6d3c61df722c7d798d", size = 20768, upload-time = "2025-09-09T00:38:56.863Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/3e/08c8cac81b2b2f7502746e6b9c8e5b0ec6432cd882c605560fc409aaf087/pymsgbox-2.0.1-py3-none-any.whl", hash = "sha256:5de8ec19bca2ca7e6c09d39c817c83f17c75cee80275235f43a9931db699f73b", size = 9994, upload-time = "2025-09-09T00:38:55.672Z" }, -] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ff/4c6f31a4f08979f12a663f2aeb6c8b765d3bd592e66eaaac445f547bb875/PyMsgBox-1.0.9.tar.gz", hash = "sha256:2194227de8bff7a3d6da541848705a155dcbb2a06ee120d9f280a1d7f51263ff", size = 18829, upload-time = "2020-10-11T01:51:43.227Z" } [[package]] name = "pyobjc" @@ -4207,9 +4201,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, upload-time = "2025-01-22T00:16:58.421Z" } wheels = [ @@ -4239,21 +4233,18 @@ wheels = [ [[package]] name = "pyparsing" -version = "3.2.4" +version = "3.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/c9/b4594e6a81371dfa9eb7a2c110ad682acf985d96115ae8b25a1d63b4bf3b/pyparsing-3.2.4.tar.gz", hash = "sha256:fff89494f45559d0f2ce46613b419f632bbb6afbdaed49696d322bcf98a58e99", size = 1098809, upload-time = "2025-09-13T05:47:19.732Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/b8/fbab973592e23ae313042d450fc26fa24282ebffba21ba373786e1ce63b4/pyparsing-3.2.4-py3-none-any.whl", hash = "sha256:91d0fcde680d42cd031daf3a6ba20da3107e08a75de50da58360e7d94ab24d36", size = 113869, upload-time = "2025-09-13T05:47:17.863Z" }, + { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" }, ] [[package]] name = "pyperclip" -version = "1.10.0" +version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/99/25f4898cf420efb6f45f519de018f4faea5391114a8618b16736ef3029f1/pyperclip-1.10.0.tar.gz", hash = "sha256:180c8346b1186921c75dfd14d9048a6b5d46bfc499778811952c6dd6eb1ca6be", size = 12193, upload-time = "2025-09-18T00:54:00.384Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/bc/22540e73c5f5ae18f02924cd3954a6c9a4aa6b713c841a94c98335d333a1/pyperclip-1.10.0-py3-none-any.whl", hash = "sha256:596fbe55dc59263bff26e61d2afbe10223e2fccb5210c9c96a28d6887cfcc7ec", size = 11062, upload-time = "2025-09-18T00:53:59.252Z" }, -] +sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961, upload-time = "2024-06-18T20:38:48.401Z" } [[package]] name = "pyprof2calltree" @@ -4287,7 +4278,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.2" +version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -4296,22 +4287,21 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, ] [[package]] name = "pytest-asyncio" -version = "1.2.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, + { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, ] [[package]] @@ -4328,26 +4318,26 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.15.1" +version = "3.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, + { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, ] [[package]] name = "pytest-randomly" -version = "4.0.1" +version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/1d/258a4bf1109258c00c35043f40433be5c16647387b6e7cd5582d638c116b/pytest_randomly-4.0.1.tar.gz", hash = "sha256:174e57bb12ac2c26f3578188490bd333f0e80620c3f47340158a86eca0593cd8", size = 14130, upload-time = "2025-09-12T15:23:00.085Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367, upload-time = "2024-10-25T15:45:34.274Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/3e/a4a9227807b56869790aad3e24472a554b585974fe7e551ea350f50897ae/pytest_randomly-4.0.1-py3-none-any.whl", hash = "sha256:e0dfad2fd4f35e07beff1e47c17fbafcf98f9bf4531fd369d9260e2f858bfcb7", size = 8304, upload-time = "2025-09-12T15:22:58.946Z" }, + { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396, upload-time = "2024-10-25T15:45:32.78Z" }, ] [[package]] @@ -4389,7 +4379,7 @@ wheels = [ [[package]] name = "pytest-xdist" -version = "3.7.1.dev24+g2b4372bd6" +version = "3.7.1.dev24+g2b4372b" source = { git = "https://github.com/sshane/pytest-xdist?rev=2b4372bd62699fb412c4fe2f95bf9f01bd2018da#2b4372bd62699fb412c4fe2f95bf9f01bd2018da" } dependencies = [ { name = "execnet" }, @@ -4431,9 +4421,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, upload-time = "2024-07-17T18:47:38.287Z" } wheels = [ @@ -4531,38 +4521,38 @@ wheels = [ [[package]] name = "pyzmq" -version = "27.1.0" +version = "27.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/66/159f38d184f08b5f971b467f87b1ab142ab1320d5200825c824b32b84b66/pyzmq-27.0.2.tar.gz", hash = "sha256:b398dd713b18de89730447347e96a0240225e154db56e35b6bb8447ffdb07798", size = 281440, upload-time = "2025-08-21T04:23:26.334Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328, upload-time = "2025-09-08T23:07:45.946Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803, upload-time = "2025-09-08T23:07:47.551Z" }, - { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836, upload-time = "2025-09-08T23:07:49.436Z" }, - { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038, upload-time = "2025-09-08T23:07:51.234Z" }, - { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531, upload-time = "2025-09-08T23:07:52.795Z" }, - { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786, upload-time = "2025-09-08T23:07:55.047Z" }, - { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220, upload-time = "2025-09-08T23:07:57.172Z" }, - { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155, upload-time = "2025-09-08T23:07:59.05Z" }, - { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428, upload-time = "2025-09-08T23:08:00.663Z" }, - { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497, upload-time = "2025-09-08T23:08:02.15Z" }, - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, - { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265, upload-time = "2025-09-08T23:09:49.376Z" }, - { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208, upload-time = "2025-09-08T23:09:51.073Z" }, - { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747, upload-time = "2025-09-08T23:09:52.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371, upload-time = "2025-09-08T23:09:54.563Z" }, - { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" }, + { url = "https://files.pythonhosted.org/packages/42/73/034429ab0f4316bf433eb6c20c3f49d1dc13b2ed4e4d951b283d300a0f35/pyzmq-27.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:063845960df76599ad4fad69fa4d884b3ba38304272104fdcd7e3af33faeeb1d", size = 1333169, upload-time = "2025-08-21T04:21:12.483Z" }, + { url = "https://files.pythonhosted.org/packages/35/02/c42b3b526eb03a570c889eea85a5602797f800a50ba8b09ddbf7db568b78/pyzmq-27.0.2-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:845a35fb21b88786aeb38af8b271d41ab0967985410f35411a27eebdc578a076", size = 909176, upload-time = "2025-08-21T04:21:13.835Z" }, + { url = "https://files.pythonhosted.org/packages/1b/35/a1c0b988fabbdf2dc5fe94b7c2bcfd61e3533e5109297b8e0daf1d7a8d2d/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:515d20b5c3c86db95503faa989853a8ab692aab1e5336db011cd6d35626c4cb1", size = 668972, upload-time = "2025-08-21T04:21:15.315Z" }, + { url = "https://files.pythonhosted.org/packages/a0/63/908ac865da32ceaeecea72adceadad28ca25b23a2ca5ff018e5bff30116f/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:862aedec0b0684a5050cdb5ec13c2da96d2f8dffda48657ed35e312a4e31553b", size = 856962, upload-time = "2025-08-21T04:21:16.652Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5a/90b3cc20b65cdf9391896fcfc15d8db21182eab810b7ea05a2986912fbe2/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5bcfc51c7a4fce335d3bc974fd1d6a916abbcdd2b25f6e89d37b8def25f57", size = 1657712, upload-time = "2025-08-21T04:21:18.666Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3c/32a5a80f9be4759325b8d7b22ce674bb87e586b4c80c6a9d77598b60d6f0/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:38ff75b2a36e3a032e9fef29a5871e3e1301a37464e09ba364e3c3193f62982a", size = 2035054, upload-time = "2025-08-21T04:21:20.073Z" }, + { url = "https://files.pythonhosted.org/packages/13/61/71084fe2ff2d7dc5713f8740d735336e87544845dae1207a8e2e16d9af90/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a5709abe8d23ca158a9d0a18c037f4193f5b6afeb53be37173a41e9fb885792", size = 1894010, upload-time = "2025-08-21T04:21:21.96Z" }, + { url = "https://files.pythonhosted.org/packages/cb/6b/77169cfb13b696e50112ca496b2ed23c4b7d8860a1ec0ff3e4b9f9926221/pyzmq-27.0.2-cp311-cp311-win32.whl", hash = "sha256:47c5dda2018c35d87be9b83de0890cb92ac0791fd59498847fc4eca6ff56671d", size = 566819, upload-time = "2025-08-21T04:21:23.31Z" }, + { url = "https://files.pythonhosted.org/packages/37/cd/86c4083e0f811f48f11bc0ddf1e7d13ef37adfd2fd4f78f2445f1cc5dec0/pyzmq-27.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:f54ca3e98f8f4d23e989c7d0edcf9da7a514ff261edaf64d1d8653dd5feb0a8b", size = 633264, upload-time = "2025-08-21T04:21:24.761Z" }, + { url = "https://files.pythonhosted.org/packages/a0/69/5b8bb6a19a36a569fac02153a9e083738785892636270f5f68a915956aea/pyzmq-27.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:2ef3067cb5b51b090fb853f423ad7ed63836ec154374282780a62eb866bf5768", size = 559316, upload-time = "2025-08-21T04:21:26.1Z" }, + { url = "https://files.pythonhosted.org/packages/68/69/b3a729e7b03e412bee2b1823ab8d22e20a92593634f664afd04c6c9d9ac0/pyzmq-27.0.2-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:5da05e3c22c95e23bfc4afeee6ff7d4be9ff2233ad6cb171a0e8257cd46b169a", size = 1305910, upload-time = "2025-08-21T04:21:27.609Z" }, + { url = "https://files.pythonhosted.org/packages/15/b7/f6a6a285193d489b223c340b38ee03a673467cb54914da21c3d7849f1b10/pyzmq-27.0.2-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4520577971d01d47e2559bb3175fce1be9103b18621bf0b241abe0a933d040", size = 895507, upload-time = "2025-08-21T04:21:29.005Z" }, + { url = "https://files.pythonhosted.org/packages/17/e6/c4ed2da5ef9182cde1b1f5d0051a986e76339d71720ec1a00be0b49275ad/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d7de7bf73165b90bd25a8668659ccb134dd28449116bf3c7e9bab5cf8a8ec9", size = 652670, upload-time = "2025-08-21T04:21:30.71Z" }, + { url = "https://files.pythonhosted.org/packages/0e/66/d781ab0636570d32c745c4e389b1c6b713115905cca69ab6233508622edd/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340e7cddc32f147c6c00d116a3f284ab07ee63dbd26c52be13b590520434533c", size = 840581, upload-time = "2025-08-21T04:21:32.008Z" }, + { url = "https://files.pythonhosted.org/packages/a6/df/f24790caf565d72544f5c8d8500960b9562c1dc848d6f22f3c7e122e73d4/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba95693f9df8bb4a9826464fb0fe89033936f35fd4a8ff1edff09a473570afa0", size = 1641931, upload-time = "2025-08-21T04:21:33.371Z" }, + { url = "https://files.pythonhosted.org/packages/65/65/77d27b19fc5e845367f9100db90b9fce924f611b14770db480615944c9c9/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:ca42a6ce2d697537da34f77a1960d21476c6a4af3e539eddb2b114c3cf65a78c", size = 2021226, upload-time = "2025-08-21T04:21:35.301Z" }, + { url = "https://files.pythonhosted.org/packages/5b/65/1ed14421ba27a4207fa694772003a311d1142b7f543179e4d1099b7eb746/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e44e665d78a07214b2772ccbd4b9bcc6d848d7895f1b2d7653f047b6318a4f6", size = 1878047, upload-time = "2025-08-21T04:21:36.749Z" }, + { url = "https://files.pythonhosted.org/packages/dd/dc/e578549b89b40dc78a387ec471c2a360766690c0a045cd8d1877d401012d/pyzmq-27.0.2-cp312-abi3-win32.whl", hash = "sha256:272d772d116615397d2be2b1417b3b8c8bc8671f93728c2f2c25002a4530e8f6", size = 558757, upload-time = "2025-08-21T04:21:38.2Z" }, + { url = "https://files.pythonhosted.org/packages/b5/89/06600980aefcc535c758414da969f37a5194ea4cdb73b745223f6af3acfb/pyzmq-27.0.2-cp312-abi3-win_amd64.whl", hash = "sha256:734be4f44efba0aa69bf5f015ed13eb69ff29bf0d17ea1e21588b095a3147b8e", size = 619281, upload-time = "2025-08-21T04:21:39.909Z" }, + { url = "https://files.pythonhosted.org/packages/30/84/df8a5c089552d17c9941d1aea4314b606edf1b1622361dae89aacedc6467/pyzmq-27.0.2-cp312-abi3-win_arm64.whl", hash = "sha256:41f0bd56d9279392810950feb2785a419c2920bbf007fdaaa7f4a07332ae492d", size = 552680, upload-time = "2025-08-21T04:21:41.571Z" }, + { url = "https://files.pythonhosted.org/packages/c7/60/027d0032a1e3b1aabcef0e309b9ff8a4099bdd5a60ab38b36a676ff2bd7b/pyzmq-27.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e297784aea724294fe95e442e39a4376c2f08aa4fae4161c669f047051e31b02", size = 836007, upload-time = "2025-08-21T04:23:00.447Z" }, + { url = "https://files.pythonhosted.org/packages/25/20/2ed1e6168aaea323df9bb2c451309291f53ba3af372ffc16edd4ce15b9e5/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3659a79ded9745bc9c2aef5b444ac8805606e7bc50d2d2eb16dc3ab5483d91f", size = 799932, upload-time = "2025-08-21T04:23:02.052Z" }, + { url = "https://files.pythonhosted.org/packages/fd/25/5c147307de546b502c9373688ce5b25dc22288d23a1ebebe5d587bf77610/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3dba49ff037d02373a9306b58d6c1e0be031438f822044e8767afccfdac4c6b", size = 567459, upload-time = "2025-08-21T04:23:03.593Z" }, + { url = "https://files.pythonhosted.org/packages/71/06/0dc56ffc615c8095cd089c9b98ce5c733e990f09ce4e8eea4aaf1041a532/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de84e1694f9507b29e7b263453a2255a73e3d099d258db0f14539bad258abe41", size = 747088, upload-time = "2025-08-21T04:23:05.334Z" }, + { url = "https://files.pythonhosted.org/packages/06/f6/4a50187e023b8848edd3f0a8e197b1a7fb08d261d8c60aae7cb6c3d71612/pyzmq-27.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f0944d65ba2b872b9fcece08411d6347f15a874c775b4c3baae7f278550da0fb", size = 544639, upload-time = "2025-08-21T04:23:07.279Z" }, ] [[package]] @@ -4665,28 +4655,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.13.1" +version = "0.12.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ab/33/c8e89216845615d14d2d42ba2bee404e7206a8db782f33400754f3799f05/ruff-0.13.1.tar.gz", hash = "sha256:88074c3849087f153d4bb22e92243ad4c1b366d7055f98726bc19aa08dc12d51", size = 5397987, upload-time = "2025-09-18T19:52:44.33Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/55/16ab6a7d88d93001e1ae4c34cbdcfb376652d761799459ff27c1dc20f6fa/ruff-0.12.11.tar.gz", hash = "sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d", size = 5347103, upload-time = "2025-08-28T13:59:08.87Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/41/ca37e340938f45cfb8557a97a5c347e718ef34702546b174e5300dbb1f28/ruff-0.13.1-py3-none-linux_armv6l.whl", hash = "sha256:b2abff595cc3cbfa55e509d89439b5a09a6ee3c252d92020bd2de240836cf45b", size = 12304308, upload-time = "2025-09-18T19:51:56.253Z" }, - { url = "https://files.pythonhosted.org/packages/ff/84/ba378ef4129415066c3e1c80d84e539a0d52feb250685091f874804f28af/ruff-0.13.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4ee9f4249bf7f8bb3984c41bfaf6a658162cdb1b22e3103eabc7dd1dc5579334", size = 12937258, upload-time = "2025-09-18T19:52:00.184Z" }, - { url = "https://files.pythonhosted.org/packages/8d/b6/ec5e4559ae0ad955515c176910d6d7c93edcbc0ed1a3195a41179c58431d/ruff-0.13.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c5da4af5f6418c07d75e6f3224e08147441f5d1eac2e6ce10dcce5e616a3bae", size = 12214554, upload-time = "2025-09-18T19:52:02.753Z" }, - { url = "https://files.pythonhosted.org/packages/70/d6/cb3e3b4f03b9b0c4d4d8f06126d34b3394f6b4d764912fe80a1300696ef6/ruff-0.13.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80524f84a01355a59a93cef98d804e2137639823bcee2931f5028e71134a954e", size = 12448181, upload-time = "2025-09-18T19:52:05.279Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ea/bf60cb46d7ade706a246cd3fb99e4cfe854efa3dfbe530d049c684da24ff/ruff-0.13.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff7f5ce8d7988767dd46a148192a14d0f48d1baea733f055d9064875c7d50389", size = 12104599, upload-time = "2025-09-18T19:52:07.497Z" }, - { url = "https://files.pythonhosted.org/packages/2d/3e/05f72f4c3d3a69e65d55a13e1dd1ade76c106d8546e7e54501d31f1dc54a/ruff-0.13.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c55d84715061f8b05469cdc9a446aa6c7294cd4bd55e86a89e572dba14374f8c", size = 13791178, upload-time = "2025-09-18T19:52:10.189Z" }, - { url = "https://files.pythonhosted.org/packages/81/e7/01b1fc403dd45d6cfe600725270ecc6a8f8a48a55bc6521ad820ed3ceaf8/ruff-0.13.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ac57fed932d90fa1624c946dc67a0a3388d65a7edc7d2d8e4ca7bddaa789b3b0", size = 14814474, upload-time = "2025-09-18T19:52:12.866Z" }, - { url = "https://files.pythonhosted.org/packages/fa/92/d9e183d4ed6185a8df2ce9faa3f22e80e95b5f88d9cc3d86a6d94331da3f/ruff-0.13.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c366a71d5b4f41f86a008694f7a0d75fe409ec298685ff72dc882f882d532e36", size = 14217531, upload-time = "2025-09-18T19:52:15.245Z" }, - { url = "https://files.pythonhosted.org/packages/3b/4a/6ddb1b11d60888be224d721e01bdd2d81faaf1720592858ab8bac3600466/ruff-0.13.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4ea9d1b5ad3e7a83ee8ebb1229c33e5fe771e833d6d3dcfca7b77d95b060d38", size = 13265267, upload-time = "2025-09-18T19:52:17.649Z" }, - { url = "https://files.pythonhosted.org/packages/81/98/3f1d18a8d9ea33ef2ad508f0417fcb182c99b23258ec5e53d15db8289809/ruff-0.13.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f70202996055b555d3d74b626406476cc692f37b13bac8828acff058c9966a", size = 13243120, upload-time = "2025-09-18T19:52:20.332Z" }, - { url = "https://files.pythonhosted.org/packages/8d/86/b6ce62ce9c12765fa6c65078d1938d2490b2b1d9273d0de384952b43c490/ruff-0.13.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f8cff7a105dad631085d9505b491db33848007d6b487c3c1979dd8d9b2963783", size = 13443084, upload-time = "2025-09-18T19:52:23.032Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6e/af7943466a41338d04503fb5a81b2fd07251bd272f546622e5b1599a7976/ruff-0.13.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9761e84255443316a258dd7dfbd9bfb59c756e52237ed42494917b2577697c6a", size = 12295105, upload-time = "2025-09-18T19:52:25.263Z" }, - { url = "https://files.pythonhosted.org/packages/3f/97/0249b9a24f0f3ebd12f007e81c87cec6d311de566885e9309fcbac5b24cc/ruff-0.13.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3d376a88c3102ef228b102211ef4a6d13df330cb0f5ca56fdac04ccec2a99700", size = 12072284, upload-time = "2025-09-18T19:52:27.478Z" }, - { url = "https://files.pythonhosted.org/packages/f6/85/0b64693b2c99d62ae65236ef74508ba39c3febd01466ef7f354885e5050c/ruff-0.13.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cbefd60082b517a82c6ec8836989775ac05f8991715d228b3c1d86ccc7df7dae", size = 12970314, upload-time = "2025-09-18T19:52:30.212Z" }, - { url = "https://files.pythonhosted.org/packages/96/fc/342e9f28179915d28b3747b7654f932ca472afbf7090fc0c4011e802f494/ruff-0.13.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd16b9a5a499fe73f3c2ef09a7885cb1d97058614d601809d37c422ed1525317", size = 13422360, upload-time = "2025-09-18T19:52:32.676Z" }, - { url = "https://files.pythonhosted.org/packages/37/54/6177a0dc10bce6f43e392a2192e6018755473283d0cf43cc7e6afc182aea/ruff-0.13.1-py3-none-win32.whl", hash = "sha256:55e9efa692d7cb18580279f1fbb525146adc401f40735edf0aaeabd93099f9a0", size = 12178448, upload-time = "2025-09-18T19:52:35.545Z" }, - { url = "https://files.pythonhosted.org/packages/64/51/c6a3a33d9938007b8bdc8ca852ecc8d810a407fb513ab08e34af12dc7c24/ruff-0.13.1-py3-none-win_amd64.whl", hash = "sha256:3a3fb595287ee556de947183489f636b9f76a72f0fa9c028bdcabf5bab2cc5e5", size = 13286458, upload-time = "2025-09-18T19:52:38.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/04/afc078a12cf68592345b1e2d6ecdff837d286bac023d7a22c54c7a698c5b/ruff-0.13.1-py3-none-win_arm64.whl", hash = "sha256:c0bae9ffd92d54e03c2bf266f466da0a65e145f298ee5b5846ed435f6a00518a", size = 12437893, upload-time = "2025-09-18T19:52:41.283Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a2/3b3573e474de39a7a475f3fbaf36a25600bfeb238e1a90392799163b64a0/ruff-0.12.11-py3-none-linux_armv6l.whl", hash = "sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065", size = 11979885, upload-time = "2025-08-28T13:58:26.654Z" }, + { url = "https://files.pythonhosted.org/packages/76/e4/235ad6d1785a2012d3ded2350fd9bc5c5af8c6f56820e696b0118dfe7d24/ruff-0.12.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93", size = 12742364, upload-time = "2025-08-28T13:58:30.256Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0d/15b72c5fe6b1e402a543aa9d8960e0a7e19dfb079f5b0b424db48b7febab/ruff-0.12.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd", size = 11920111, upload-time = "2025-08-28T13:58:33.677Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/f66339d7893798ad3e17fa5a1e587d6fd9806f7c1c062b63f8b09dda6702/ruff-0.12.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee", size = 12160060, upload-time = "2025-08-28T13:58:35.74Z" }, + { url = "https://files.pythonhosted.org/packages/03/69/9870368326db26f20c946205fb2d0008988aea552dbaec35fbacbb46efaa/ruff-0.12.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0", size = 11799848, upload-time = "2025-08-28T13:58:38.051Z" }, + { url = "https://files.pythonhosted.org/packages/25/8c/dd2c7f990e9b3a8a55eee09d4e675027d31727ce33cdb29eab32d025bdc9/ruff-0.12.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644", size = 13536288, upload-time = "2025-08-28T13:58:40.046Z" }, + { url = "https://files.pythonhosted.org/packages/7a/30/d5496fa09aba59b5e01ea76775a4c8897b13055884f56f1c35a4194c2297/ruff-0.12.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211", size = 14490633, upload-time = "2025-08-28T13:58:42.285Z" }, + { url = "https://files.pythonhosted.org/packages/9b/2f/81f998180ad53445d403c386549d6946d0748e536d58fce5b5e173511183/ruff-0.12.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793", size = 13888430, upload-time = "2025-08-28T13:58:44.641Z" }, + { url = "https://files.pythonhosted.org/packages/87/71/23a0d1d5892a377478c61dbbcffe82a3476b050f38b5162171942a029ef3/ruff-0.12.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee", size = 12913133, upload-time = "2025-08-28T13:58:47.039Z" }, + { url = "https://files.pythonhosted.org/packages/80/22/3c6cef96627f89b344c933781ed38329bfb87737aa438f15da95907cbfd5/ruff-0.12.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8", size = 13169082, upload-time = "2025-08-28T13:58:49.157Z" }, + { url = "https://files.pythonhosted.org/packages/05/b5/68b3ff96160d8b49e8dd10785ff3186be18fd650d356036a3770386e6c7f/ruff-0.12.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f", size = 13139490, upload-time = "2025-08-28T13:58:51.593Z" }, + { url = "https://files.pythonhosted.org/packages/59/b9/050a3278ecd558f74f7ee016fbdf10591d50119df8d5f5da45a22c6afafc/ruff-0.12.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000", size = 11958928, upload-time = "2025-08-28T13:58:53.943Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bc/93be37347db854806904a43b0493af8d6873472dfb4b4b8cbb27786eb651/ruff-0.12.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2", size = 11764513, upload-time = "2025-08-28T13:58:55.976Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a1/1471751e2015a81fd8e166cd311456c11df74c7e8769d4aabfbc7584c7ac/ruff-0.12.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39", size = 12745154, upload-time = "2025-08-28T13:58:58.16Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/2542b14890d0f4872dd81b7b2a6aed3ac1786fae1ce9b17e11e6df9e31e3/ruff-0.12.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9", size = 13227653, upload-time = "2025-08-28T13:59:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/2fbfc61047dbfd009c58a28369a693a1484ad15441723be1cd7fe69bb679/ruff-0.12.11-py3-none-win32.whl", hash = "sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3", size = 11944270, upload-time = "2025-08-28T13:59:02.347Z" }, + { url = "https://files.pythonhosted.org/packages/08/a5/34276984705bfe069cd383101c45077ee029c3fe3b28225bf67aa35f0647/ruff-0.12.11-py3-none-win_amd64.whl", hash = "sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd", size = 13046600, upload-time = "2025-08-28T13:59:04.751Z" }, + { url = "https://files.pythonhosted.org/packages/84/a8/001d4a7c2b37623a3fd7463208267fb906df40ff31db496157549cfd6e72/ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea", size = 12135290, upload-time = "2025-08-28T13:59:06.933Z" }, ] [[package]] @@ -4700,46 +4690,47 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.38.0" +version = "2.35.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/22/60fd703b34d94d216b2387e048ac82de3e86b63bc28869fb076f8bb0204a/sentry_sdk-2.38.0.tar.gz", hash = "sha256:792d2af45e167e2f8a3347143f525b9b6bac6f058fb2014720b40b84ccbeb985", size = 348116, upload-time = "2025-09-15T15:00:37.846Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/79/0ecb942f3f1ad26c40c27f81ff82392d85c01d26a45e3c72c2b37807e680/sentry_sdk-2.35.2.tar.gz", hash = "sha256:e9e8f3c795044beb59f2c8f4c6b9b0f9779e5e604099882df05eec525e782cc6", size = 343377, upload-time = "2025-09-01T11:00:58.633Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/84/bde4c4bbb269b71bc09316af8eb00da91f67814d40337cc12ef9c8742541/sentry_sdk-2.38.0-py2.py3-none-any.whl", hash = "sha256:2324aea8573a3fa1576df7fb4d65c4eb8d9929c8fa5939647397a07179eef8d0", size = 370346, upload-time = "2025-09-15T15:00:35.821Z" }, + { url = "https://files.pythonhosted.org/packages/c0/91/a43308dc82a0e32d80cd0dfdcfca401ecbd0f431ab45f24e48bb97b7800d/sentry_sdk-2.35.2-py2.py3-none-any.whl", hash = "sha256:38c98e3cbb620dd3dd80a8d6e39c753d453dd41f8a9df581b0584c19a52bc926", size = 363975, upload-time = "2025-09-01T11:00:56.574Z" }, ] [[package]] name = "setproctitle" -version = "1.3.7" +version = "1.3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8d/48/49393a96a2eef1ab418b17475fb92b8fcfad83d099e678751b05472e69de/setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e", size = 27002, upload-time = "2025-09-05T12:51:25.278Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/af/56efe21c53ac81ac87e000b15e60b3d8104224b4313b6eacac3597bd183d/setproctitle-1.3.6.tar.gz", hash = "sha256:c9f32b96c700bb384f33f7cf07954bb609d35dd82752cef57fb2ee0968409169", size = 26889, upload-time = "2025-04-29T13:35:00.184Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/cd/1b7ba5cad635510720ce19d7122154df96a2387d2a74217be552887c93e5/setproctitle-1.3.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a600eeb4145fb0ee6c287cb82a2884bd4ec5bbb076921e287039dcc7b7cc6dd0", size = 18085, upload-time = "2025-09-05T12:49:22.183Z" }, - { url = "https://files.pythonhosted.org/packages/8f/1a/b2da0a620490aae355f9d72072ac13e901a9fec809a6a24fc6493a8f3c35/setproctitle-1.3.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97a090fed480471bb175689859532709e28c085087e344bca45cf318034f70c4", size = 13097, upload-time = "2025-09-05T12:49:23.322Z" }, - { url = "https://files.pythonhosted.org/packages/18/2e/bd03ff02432a181c1787f6fc2a678f53b7dacdd5ded69c318fe1619556e8/setproctitle-1.3.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1607b963e7b53e24ec8a2cb4e0ab3ae591d7c6bf0a160feef0551da63452b37f", size = 32191, upload-time = "2025-09-05T12:49:24.567Z" }, - { url = "https://files.pythonhosted.org/packages/28/78/1e62fc0937a8549f2220445ed2175daacee9b6764c7963b16148119b016d/setproctitle-1.3.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a20fb1a3974e2dab857870cf874b325b8705605cb7e7e8bcbb915bca896f52a9", size = 33203, upload-time = "2025-09-05T12:49:25.871Z" }, - { url = "https://files.pythonhosted.org/packages/a0/3c/65edc65db3fa3df400cf13b05e9d41a3c77517b4839ce873aa6b4043184f/setproctitle-1.3.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8d961bba676e07d77665204f36cffaa260f526e7b32d07ab3df6a2c1dfb44ba", size = 34963, upload-time = "2025-09-05T12:49:27.044Z" }, - { url = "https://files.pythonhosted.org/packages/a1/32/89157e3de997973e306e44152522385f428e16f92f3cf113461489e1e2ee/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:db0fd964fbd3a9f8999b502f65bd2e20883fdb5b1fae3a424e66db9a793ed307", size = 32398, upload-time = "2025-09-05T12:49:28.909Z" }, - { url = "https://files.pythonhosted.org/packages/4a/18/77a765a339ddf046844cb4513353d8e9dcd8183da9cdba6e078713e6b0b2/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:db116850fcf7cca19492030f8d3b4b6e231278e8fe097a043957d22ce1bdf3ee", size = 33657, upload-time = "2025-09-05T12:49:30.323Z" }, - { url = "https://files.pythonhosted.org/packages/6b/63/f0b6205c64d74d2a24a58644a38ec77bdbaa6afc13747e75973bf8904932/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:316664d8b24a5c91ee244460bdaf7a74a707adaa9e14fbe0dc0a53168bb9aba1", size = 31836, upload-time = "2025-09-05T12:49:32.309Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/e1277f9ba302f1a250bbd3eedbbee747a244b3cc682eb58fb9733968f6d8/setproctitle-1.3.7-cp311-cp311-win32.whl", hash = "sha256:b74774ca471c86c09b9d5037c8451fff06bb82cd320d26ae5a01c758088c0d5d", size = 12556, upload-time = "2025-09-05T12:49:33.529Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7b/822a23f17e9003dfdee92cd72758441ca2a3680388da813a371b716fb07f/setproctitle-1.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:acb9097213a8dd3410ed9f0dc147840e45ca9797785272928d4be3f0e69e3be4", size = 13243, upload-time = "2025-09-05T12:49:34.553Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f0/2dc88e842077719d7384d86cc47403e5102810492b33680e7dadcee64cd8/setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e", size = 18049, upload-time = "2025-09-05T12:49:36.241Z" }, - { url = "https://files.pythonhosted.org/packages/f0/b4/50940504466689cda65680c9e9a1e518e5750c10490639fa687489ac7013/setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798", size = 13079, upload-time = "2025-09-05T12:49:38.088Z" }, - { url = "https://files.pythonhosted.org/packages/d0/99/71630546b9395b095f4082be41165d1078204d1696c2d9baade3de3202d0/setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629", size = 32932, upload-time = "2025-09-05T12:49:39.271Z" }, - { url = "https://files.pythonhosted.org/packages/50/22/cee06af4ffcfb0e8aba047bd44f5262e644199ae7527ae2c1f672b86495c/setproctitle-1.3.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6915964a6dda07920a1159321dcd6d94fc7fc526f815ca08a8063aeca3c204f1", size = 33736, upload-time = "2025-09-05T12:49:40.565Z" }, - { url = "https://files.pythonhosted.org/packages/5c/00/a5949a8bb06ef5e7df214fc393bb2fb6aedf0479b17214e57750dfdd0f24/setproctitle-1.3.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cff72899861c765bd4021d1ff1c68d60edc129711a2fdba77f9cb69ef726a8b6", size = 35605, upload-time = "2025-09-05T12:49:42.362Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3a/50caca532a9343828e3bf5778c7a84d6c737a249b1796d50dd680290594d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b7cb05bd446687ff816a3aaaf831047fc4c364feff7ada94a66024f1367b448c", size = 33143, upload-time = "2025-09-05T12:49:43.515Z" }, - { url = "https://files.pythonhosted.org/packages/ca/14/b843a251296ce55e2e17c017d6b9f11ce0d3d070e9265de4ecad948b913d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3a57b9a00de8cae7e2a1f7b9f0c2ac7b69372159e16a7708aa2f38f9e5cc987a", size = 34434, upload-time = "2025-09-05T12:49:45.31Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b7/06145c238c0a6d2c4bc881f8be230bb9f36d2bf51aff7bddcb796d5eed67/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d8828b356114f6b308b04afe398ed93803d7fca4a955dd3abe84430e28d33739", size = 32795, upload-time = "2025-09-05T12:49:46.419Z" }, - { url = "https://files.pythonhosted.org/packages/ef/dc/ef76a81fac9bf27b84ed23df19c1f67391a753eed6e3c2254ebcb5133f56/setproctitle-1.3.7-cp312-cp312-win32.whl", hash = "sha256:b0304f905efc845829ac2bc791ddebb976db2885f6171f4a3de678d7ee3f7c9f", size = 12552, upload-time = "2025-09-05T12:49:47.635Z" }, - { url = "https://files.pythonhosted.org/packages/e2/5b/a9fe517912cd6e28cf43a212b80cb679ff179a91b623138a99796d7d18a0/setproctitle-1.3.7-cp312-cp312-win_amd64.whl", hash = "sha256:9888ceb4faea3116cf02a920ff00bfbc8cc899743e4b4ac914b03625bdc3c300", size = 13247, upload-time = "2025-09-05T12:49:49.16Z" }, - { url = "https://files.pythonhosted.org/packages/c3/5b/5e1c117ac84e3cefcf8d7a7f6b2461795a87e20869da065a5c087149060b/setproctitle-1.3.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b1cac6a4b0252b8811d60b6d8d0f157c0fdfed379ac89c25a914e6346cf355a1", size = 12587, upload-time = "2025-09-05T12:51:21.195Z" }, - { url = "https://files.pythonhosted.org/packages/73/02/b9eadc226195dcfa90eed37afe56b5dd6fa2f0e5220ab8b7867b8862b926/setproctitle-1.3.7-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1704c9e041f2b1dc38f5be4552e141e1432fba3dd52c72eeffd5bc2db04dc65", size = 14286, upload-time = "2025-09-05T12:51:22.61Z" }, - { url = "https://files.pythonhosted.org/packages/28/26/1be1d2a53c2a91ec48fa2ff4a409b395f836798adf194d99de9c059419ea/setproctitle-1.3.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b08b61976ffa548bd5349ce54404bf6b2d51bd74d4f1b241ed1b0f25bce09c3a", size = 13282, upload-time = "2025-09-05T12:51:24.094Z" }, + { url = "https://files.pythonhosted.org/packages/27/3b/8288d0cd969a63500dd62fc2c99ce6980f9909ccef0770ab1f86c361e0bf/setproctitle-1.3.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a1d856b0f4e4a33e31cdab5f50d0a14998f3a2d726a3fd5cb7c4d45a57b28d1b", size = 17412, upload-time = "2025-04-29T13:32:58.135Z" }, + { url = "https://files.pythonhosted.org/packages/39/37/43a5a3e25ca1048dbbf4db0d88d346226f5f1acd131bb8e660f4bfe2799f/setproctitle-1.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50706b9c0eda55f7de18695bfeead5f28b58aa42fd5219b3b1692d554ecbc9ec", size = 11963, upload-time = "2025-04-29T13:32:59.17Z" }, + { url = "https://files.pythonhosted.org/packages/5b/47/f103c40e133154783c91a10ab08ac9fc410ed835aa85bcf7107cb882f505/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af188f3305f0a65c3217c30c6d4c06891e79144076a91e8b454f14256acc7279", size = 31718, upload-time = "2025-04-29T13:33:00.36Z" }, + { url = "https://files.pythonhosted.org/packages/1f/13/7325dd1c008dd6c0ebd370ddb7505977054a87e406f142318e395031a792/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce0ed8b3f64c71c140f0ec244e5fdf8ecf78ddf8d2e591d4a8b6aa1c1214235", size = 33027, upload-time = "2025-04-29T13:33:01.499Z" }, + { url = "https://files.pythonhosted.org/packages/0c/0a/6075bfea05a71379d77af98a9ac61163e8b6e5ef1ae58cd2b05871b2079c/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70100e2087fe05359f249a0b5f393127b3a1819bf34dec3a3e0d4941138650c9", size = 30223, upload-time = "2025-04-29T13:33:03.259Z" }, + { url = "https://files.pythonhosted.org/packages/cc/41/fbf57ec52f4f0776193bd94334a841f0bc9d17e745f89c7790f336420c65/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1065ed36bd03a3fd4186d6c6de5f19846650b015789f72e2dea2d77be99bdca1", size = 31204, upload-time = "2025-04-29T13:33:04.455Z" }, + { url = "https://files.pythonhosted.org/packages/97/b5/f799fb7a00de29fb0ac1dfd015528dea425b9e31a8f1068a0b3df52d317f/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4adf6a0013fe4e0844e3ba7583ec203ca518b9394c6cc0d3354df2bf31d1c034", size = 31181, upload-time = "2025-04-29T13:33:05.697Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b7/81f101b612014ec61723436022c31146178813d6ca6b947f7b9c84e9daf4/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb7452849f6615871eabed6560ffedfe56bc8af31a823b6be4ce1e6ff0ab72c5", size = 30101, upload-time = "2025-04-29T13:33:07.223Z" }, + { url = "https://files.pythonhosted.org/packages/67/23/681232eed7640eab96719daa8647cc99b639e3daff5c287bd270ef179a73/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a094b7ce455ca341b59a0f6ce6be2e11411ba6e2860b9aa3dbb37468f23338f4", size = 32438, upload-time = "2025-04-29T13:33:08.538Z" }, + { url = "https://files.pythonhosted.org/packages/19/f8/4d075a7bdc3609ac71535b849775812455e4c40aedfbf0778a6f123b1774/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad1c2c2baaba62823a7f348f469a967ece0062140ca39e7a48e4bbb1f20d54c4", size = 30625, upload-time = "2025-04-29T13:33:09.707Z" }, + { url = "https://files.pythonhosted.org/packages/5f/73/a2a8259ebee166aee1ca53eead75de0e190b3ddca4f716e5c7470ebb7ef6/setproctitle-1.3.6-cp311-cp311-win32.whl", hash = "sha256:8050c01331135f77ec99d99307bfbc6519ea24d2f92964b06f3222a804a3ff1f", size = 11488, upload-time = "2025-04-29T13:33:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/c9/15/52cf5e1ff0727d53704cfdde2858eaf237ce523b0b04db65faa84ff83e13/setproctitle-1.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:9b73cf0fe28009a04a35bb2522e4c5b5176cc148919431dcb73fdbdfaab15781", size = 12201, upload-time = "2025-04-29T13:33:12.389Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fb/99456fd94d4207c5f6c40746a048a33a52b4239cd7d9c8d4889e2210ec82/setproctitle-1.3.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:af44bb7a1af163806bbb679eb8432fa7b4fb6d83a5d403b541b675dcd3798638", size = 17399, upload-time = "2025-04-29T13:33:13.406Z" }, + { url = "https://files.pythonhosted.org/packages/d5/48/9699191fe6062827683c43bfa9caac33a2c89f8781dd8c7253fa3dba85fd/setproctitle-1.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cca16fd055316a48f0debfcbfb6af7cea715429fc31515ab3fcac05abd527d8", size = 11966, upload-time = "2025-04-29T13:33:14.976Z" }, + { url = "https://files.pythonhosted.org/packages/33/03/b085d192b9ecb9c7ce6ad6ef30ecf4110b7f39430b58a56245569827fcf4/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea002088d5554fd75e619742cefc78b84a212ba21632e59931b3501f0cfc8f67", size = 32017, upload-time = "2025-04-29T13:33:16.163Z" }, + { url = "https://files.pythonhosted.org/packages/ae/68/c53162e645816f97212002111420d1b2f75bf6d02632e37e961dc2cd6d8b/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb465dd5825356c1191a038a86ee1b8166e3562d6e8add95eec04ab484cfb8a2", size = 33419, upload-time = "2025-04-29T13:33:18.239Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0d/119a45d15a816a6cf5ccc61b19729f82620095b27a47e0a6838216a95fae/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2c8e20487b3b73c1fa72c56f5c89430617296cd380373e7af3a538a82d4cd6d", size = 30711, upload-time = "2025-04-29T13:33:19.571Z" }, + { url = "https://files.pythonhosted.org/packages/e3/fb/5e9b5068df9e9f31a722a775a5e8322a29a638eaaa3eac5ea7f0b35e6314/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d6252098e98129a1decb59b46920d4eca17b0395f3d71b0d327d086fefe77d", size = 31742, upload-time = "2025-04-29T13:33:21.172Z" }, + { url = "https://files.pythonhosted.org/packages/35/88/54de1e73e8fce87d587889c7eedb48fc4ee2bbe4e4ca6331690d03024f86/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf355fbf0d4275d86f9f57be705d8e5eaa7f8ddb12b24ced2ea6cbd68fdb14dc", size = 31925, upload-time = "2025-04-29T13:33:22.427Z" }, + { url = "https://files.pythonhosted.org/packages/f3/01/65948d7badd66e63e3db247b923143da142790fa293830fdecf832712c2d/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e288f8a162d663916060beb5e8165a8551312b08efee9cf68302687471a6545d", size = 30981, upload-time = "2025-04-29T13:33:23.739Z" }, + { url = "https://files.pythonhosted.org/packages/22/20/c495e61786f1d38d5dc340b9d9077fee9be3dfc7e89f515afe12e1526dbc/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b2e54f4a2dc6edf0f5ea5b1d0a608d2af3dcb5aa8c8eeab9c8841b23e1b054fe", size = 33209, upload-time = "2025-04-29T13:33:24.915Z" }, + { url = "https://files.pythonhosted.org/packages/98/3f/a457b8550fbd34d5b482fe20b8376b529e76bf1fbf9a474a6d9a641ab4ad/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b6f4abde9a2946f57e8daaf1160b2351bcf64274ef539e6675c1d945dbd75e2a", size = 31587, upload-time = "2025-04-29T13:33:26.123Z" }, + { url = "https://files.pythonhosted.org/packages/44/fe/743517340e5a635e3f1c4310baea20c16c66202f96a6f4cead222ffd6d84/setproctitle-1.3.6-cp312-cp312-win32.whl", hash = "sha256:db608db98ccc21248370d30044a60843b3f0f3d34781ceeea67067c508cd5a28", size = 11487, upload-time = "2025-04-29T13:33:27.403Z" }, + { url = "https://files.pythonhosted.org/packages/60/9a/d88f1c1f0f4efff1bd29d9233583ee341114dda7d9613941453984849674/setproctitle-1.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:082413db8a96b1f021088e8ec23f0a61fec352e649aba20881895815388b66d3", size = 12208, upload-time = "2025-04-29T13:33:28.852Z" }, ] [[package]] @@ -4756,7 +4747,7 @@ name = "shapely" version = "2.1.1" 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/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } wheels = [ @@ -4780,22 +4771,22 @@ wheels = [ [[package]] name = "siphash24" -version = "1.8" +version = "1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/67/a2/e049b6fccf7a94bd1b2f68b3059a7d6a7aea86a808cac80cb9ae71ab6254/siphash24-1.8.tar.gz", hash = "sha256:aa932f0af4a7335caef772fdaf73a433a32580405c41eb17ff24077944b0aa97", size = 19946, upload-time = "2025-09-02T20:42:04.856Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/be/f0a0ffbb00c51c5633b41459b5ce9b017c025a9256b4403e648c18e70850/siphash24-1.7.tar.gz", hash = "sha256:6e90fee5f199ea25b4e7303646b31872a437174fe885a93dbd4cf7784eb48164", size = 19801, upload-time = "2024-10-15T13:41:51.924Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/23/f53f5bd8866c6ea3abe434c9f208e76ea027210d8b75cd0e0dc849661c7a/siphash24-1.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4662ac616bce4d3c9d6003a0d398e56f8be408fc53a166b79fad08d4f34268e", size = 76930, upload-time = "2025-09-02T20:41:00.869Z" }, - { url = "https://files.pythonhosted.org/packages/0b/25/aebf246904424a06e7ffb7a40cfa9ea9e590ea0fac82e182e0f5d1f1d7ef/siphash24-1.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:53d6bed0951a99c6d2891fa6f8acfd5ca80c3e96c60bcee99f6fa01a04773b1c", size = 74315, upload-time = "2025-09-02T20:41:02.38Z" }, - { url = "https://files.pythonhosted.org/packages/59/3f/7010407c3416ef052d46550d54afb2581fb247018fc6500af8c66669eff2/siphash24-1.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d114c03648630e9e07dac2fe95442404e4607adca91640d274ece1a4fa71123e", size = 99756, upload-time = "2025-09-02T20:41:03.902Z" }, - { url = "https://files.pythonhosted.org/packages/d4/9f/09c734833e69badd7e3faed806b4372bd6564ae0946bd250d5239885914f/siphash24-1.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88c1a55ff82b127c5d3b96927a430d8859e6a98846a5b979833ac790682dd91b", size = 104044, upload-time = "2025-09-02T20:41:05.505Z" }, - { url = "https://files.pythonhosted.org/packages/24/30/56a26d9141a34433da221f732599e2b23d2d70a966c249a9f00feb9a2915/siphash24-1.8-cp311-cp311-win32.whl", hash = "sha256:9430255e6a1313470f52c07c4a4643c451a5b2853f6d4008e4dda05cafb6ce7c", size = 62196, upload-time = "2025-09-02T20:41:07.299Z" }, - { url = "https://files.pythonhosted.org/packages/47/b2/11b0ae63fd374652544e1b12f72ba2cc3fe6c93c1483bd8ff6935b0a8a4b/siphash24-1.8-cp311-cp311-win_amd64.whl", hash = "sha256:1e4b37e4ef0b4496169adce2a58b6c3f230b5852dfa5f7ad0b2d664596409e47", size = 77162, upload-time = "2025-09-02T20:41:08.878Z" }, - { url = "https://files.pythonhosted.org/packages/7f/82/ce3545ce8052ac7ca104b183415a27ec3335e5ed51978fdd7b433f3cfe5b/siphash24-1.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:df5ed437c6e6cc96196b38728e57cd30b0427df45223475a90e173f5015ef5ba", size = 78136, upload-time = "2025-09-02T20:41:10.083Z" }, - { url = "https://files.pythonhosted.org/packages/15/88/896c3b91bc9deb78c415448b1db67343917f35971a9e23a5967a9d323b8a/siphash24-1.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f4ef78abdf811325c7089a35504df339c48c0007d4af428a044431d329721e56", size = 74588, upload-time = "2025-09-02T20:41:11.251Z" }, - { url = "https://files.pythonhosted.org/packages/12/fd/8dad3f5601db485ba862e1c1f91a5d77fb563650856a6708e9acb40ee53c/siphash24-1.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:065eff55c4fefb3a29fd26afb2c072abf7f668ffd53b91d41f92a1c485fcbe5c", size = 98655, upload-time = "2025-09-02T20:41:12.45Z" }, - { url = "https://files.pythonhosted.org/packages/e3/cc/e0c352624c1f2faad270aeb5cce6e173977ef66b9b5e918aa6f32af896bf/siphash24-1.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6fa84ebfd47677262aa0bcb0f5a70f796f5fc5704b287ee1b65a3bd4fb7a5d", size = 103217, upload-time = "2025-09-02T20:41:13.746Z" }, - { url = "https://files.pythonhosted.org/packages/5b/f6/0b1675bea4d40affcae642d9c7337702a4138b93c544230280712403e968/siphash24-1.8-cp312-cp312-win32.whl", hash = "sha256:6582f73615552ca055e51e03cb02a28e570a641a7f500222c86c2d811b5037eb", size = 63114, upload-time = "2025-09-02T20:41:14.972Z" }, - { url = "https://files.pythonhosted.org/packages/3d/39/afefef85d72ed8b5cf1aa9283f712e3cd43c9682fabbc809dec54baa8452/siphash24-1.8-cp312-cp312-win_amd64.whl", hash = "sha256:44ea6d794a7cbe184e1e1da2df81c5ebb672ab3867935c3e87c08bb0c2fa4879", size = 76232, upload-time = "2025-09-02T20:41:16.112Z" }, + { url = "https://files.pythonhosted.org/packages/4e/67/4ffd23a848739966e1b314ef99f6410035bccee00be14261313787b8f506/siphash24-1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de75488e93f1cd12c8d5004efd1ebd958c0265205a9d73e8dd8b071900838841", size = 80493, upload-time = "2024-10-15T13:41:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/56/bd/ec198a8c7aef65e967ae84f633bd9950d784c9e527d738c9a3e4bccc34a5/siphash24-1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffca9908450f9f346e97a223185fcd16217d67d84c6f246f3080c4224f41a514", size = 75350, upload-time = "2024-10-15T13:41:16.262Z" }, + { url = "https://files.pythonhosted.org/packages/50/5a/77838c916bd15addfc2e51286db4c442cb12e25eb4f8d296c394c2280240/siphash24-1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8ff44ce166452993fea267ea1b2fd089d8e7f103b13d360da441f12b0df121d", size = 100567, upload-time = "2024-10-15T13:41:17.435Z" }, + { url = "https://files.pythonhosted.org/packages/f0/aa/736a0a2efae9a6f69ac1ee4d28c2274fcad2150349fac752d6c525c4e06e/siphash24-1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4062548dcb1eef13bbe0356d6f8675bfe4571ef38d7103445daa82ba167240d1", size = 105630, upload-time = "2024-10-15T13:41:18.578Z" }, + { url = "https://files.pythonhosted.org/packages/79/52/1afbd70142d3db093d49197e3abe15ca2f1a14678299327ba776944b4771/siphash24-1.7-cp311-cp311-win32.whl", hash = "sha256:7b4ea29376b688fbcc3d25707c15a9dfe7b4ebbc4322878d75bb77e199210a39", size = 67648, upload-time = "2024-10-15T13:41:19.606Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1d/bedcd04c2d1d199c9f6b3e61a6caae0e17257696c9f49594e49856b17a99/siphash24-1.7-cp311-cp311-win_amd64.whl", hash = "sha256:ec06104e6ef1e512ee30f1b8aeae2b83c0f55f12a94042f0df5a87d43a1f4c52", size = 80046, upload-time = "2024-10-15T13:41:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/3e/62/93e552af9535a416f684327f870143ee42fc9e816091672467cdfd62cce6/siphash24-1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:76a64ff0cdd192e4d0a391956d9b121c56ed56e773c5ab7eb7c3e035fd16e8cb", size = 82084, upload-time = "2024-10-15T13:41:21.776Z" }, + { url = "https://files.pythonhosted.org/packages/59/3e/b0791ab53aa9ac191b71a021eab2e75baa7c27d7feb7ec148d7961d148ba/siphash24-1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ca649bc7437d614f758891deade3b187832792a853269219e77f10509f82fe", size = 76233, upload-time = "2024-10-15T13:41:22.787Z" }, + { url = "https://files.pythonhosted.org/packages/29/4c/4c1b809bf302e9b60f3ec09ba115b2a4ac1ff6755735ee8884924fcdb45e/siphash24-1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc37dd0aed23f76bd257fbd2953fd5d954b329d7463c6ff57263a2699c52dde6", size = 98188, upload-time = "2024-10-15T13:41:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/96/bf/e6b49f8ff88130bd224f291ea77d30fdde4df5f6572c519aca5d8fc8a27c/siphash24-1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eea490a200891905856b6ad0f9c56d4ec787876220bcb34c49441b2566b97887", size = 102946, upload-time = "2024-10-15T13:41:25.633Z" }, + { url = "https://files.pythonhosted.org/packages/3d/75/45c831626013950fb2ea715c218c3397e5cf2328a67208bf5d8ff69aa9e6/siphash24-1.7-cp312-cp312-win32.whl", hash = "sha256:69eb8c2c112a738875bb283cd53ef5e86874bc5aed17f3020b38e9174208fb79", size = 68323, upload-time = "2024-10-15T13:41:27.349Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d3/39190c40a68defd19b99c1082dd7455543a52283803bfa111b0e45fae968/siphash24-1.7-cp312-cp312-win_amd64.whl", hash = "sha256:7459569ea4669b6feeaf7d299fc5157cc5c69ca1231dc0decb7a7da2397c782e", size = 81000, upload-time = "2024-10-15T13:41:28.364Z" }, ] [[package]] @@ -4842,9 +4833,9 @@ wheels = [ [[package]] name = "spidev" -version = "3.8" +version = "3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/67/87/039b6eeea781598015b538691bc174cc0bf77df9d4d2d3b8bf9245c0de8c/spidev-3.8.tar.gz", hash = "sha256:2bc02fb8c6312d519ebf1f4331067427c0921d3f77b8bcaf05189a2e8b8382c0", size = 13893, upload-time = "2025-09-15T18:56:20.672Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/99/dd50af8200e224ce9412ad01cdbeeb5b39b2d61acd72138f2b92c4a6d619/spidev-3.7.tar.gz", hash = "sha256:ce628a5ff489f45132679879bff5f455a66abf9751af01843850155b06ae92f0", size = 11616, upload-time = "2025-05-06T14:23:30.783Z" } [[package]] name = "sympy" @@ -4881,14 +4872,14 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.4.20250913" +version = "2.32.4.20250809" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/b0/9355adb86ec84d057fea765e4c49cce592aaf3d5117ce5609a95a7fc3dac/types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3", size = 23027, upload-time = "2025-08-09T03:17:10.664Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6f/ec0012be842b1d888d46884ac5558fd62aeae1f0ec4f7a581433d890d4b5/types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163", size = 20644, upload-time = "2025-08-09T03:17:09.716Z" }, ] [[package]] @@ -4985,7 +4976,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, upload-time = "2024-11-14T00:11:41.584Z" } wheels = [ @@ -5042,42 +5033,42 @@ wheels = [ [[package]] name = "zstandard" -version = "0.25.0" +version = "0.24.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/1b/c20b2ef1d987627765dcd5bf1dadb8ef6564f00a87972635099bb76b7a05/zstandard-0.24.0.tar.gz", hash = "sha256:fe3198b81c00032326342d973e526803f183f97aa9e9a98e3f897ebafe21178f", size = 905681, upload-time = "2025-08-17T18:36:36.352Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, - { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, - { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, - { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, - { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, - { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, - { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, - { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, - { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, - { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, - { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, - { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, - { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, - { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, - { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, - { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, - { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, - { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, - { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, - { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, - { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, - { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, - { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, - { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, - { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, - { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, + { url = "https://files.pythonhosted.org/packages/01/1f/5c72806f76043c0ef9191a2b65281dacdf3b65b0828eb13bb2c987c4fb90/zstandard-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:addfc23e3bd5f4b6787b9ca95b2d09a1a67ad5a3c318daaa783ff90b2d3a366e", size = 795228, upload-time = "2025-08-17T18:21:46.978Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ba/3059bd5cd834666a789251d14417621b5c61233bd46e7d9023ea8bc1043a/zstandard-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b005bcee4be9c3984b355336283afe77b2defa76ed6b89332eced7b6fa68b68", size = 640520, upload-time = "2025-08-17T18:21:48.162Z" }, + { url = "https://files.pythonhosted.org/packages/57/07/f0e632bf783f915c1fdd0bf68614c4764cae9dd46ba32cbae4dd659592c3/zstandard-0.24.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:3f96a9130171e01dbb6c3d4d9925d604e2131a97f540e223b88ba45daf56d6fb", size = 5347682, upload-time = "2025-08-17T18:21:50.266Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4c/63523169fe84773a7462cd090b0989cb7c7a7f2a8b0a5fbf00009ba7d74d/zstandard-0.24.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd0d3d16e63873253bad22b413ec679cf6586e51b5772eb10733899832efec42", size = 5057650, upload-time = "2025-08-17T18:21:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/c6/16/49013f7ef80293f5cebf4c4229535a9f4c9416bbfd238560edc579815dbe/zstandard-0.24.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b7a8c30d9bf4bd5e4dcfe26900bef0fcd9749acde45cdf0b3c89e2052fda9a13", size = 5404893, upload-time = "2025-08-17T18:21:54.54Z" }, + { url = "https://files.pythonhosted.org/packages/4d/38/78e8bcb5fc32a63b055f2b99e0be49b506f2351d0180173674f516cf8a7a/zstandard-0.24.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:52cd7d9fa0a115c9446abb79b06a47171b7d916c35c10e0c3aa6f01d57561382", size = 5452389, upload-time = "2025-08-17T18:21:56.822Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/81671f05619edbacd49bd84ce6899a09fc8299be20c09ae92f6618ccb92d/zstandard-0.24.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0f6fc2ea6e07e20df48752e7700e02e1892c61f9a6bfbacaf2c5b24d5ad504b", size = 5558888, upload-time = "2025-08-17T18:21:58.68Z" }, + { url = "https://files.pythonhosted.org/packages/49/cc/e83feb2d7d22d1f88434defbaeb6e5e91f42a4f607b5d4d2d58912b69d67/zstandard-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e46eb6702691b24ddb3e31e88b4a499e31506991db3d3724a85bd1c5fc3cfe4e", size = 5048038, upload-time = "2025-08-17T18:22:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/08/c3/7a5c57ff49ef8943877f85c23368c104c2aea510abb339a2dc31ad0a27c3/zstandard-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5e3b9310fd7f0d12edc75532cd9a56da6293840c84da90070d692e0bb15f186", size = 5573833, upload-time = "2025-08-17T18:22:02.402Z" }, + { url = "https://files.pythonhosted.org/packages/f9/00/64519983cd92535ba4bdd4ac26ac52db00040a52d6c4efb8d1764abcc343/zstandard-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76cdfe7f920738ea871f035568f82bad3328cbc8d98f1f6988264096b5264efd", size = 4961072, upload-time = "2025-08-17T18:22:04.384Z" }, + { url = "https://files.pythonhosted.org/packages/72/ab/3a08a43067387d22994fc87c3113636aa34ccd2914a4d2d188ce365c5d85/zstandard-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f2fe35ec84908dddf0fbf66b35d7c2878dbe349552dd52e005c755d3493d61c", size = 5268462, upload-time = "2025-08-17T18:22:06.095Z" }, + { url = "https://files.pythonhosted.org/packages/49/cf/2abb3a1ad85aebe18c53e7eca73223f1546ddfa3bf4d2fb83fc5a064c5ca/zstandard-0.24.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:aa705beb74ab116563f4ce784fa94771f230c05d09ab5de9c397793e725bb1db", size = 5443319, upload-time = "2025-08-17T18:22:08.572Z" }, + { url = "https://files.pythonhosted.org/packages/40/42/0dd59fc2f68f1664cda11c3b26abdf987f4e57cb6b6b0f329520cd074552/zstandard-0.24.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:aadf32c389bb7f02b8ec5c243c38302b92c006da565e120dfcb7bf0378f4f848", size = 5822355, upload-time = "2025-08-17T18:22:10.537Z" }, + { url = "https://files.pythonhosted.org/packages/99/c0/ea4e640fd4f7d58d6f87a1e7aca11fb886ac24db277fbbb879336c912f63/zstandard-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e40cd0fc734aa1d4bd0e7ad102fd2a1aefa50ce9ef570005ffc2273c5442ddc3", size = 5365257, upload-time = "2025-08-17T18:22:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/27/a9/92da42a5c4e7e4003271f2e1f0efd1f37cfd565d763ad3604e9597980a1c/zstandard-0.24.0-cp311-cp311-win32.whl", hash = "sha256:cda61c46343809ecda43dc620d1333dd7433a25d0a252f2dcc7667f6331c7b61", size = 435559, upload-time = "2025-08-17T18:22:17.29Z" }, + { url = "https://files.pythonhosted.org/packages/e2/8e/2c8e5c681ae4937c007938f954a060fa7c74f36273b289cabdb5ef0e9a7e/zstandard-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b95fc06489aa9388400d1aab01a83652bc040c9c087bd732eb214909d7fb0dd", size = 505070, upload-time = "2025-08-17T18:22:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/52/10/a2f27a66bec75e236b575c9f7b0d7d37004a03aa2dcde8e2decbe9ed7b4d/zstandard-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad9fd176ff6800a0cf52bcf59c71e5de4fa25bf3ba62b58800e0f84885344d34", size = 461507, upload-time = "2025-08-17T18:22:15.964Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/0bd281d9154bba7fc421a291e263911e1d69d6951aa80955b992a48289f6/zstandard-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2bda8f2790add22773ee7a4e43c90ea05598bffc94c21c40ae0a9000b0133c3", size = 795710, upload-time = "2025-08-17T18:22:19.189Z" }, + { url = "https://files.pythonhosted.org/packages/36/26/b250a2eef515caf492e2d86732e75240cdac9d92b04383722b9753590c36/zstandard-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cc76de75300f65b8eb574d855c12518dc25a075dadb41dd18f6322bda3fe15d5", size = 640336, upload-time = "2025-08-17T18:22:20.466Z" }, + { url = "https://files.pythonhosted.org/packages/79/bf/3ba6b522306d9bf097aac8547556b98a4f753dc807a170becaf30dcd6f01/zstandard-0.24.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d2b3b4bda1a025b10fe0269369475f420177f2cb06e0f9d32c95b4873c9f80b8", size = 5342533, upload-time = "2025-08-17T18:22:22.326Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ec/22bc75bf054e25accdf8e928bc68ab36b4466809729c554ff3a1c1c8bce6/zstandard-0.24.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b84c6c210684286e504022d11ec294d2b7922d66c823e87575d8b23eba7c81f", size = 5062837, upload-time = "2025-08-17T18:22:24.416Z" }, + { url = "https://files.pythonhosted.org/packages/48/cc/33edfc9d286e517fb5b51d9c3210e5bcfce578d02a675f994308ca587ae1/zstandard-0.24.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c59740682a686bf835a1a4d8d0ed1eefe31ac07f1c5a7ed5f2e72cf577692b00", size = 5393855, upload-time = "2025-08-17T18:22:26.786Z" }, + { url = "https://files.pythonhosted.org/packages/73/36/59254e9b29da6215fb3a717812bf87192d89f190f23817d88cb8868c47ac/zstandard-0.24.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6324fde5cf5120fbf6541d5ff3c86011ec056e8d0f915d8e7822926a5377193a", size = 5451058, upload-time = "2025-08-17T18:22:28.885Z" }, + { url = "https://files.pythonhosted.org/packages/9a/c7/31674cb2168b741bbbe71ce37dd397c9c671e73349d88ad3bca9e9fae25b/zstandard-0.24.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51a86bd963de3f36688553926a84e550d45d7f9745bd1947d79472eca27fcc75", size = 5546619, upload-time = "2025-08-17T18:22:31.115Z" }, + { url = "https://files.pythonhosted.org/packages/e6/01/1a9f22239f08c00c156f2266db857545ece66a6fc0303d45c298564bc20b/zstandard-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d82ac87017b734f2fb70ff93818c66f0ad2c3810f61040f077ed38d924e19980", size = 5046676, upload-time = "2025-08-17T18:22:33.077Z" }, + { url = "https://files.pythonhosted.org/packages/a7/91/6c0cf8fa143a4988a0361380ac2ef0d7cb98a374704b389fbc38b5891712/zstandard-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92ea7855d5bcfb386c34557516c73753435fb2d4a014e2c9343b5f5ba148b5d8", size = 5576381, upload-time = "2025-08-17T18:22:35.391Z" }, + { url = "https://files.pythonhosted.org/packages/e2/77/1526080e22e78871e786ccf3c84bf5cec9ed25110a9585507d3c551da3d6/zstandard-0.24.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3adb4b5414febf074800d264ddf69ecade8c658837a83a19e8ab820e924c9933", size = 4953403, upload-time = "2025-08-17T18:22:37.266Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d0/a3a833930bff01eab697eb8abeafb0ab068438771fa066558d96d7dafbf9/zstandard-0.24.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6374feaf347e6b83ec13cc5dcfa70076f06d8f7ecd46cc71d58fac798ff08b76", size = 5267396, upload-time = "2025-08-17T18:22:39.757Z" }, + { url = "https://files.pythonhosted.org/packages/f3/5e/90a0db9a61cd4769c06374297ecfcbbf66654f74cec89392519deba64d76/zstandard-0.24.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:13fc548e214df08d896ee5f29e1f91ee35db14f733fef8eabea8dca6e451d1e2", size = 5433269, upload-time = "2025-08-17T18:22:42.131Z" }, + { url = "https://files.pythonhosted.org/packages/ce/58/fc6a71060dd67c26a9c5566e0d7c99248cbe5abfda6b3b65b8f1a28d59f7/zstandard-0.24.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0a416814608610abf5488889c74e43ffa0343ca6cf43957c6b6ec526212422da", size = 5814203, upload-time = "2025-08-17T18:22:44.017Z" }, + { url = "https://files.pythonhosted.org/packages/5c/6a/89573d4393e3ecbfa425d9a4e391027f58d7810dec5cdb13a26e4cdeef5c/zstandard-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0d66da2649bb0af4471699aeb7a83d6f59ae30236fb9f6b5d20fb618ef6c6777", size = 5359622, upload-time = "2025-08-17T18:22:45.802Z" }, + { url = "https://files.pythonhosted.org/packages/60/ff/2cbab815d6f02a53a9d8d8703bc727d8408a2e508143ca9af6c3cca2054b/zstandard-0.24.0-cp312-cp312-win32.whl", hash = "sha256:ff19efaa33e7f136fe95f9bbcc90ab7fb60648453b03f95d1de3ab6997de0f32", size = 435968, upload-time = "2025-08-17T18:22:49.493Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a3/8f96b8ddb7ad12344218fbd0fd2805702dafd126ae9f8a1fb91eef7b33da/zstandard-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc05f8a875eb651d1cc62e12a4a0e6afa5cd0cc231381adb830d2e9c196ea895", size = 505195, upload-time = "2025-08-17T18:22:47.193Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4a/bfca20679da63bfc236634ef2e4b1b4254203098b0170e3511fee781351f/zstandard-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:b04c94718f7a8ed7cdd01b162b6caa1954b3c9d486f00ecbbd300f149d2b2606", size = 461605, upload-time = "2025-08-17T18:22:48.317Z" }, ] From b6e0d4807a3dad8715d0bd48fe1676e6abfbfcb8 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Sat, 20 Sep 2025 20:10:51 -0700 Subject: [PATCH 035/341] [bot] Update Python packages (#36184) * Update Python packages * not available anymore * also this * also this * maybe? * version * try * Revert "version" This reverts commit 9ac4401b9ca59677b82736faff8baf66861df5f2. * revert * cffi * issue * comment --------- Co-authored-by: Vehicle Researcher Co-authored-by: Maxime Desroches --- panda | 2 +- pyproject.toml | 2 +- selfdrive/modeld/SConscript | 4 +- selfdrive/modeld/dmonitoringmodeld.py | 2 +- selfdrive/modeld/modeld.py | 2 +- tinygrad_repo | 2 +- uv.lock | 771 +++++++++++++------------- 7 files changed, 395 insertions(+), 390 deletions(-) diff --git a/panda b/panda index a2064b86f3..1289337ceb 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit a2064b86f3c9908883033a953503f150cedacbc7 +Subproject commit 1289337ceb6205ad985a5469baa950b319329327 diff --git a/pyproject.toml b/pyproject.toml index 489876f78c..9c8a6148a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,7 +119,7 @@ dev = [ "tabulate", "types-requests", "types-tabulate", - "raylib", + "raylib < 5.5.0.3", # TODO: unpin when they fix https://github.com/electronstudio/raylib-python-cffi/issues/186 ] tools = [ diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index 6802b45e6e..1a6df4bb9b 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -51,8 +51,8 @@ def tg_compile(flags, model_name): for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: flags = { 'larch64': 'DEV=QCOM', - 'Darwin': 'DEV=CPU IMAGE=0', - }.get(arch, 'DEV=LLVM IMAGE=0') + 'Darwin': f'DEV=CPU HOME={os.path.expanduser("~")} IMAGE=0', # tinygrad calls brew which needs a $HOME in the env + }.get(arch, 'DEV=CPU CPU_LLVM=1 IMAGE=0') tg_compile(flags, model_name) # Compile BIG model if USB GPU is available diff --git a/selfdrive/modeld/dmonitoringmodeld.py b/selfdrive/modeld/dmonitoringmodeld.py index 215056e6e6..5aeb035bdc 100755 --- a/selfdrive/modeld/dmonitoringmodeld.py +++ b/selfdrive/modeld/dmonitoringmodeld.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os from openpilot.system.hardware import TICI -os.environ['DEV'] = 'QCOM' if TICI else 'LLVM' +os.environ['DEV'] = 'QCOM' if TICI else 'CPU' from tinygrad.tensor import Tensor from tinygrad.dtype import dtypes import math diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index e08fc30c2e..006eeef6f5 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os from openpilot.system.hardware import TICI -os.environ['DEV'] = 'QCOM' if TICI else 'LLVM' +os.environ['DEV'] = 'QCOM' if TICI else 'CPU' USBGPU = "USBGPU" in os.environ if USBGPU: os.environ['DEV'] = 'AMD' diff --git a/tinygrad_repo b/tinygrad_repo index 965ea59b16..73c8dae60d 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 965ea59b16679793b8f48368ac24c4a0ef587e71 +Subproject commit 73c8dae60d0eaa3a27a252f7967d2dab8378109e diff --git a/uv.lock b/uv.lock index 25d2b626cf..c24d9dbbbe 100644 --- a/uv.lock +++ b/uv.lock @@ -152,21 +152,21 @@ wheels = [ [[package]] name = "azure-core" -version = "1.35.0" +version = "1.35.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/89/f53968635b1b2e53e4aad2dd641488929fef4ca9dfb0b97927fa7697ddf3/azure_core-1.35.0.tar.gz", hash = "sha256:c0be528489485e9ede59b6971eb63c1eaacf83ef53001bfe3904e475e972be5c", size = 339689, upload-time = "2025-07-03T00:55:23.496Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/6b/2653adc0f33adba8f11b1903701e6b1c10d34ce5d8e25dfa13a422f832b0/azure_core-1.35.1.tar.gz", hash = "sha256:435d05d6df0fff2f73fb3c15493bb4721ede14203f1ff1382aa6b6b2bdd7e562", size = 345290, upload-time = "2025-09-11T22:58:04.481Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/78/bf94897361fdd650850f0f2e405b2293e2f12808239046232bdedf554301/azure_core-1.35.0-py3-none-any.whl", hash = "sha256:8db78c72868a58f3de8991eb4d22c4d368fae226dac1002998d6c50437e7dad1", size = 210708, upload-time = "2025-07-03T00:55:25.238Z" }, + { url = "https://files.pythonhosted.org/packages/27/52/805980aa1ba18282077c484dba634ef0ede1e84eec8be9c92b2e162d0ed6/azure_core-1.35.1-py3-none-any.whl", hash = "sha256:12da0c9e08e48e198f9158b56ddbe33b421477e1dc98c2e1c8f9e254d92c468b", size = 211800, upload-time = "2025-09-11T22:58:06.281Z" }, ] [[package]] name = "azure-identity" -version = "1.24.0" +version = "1.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, @@ -175,9 +175,9 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/44/f3ee20bacb220b6b4a2b0a6cf7e742eecb383a5ccf604dd79ec27c286b7e/azure_identity-1.24.0.tar.gz", hash = "sha256:6c3a40b2a70af831e920b89e6421e8dcd4af78a0cb38b9642d86c67643d4930c", size = 271630, upload-time = "2025-08-07T22:27:36.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/9e/4c9682a286c3c89e437579bd9f64f311020e5125c1321fd3a653166b5716/azure_identity-1.25.0.tar.gz", hash = "sha256:4177df34d684cddc026e6cf684e1abb57767aa9d84e7f2129b080ec45eee7733", size = 278507, upload-time = "2025-09-12T01:30:04.418Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/74/17428cb429e8d52f6d0d69ed685f4760a545cb0156594963a9337b53b6c9/azure_identity-1.24.0-py3-none-any.whl", hash = "sha256:9e04997cde0ab02ed66422c74748548e620b7b29361c72ce622acab0267ff7c4", size = 187890, upload-time = "2025-08-07T22:27:38.033Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/81683b6756676a22e037b209695b08008258e603f7e47c56834029c5922a/azure_identity-1.25.0-py3-none-any.whl", hash = "sha256:becaec086bbdf8d1a6aa4fb080c2772a0f824a97d50c29637ec8cc4933f1e82d", size = 190861, upload-time = "2025-09-12T01:30:06.474Z" }, ] [[package]] @@ -197,25 +197,25 @@ wheels = [ [[package]] name = "casadi" -version = "3.7.1" +version = "3.7.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/21/1b4ea75dbddfcdbc7cd70d83147dde72899667097c0c05b288fd7015ef10/casadi-3.7.1.tar.gz", hash = "sha256:12577155cd3cd79ba162381bfed6add1541bc770ba3f1f1334e4eb159d9b39ba", size = 6065586, upload-time = "2025-07-23T21:47:56.483Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/62/1e98662024915ecb09c6894c26a3f497f4afa66570af3f53db4651fc45f1/casadi-3.7.2.tar.gz", hash = "sha256:b4d7bd8acdc4180306903ae1c9eddaf41be2a3ae2fa7154c57174ae64acdc60d", size = 6053600, upload-time = "2025-09-10T10:05:49.521Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/4d/cfe092b2deb65dbf913d38148e30dfe41bbe9f289f892ea7a5d3526532d2/casadi-3.7.1-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:c13fafdff0cdf73392fe2002245befe7496b5ac99b70adb64babff0099068c8a", size = 47100775, upload-time = "2025-07-23T19:56:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/13/41/c4ad627e447a083fbfd2826f1f4684223f0072fdc49f58157ca6a6ce0a77/casadi-3.7.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:74d9c6bad3c7cb347494d52cc3e6faf3fbf3300046fab0e1ddc9439d3fd68486", size = 42282155, upload-time = "2025-07-23T19:59:11.191Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e2/53733933d454657e319cc8074e004ec46660c4797cbffcaf7695a874b0da/casadi-3.7.1-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:6fbc0803e6c7ffb9ba9a41ba38e7c1dadee73835edbacacce3530343f078f3f9", size = 47272638, upload-time = "2025-07-23T20:03:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/e1/72/8002d4fe64325842eb6a7f7ac1e4574c02584e85e5b9d93a08a789095505/casadi-3.7.1-cp311-none-manylinux2014_i686.whl", hash = "sha256:38a16b0c7caff5297df91c7e3a65091824979367e6fc8e6eb0e5ab9f1f832af8", size = 72418203, upload-time = "2025-07-23T20:07:18.681Z" }, - { url = "https://files.pythonhosted.org/packages/96/e5/065287b78c4f6a00a010d7c3f5316df1b95f7c5bda644f2151b54a79a964/casadi-3.7.1-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:2dd75cc1b5ca8009586186e4a63e0cdd83a672bf308edc75ef84f9c896cd971d", size = 75558461, upload-time = "2025-07-23T21:33:20.771Z" }, - { url = "https://files.pythonhosted.org/packages/b0/4c/084d2f3bca33d0b3d509be17c3b678f6bcc03f3409d42e88750cb1e44966/casadi-3.7.1-cp311-none-win_amd64.whl", hash = "sha256:3ab68c6dd621b5f150635bf06aa8e1697cae2fbe0137e9d47270c54d305c334c", size = 50987255, upload-time = "2025-07-23T21:34:20.349Z" }, - { url = "https://files.pythonhosted.org/packages/e1/a3/65942d3e0f7589a72e69dd9dee575963ecf51504e985f132bce8b0d24988/casadi-3.7.1-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:4a29ae3212b0ae6931d439ca3bfa2ac279826e99c2c9a443704e84021933ab76", size = 47102059, upload-time = "2025-07-23T20:36:47.174Z" }, - { url = "https://files.pythonhosted.org/packages/2e/f4/dc371199583f6b154ba0966c4132418737a2d2e019c9f6ad227ebd279684/casadi-3.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:70edf1df3fc83401eb5a2f7053ae8394f4e612e61c2ad6de088e3d76d0ec845e", size = 42282702, upload-time = "2025-07-23T20:39:41.499Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ff/5f8a9378cd7b322d1dcc8b8aa9fbc454ad57152b754c94b50ffdc67254a0/casadi-3.7.1-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:a1717bbd07a6eb98acddf5ea5da7e78a9b85b75dbabb55190cc6b0f2812ef39d", size = 47271636, upload-time = "2025-07-23T20:42:59.961Z" }, - { url = "https://files.pythonhosted.org/packages/e1/11/e1b6a76bf288fc86dc8919cbcd2d3a654e7ead4240d9928deb8ed4ee91da/casadi-3.7.1-cp312-none-manylinux2014_i686.whl", hash = "sha256:57ae29d16af0716ebc15d161bcf0bcd97d35631b10c0e80838290d249cac80d7", size = 72410197, upload-time = "2025-07-23T21:35:35.509Z" }, - { url = "https://files.pythonhosted.org/packages/c1/76/bb43ff625066b178132172d41db0c5c962b4af9db15c88b8a23e65376d18/casadi-3.7.1-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:756f3d07d3414dc54f05ebec883ac3db6e307bfe661719d188340504b6bed420", size = 75552890, upload-time = "2025-07-23T21:37:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/11/0c/8026b0b238e00a2e63c7142505cbcd0931b4db4140d55c83962f9d8e0b02/casadi-3.7.1-cp312-none-win_amd64.whl", hash = "sha256:b228fbcd1f41eb8d5405dbc1e0ecb780daf89808392706bb77fd2b240f8a437e", size = 50984453, upload-time = "2025-07-23T21:38:18.21Z" }, + { url = "https://files.pythonhosted.org/packages/29/01/d5e3058775ec8e24a01eb74d36099493b872536ef9e39f1e49624b977778/casadi-3.7.2-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:f43b0562d05a5e6e81f1885fc4ae426c382e36eebfd8d27f1baff6052178a9b0", size = 47115880, upload-time = "2025-09-10T07:52:24.399Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cf/4af27e010d599a5419129d34fdde41637029a1cca2a40bef0965d6d52228/casadi-3.7.2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:70add3334b437b60a9bc0f864d094350f1a4fcbf9e8bafec870b61aed64674df", size = 42293337, upload-time = "2025-09-10T08:03:32.556Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4c/d1a50cc840103e00effcbaf8e911b6b3fb6ba2c8f4025466f524854968ed/casadi-3.7.2-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:392d3367a4b33cf223013dad8122a0e549da40b1702a5375f82f85b563e5c0cf", size = 47277175, upload-time = "2025-09-10T08:04:08.811Z" }, + { url = "https://files.pythonhosted.org/packages/be/29/6e5714d124e6ddafbccc3ed774ca603081caa1175c7f0e1c52484184dfb3/casadi-3.7.2-cp311-none-manylinux2014_i686.whl", hash = "sha256:2ce09e0ced6df33048dccd582b5cfa2c9ff5193b12858b2584078afc17761905", size = 72438460, upload-time = "2025-09-10T08:05:02.769Z" }, + { url = "https://files.pythonhosted.org/packages/23/32/ac1f3999273aa4aae48516f6f4b7b267e0cc70d8527866989798cb81312f/casadi-3.7.2-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:5086799a46d10ba884b72fd02c21be09dae52cbc189272354a5d424791b55f37", size = 75574474, upload-time = "2025-09-10T08:06:00.709Z" }, + { url = "https://files.pythonhosted.org/packages/68/78/7fd10709504c1757f70db3893870a891fcb9f1ec9f05e8ef2e3f3b9d7e2f/casadi-3.7.2-cp311-none-win_amd64.whl", hash = "sha256:72aa5727417d781ed216f16b5e93c6ddca5db27d83b0015a729e8ad570cdc465", size = 50994144, upload-time = "2025-09-10T08:06:42.384Z" }, + { url = "https://files.pythonhosted.org/packages/65/c8/689d085447b1966f42bdb8aa4fbebef49a09697dbee32ab02a865c17ac1b/casadi-3.7.2-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:309ea41a69c9230390d349b0dd899c6a19504d1904c0756bef463e47fb5c8f9a", size = 47116756, upload-time = "2025-09-10T07:53:00.931Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c0/3c4704394a6fd4dfb2123a4fd71ba64a001f340670a3eba45be7a19ac736/casadi-3.7.2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6033381234db810b2247d16c6352e679a009ec4365d04008fc768866e011ed58", size = 42293718, upload-time = "2025-09-10T08:07:16.415Z" }, + { url = "https://files.pythonhosted.org/packages/f3/24/4cf05469ddf8544da5e92f359f96d716a97e7482999f085a632bc4ef344a/casadi-3.7.2-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:732f2804d0766454bb75596339e4f2da6662ffb669621da0f630ed4af9e83d6a", size = 47276175, upload-time = "2025-09-10T08:08:09.29Z" }, + { url = "https://files.pythonhosted.org/packages/82/08/b5f57fea03128efd5c860673b6ac44776352e6c1af862b8177f4c503fffe/casadi-3.7.2-cp312-none-manylinux2014_i686.whl", hash = "sha256:cf17298ff0c162735bdf9bf72b765c636ae732130604017a3b52e26e35402857", size = 72430454, upload-time = "2025-09-10T08:09:10.781Z" }, + { url = "https://files.pythonhosted.org/packages/24/ab/d7233c915b12c005655437c6c4cf0ae46cbbb2b20d743cb5e4881ad3104a/casadi-3.7.2-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:cde616930fa1440ad66f1850670399423edd37354eed9b12e74b3817b98d1187", size = 75568903, upload-time = "2025-09-10T08:10:07.108Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/5b984124f539656efdf079f3d8f09d73667808ec8d0546e6bce6dc60ade6/casadi-3.7.2-cp312-none-win_amd64.whl", hash = "sha256:81d677d2b020c1307c1eb25eae15686e5de199bb066828c3eaabdfaaaf457ffd", size = 50991347, upload-time = "2025-09-10T08:10:46.629Z" }, ] [[package]] @@ -229,36 +229,38 @@ wheels = [ [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser" }, + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, ] [[package]] @@ -294,14 +296,14 @@ wheels = [ [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, ] [[package]] @@ -415,31 +417,31 @@ wheels = [ [[package]] name = "cython" -version = "3.1.3" +version = "3.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/ab/915337fb39ab4f4539a313df38fc69938df3bf14141b90d61dfd5c2919de/cython-3.1.3.tar.gz", hash = "sha256:10ee785e42328924b78f75a74f66a813cb956b4a9bc91c44816d089d5934c089", size = 3186689, upload-time = "2025-08-13T06:19:13.619Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/f6/d762df1f436a0618455d37f4e4c4872a7cd0dcfc8dec3022ee99e4389c69/cython-3.1.4.tar.gz", hash = "sha256:9aefefe831331e2d66ab31799814eae4d0f8a2d246cbaaaa14d1be29ef777683", size = 3190778, upload-time = "2025-09-16T07:20:33.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/51/54f5d1bed7b7d003d0584996a030542497aeb05b9241782c217ea71061f5/cython-3.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b3b2f6587b42efdece2d174a2aa4234da4524cc6673f3955c2e62b60c6d11fd", size = 3002973, upload-time = "2025-08-13T06:19:50.777Z" }, - { url = "https://files.pythonhosted.org/packages/05/07/b4043fed60070ee21b0d9ff3a877d2ecdc79231e55119ce852b79b690306/cython-3.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:963cf640d049fcca1cefd62d1653f859892d6dc8e4d958eb49a5babc491de6a1", size = 2865389, upload-time = "2025-08-13T06:19:52.675Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e3/67d349b5310a40f281e153e826ca24d9f7ba2997c06800eda781253dccfd/cython-3.1.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2ab05d1bf2d5522ecff35d94ca233b77db2300413597c3ca0b6448377fa4bd7c", size = 3410316, upload-time = "2025-08-13T06:19:55.217Z" }, - { url = "https://files.pythonhosted.org/packages/49/c4/84aae921135174111091547fa726ea5f8bba718cd12b2589a70c2aab8d5c/cython-3.1.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd517e3be052fb49443585b01f02f46080b3408e32c1108a0fdc4cc25b3c9d30", size = 3189568, upload-time = "2025-08-13T06:19:57.503Z" }, - { url = "https://files.pythonhosted.org/packages/e2/85/1bf18883f1a1f656ad83a671e54553caedb1ee2f39a3e792a14aa82557a2/cython-3.1.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a48e2180d74e3c528561d85b48f9a939a429537f9ea8aac7fb16180e7bff47e2", size = 3312649, upload-time = "2025-08-13T06:19:59.894Z" }, - { url = "https://files.pythonhosted.org/packages/68/da/cc1373decc0d14a25f1b434f47de5e27696f3092319aa5e19fcf84157415/cython-3.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7c9daa90b15f59aa2a0d638ac1b36777a7e80122099952a0295c71190ce14bc", size = 3203821, upload-time = "2025-08-13T06:20:02.124Z" }, - { url = "https://files.pythonhosted.org/packages/0a/32/e10582d6f7b02ee63607f388dbbd34e329c54559c85961ddc5c655266519/cython-3.1.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:08ac646ff42781827f23b7a9b61669cdb92055f52724cd8cbe0e1defc56fce2e", size = 3426853, upload-time = "2025-08-13T06:20:04.474Z" }, - { url = "https://files.pythonhosted.org/packages/e0/da/5b0d1b5a4c7622ec812ad9374c9c6b426d69818f13f51e36f4f25ca01c86/cython-3.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bb0e0e7fceaffa22e4dc9600f7f75998eef5cc6ac5a8c0733b482851ba765ef2", size = 3328872, upload-time = "2025-08-13T06:20:06.834Z" }, - { url = "https://files.pythonhosted.org/packages/b0/5f/f102f6c8d27338f0baf094754c67f920828a19612053abc903e66f84506f/cython-3.1.3-cp311-cp311-win32.whl", hash = "sha256:42b1c3ebe36a52e2a8e939c0651e9ca5d30b81d03f800bbf0499deb0503ab565", size = 2480850, upload-time = "2025-08-13T06:20:08.988Z" }, - { url = "https://files.pythonhosted.org/packages/b7/60/415d0f0f7c2e227707baec11c968387d33507d2eb7f26a2950a323c705e5/cython-3.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:34a973844998281951bf54cdd0b6a9946ba03ba94580820738583a00da167d8f", size = 2712560, upload-time = "2025-08-13T06:20:11.877Z" }, - { url = "https://files.pythonhosted.org/packages/79/26/f433fdfd5182b9231819a99acc49a1f856669532306e7a38dce63ba7297e/cython-3.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:849ef3d15d4354e5f74cdb6d3c80d80b03209b3bf1f4ff93315890b19da18944", size = 3014237, upload-time = "2025-08-13T06:20:13.77Z" }, - { url = "https://files.pythonhosted.org/packages/e5/6c/1bebf44f5f177f8c750e608f82c08cd699b8f28cc233e799379bfcf2a2c2/cython-3.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93dd0f62a3f8e93166d8584f8b243180d681ba8fed1f530b55d5f70c348c5797", size = 2844261, upload-time = "2025-08-13T06:20:15.619Z" }, - { url = "https://files.pythonhosted.org/packages/c7/74/983005ce5954f6dc8360b105a561e51a60ea619c53296afac98253d1c9d7/cython-3.1.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ff4a2cb84798faffb3988bd94636c3ad31a95ff44ef017f09121abffc56f84cf", size = 3388846, upload-time = "2025-08-13T06:20:17.679Z" }, - { url = "https://files.pythonhosted.org/packages/68/50/dbe7edefe9b652bc72d49da07785173e89197b9a2d64a2ac45da9795eccf/cython-3.1.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b05319e36f34d5388deea5cc2bcfd65f9ebf76f4ea050829421a69625dbba4a", size = 3167022, upload-time = "2025-08-13T06:20:19.863Z" }, - { url = "https://files.pythonhosted.org/packages/4a/55/b50548b77203e22262002feae23a172f95282b4b8deb5ad104f08e3dc20d/cython-3.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac902a17934a6da46f80755f49413bc4c03a569ae3c834f5d66da7288ba7e6c", size = 3317782, upload-time = "2025-08-13T06:20:21.962Z" }, - { url = "https://files.pythonhosted.org/packages/f6/1b/20a97507d528dc330d67be4fbad6bc767c681d56192bce8f7117a187b2ad/cython-3.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7d7a555a864b1b08576f9e8a67f3789796a065837544f9f683ebf3188012fdbd", size = 3179818, upload-time = "2025-08-13T06:20:24.419Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/7b32a19c4c6bb0e2cc7ae52269b6b23a42f67f730e4abd4f8dca63660f7a/cython-3.1.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b827ce7d97ef8624adcf2bdda594b3dcb6c9b4f124d8f72001d8aea27d69dc1c", size = 3403206, upload-time = "2025-08-13T06:20:26.846Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e1/08cfd4c5e99f79e62d4a7b0009bbd906748633270935c8a55eee51fead76/cython-3.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7851107204085f4f02d0eb6b660ddcad2ce4846e8b7a1eaba724a0bd3cd68a6b", size = 3327837, upload-time = "2025-08-13T06:20:28.946Z" }, - { url = "https://files.pythonhosted.org/packages/23/1b/f3d384be86fa2a6e110b42f42bf97295a513197aaa5532f451c73bf5b48e/cython-3.1.3-cp312-cp312-win32.whl", hash = "sha256:ed20f1b45b2da5a4f8e71a80025bca1cdc96ba35820b0b17658a4a025be920b0", size = 2485990, upload-time = "2025-08-13T06:20:31.517Z" }, - { url = "https://files.pythonhosted.org/packages/de/f0/17cff75e3c141bda73d770f7412632f53e55778d3bfdadfeec4dd3a7d1d6/cython-3.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:dc4ca0f4dec55124cd79ddcfc555be1cbe0092cc99bcf1403621d17b9c6218bb", size = 2704002, upload-time = "2025-08-13T06:20:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/56/c8/46ac27096684f33e27dab749ef43c6b0119c6a0d852971eaefb73256dc4c/cython-3.1.3-py3-none-any.whl", hash = "sha256:d13025b34f72f77bf7f65c1cd628914763e6c285f4deb934314c922b91e6be5a", size = 1225725, upload-time = "2025-08-13T06:19:09.593Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ab/0a568bac7c4c052db4ae27edf01e16f3093cdfef04a2dfd313ef1b3c478a/cython-3.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d1d7013dba5fb0506794d4ef8947ff5ed021370614950a8d8d04e57c8c84499e", size = 3026389, upload-time = "2025-09-16T07:22:02.212Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b7/51f5566e1309215a7fef744975b2fabb56d3fdc5fa1922fd7e306c14f523/cython-3.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eed989f5c139d6550ef2665b783d86fab99372590c97f10a3c26c4523c5fce9e", size = 2955954, upload-time = "2025-09-16T07:22:03.782Z" }, + { url = "https://files.pythonhosted.org/packages/28/fd/ad8314520000fe96292fb8208c640fa862baa3053d2f3453a2acb50cafb8/cython-3.1.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3df3beb8b024dfd73cfddb7f2f7456751cebf6e31655eed3189c209b634bc2f2", size = 3412005, upload-time = "2025-09-16T07:22:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3b/e570f8bcb392e7943fc9a25d1b2d1646ef0148ff017d3681511acf6bbfdc/cython-3.1.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8354703f1168e1aaa01348940f719734c1f11298be333bdb5b94101d49677c0", size = 3191100, upload-time = "2025-09-16T07:22:07.144Z" }, + { url = "https://files.pythonhosted.org/packages/78/81/f1ea09f563ebab732542cb11bf363710e53f3842458159ea2c160788bc8e/cython-3.1.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a928bd7d446247855f54f359057ab4a32c465219c8c1e299906a483393a59a9e", size = 3313786, upload-time = "2025-09-16T07:22:09.15Z" }, + { url = "https://files.pythonhosted.org/packages/ca/17/06575eb6175a926523bada7dac1cd05cc74add96cebbf2e8b492a2494291/cython-3.1.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c233bfff4cc7b9d629eecb7345f9b733437f76dc4441951ec393b0a6e29919fc", size = 3205775, upload-time = "2025-09-16T07:22:10.745Z" }, + { url = "https://files.pythonhosted.org/packages/10/ba/61a8cf56a76ab21ddf6476b70884feff2a2e56b6d9010e1e1b1e06c46f70/cython-3.1.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e9691a2cbc2faf0cd819108bceccf9bfc56c15a06d172eafe74157388c44a601", size = 3428423, upload-time = "2025-09-16T07:22:12.404Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/42cf9239088d6b4b62c1c017c36e0e839f64c8d68674ce4172d0e0168d3b/cython-3.1.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ada319207432ea7c6691c70b5c112d261637d79d21ba086ae3726fedde79bfbf", size = 3330489, upload-time = "2025-09-16T07:22:14.576Z" }, + { url = "https://files.pythonhosted.org/packages/b5/08/36a619d6b1fc671a11744998e5cdd31790589e3cb4542927c97f3f351043/cython-3.1.4-cp311-cp311-win32.whl", hash = "sha256:dae81313c28222bf7be695f85ae1d16625aac35a0973a3af1e001f63379440c5", size = 2482410, upload-time = "2025-09-16T07:22:17.373Z" }, + { url = "https://files.pythonhosted.org/packages/6d/58/7d9ae7944bcd32e6f02d1a8d5d0c3875125227d050e235584127f2c64ffd/cython-3.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:60d2f192059ac34c5c26527f2beac823d34aaa766ef06792a3b7f290c18ac5e2", size = 2713755, upload-time = "2025-09-16T07:22:18.949Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/2939c739cfdc67ab94935a2c4fcc75638afd15e1954552655503a4112e92/cython-3.1.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0d26af46505d0e54fe0f05e7ad089fd0eed8fa04f385f3ab88796f554467bcb9", size = 3062976, upload-time = "2025-09-16T07:22:20.517Z" }, + { url = "https://files.pythonhosted.org/packages/eb/bd/a84de57fd01017bf5dba84a49aeee826db21112282bf8d76ab97567ee15d/cython-3.1.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66ac8bb5068156c92359e3f0eefa138c177d59d1a2e8a89467881fa7d06aba3b", size = 2970701, upload-time = "2025-09-16T07:22:22.644Z" }, + { url = "https://files.pythonhosted.org/packages/71/79/a09004c8e42f5be188c7636b1be479cdb244a6d8837e1878d062e4e20139/cython-3.1.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2e42714faec723d2305607a04bafb49a48a8d8f25dd39368d884c058dbcfbc", size = 3387730, upload-time = "2025-09-16T07:22:24.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bd/979f8c59e247f562642f3eb98a1b453530e1f7954ef071835c08ed2bf6ba/cython-3.1.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0fd655b27997a209a574873304ded9629de588f021154009e8f923475e2c677", size = 3167289, upload-time = "2025-09-16T07:22:26.35Z" }, + { url = "https://files.pythonhosted.org/packages/34/f8/0b98537f0b4e8c01f76d2a6cf75389987538e4d4ac9faf25836fd18c9689/cython-3.1.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9def7c41f4dc339003b1e6875f84edf059989b9c7f5e9a245d3ce12c190742d9", size = 3321099, upload-time = "2025-09-16T07:22:27.957Z" }, + { url = "https://files.pythonhosted.org/packages/f3/39/437968a2e7c7f57eb6e1144f6aca968aa15fbbf169b2d4da5d1ff6c21442/cython-3.1.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:196555584a8716bf7e017e23ca53e9f632ed493f9faa327d0718e7551588f55d", size = 3179897, upload-time = "2025-09-16T07:22:30.014Z" }, + { url = "https://files.pythonhosted.org/packages/2c/04/b3f42915f034d133f1a34e74a2270bc2def02786f9b40dc9028fbb968814/cython-3.1.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7fff0e739e07a20726484b8898b8628a7b87acb960d0fc5486013c6b77b7bb97", size = 3400936, upload-time = "2025-09-16T07:22:31.705Z" }, + { url = "https://files.pythonhosted.org/packages/21/eb/2ad9fa0896ab6cf29875a09a9f4aaea37c28b79b869a013bf9b58e4e652e/cython-3.1.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2754034fa10f95052949cd6b07eb2f61d654c1b9cfa0b17ea53a269389422e8", size = 3332131, upload-time = "2025-09-16T07:22:33.32Z" }, + { url = "https://files.pythonhosted.org/packages/3c/bf/f19283f8405e7e564c3353302a8665ea2c589be63a8e1be1b503043366a9/cython-3.1.4-cp312-cp312-win32.whl", hash = "sha256:2e0808ff3614a1dbfd1adfcbff9b2b8119292f1824b3535b4a173205109509f8", size = 2487672, upload-time = "2025-09-16T07:22:35.227Z" }, + { url = "https://files.pythonhosted.org/packages/30/bf/32150a2e6c7b50b81c5dc9e942d41969400223a9c49d04e2ed955709894c/cython-3.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:f262b32327b6bce340cce5d45bbfe3972cb62543a4930460d8564a489f3aea12", size = 2705348, upload-time = "2025-09-16T07:22:37.922Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/f7351052cf9db771fe4f32fca47fd66e6d9b53d8613b17faf7d130a9d553/cython-3.1.4-py3-none-any.whl", hash = "sha256:d194d95e4fa029a3f6c7d46bdd16d973808c7ea4797586911fdb67cb98b1a2c6", size = 1227541, upload-time = "2025-09-16T07:20:29.595Z" }, ] [[package]] @@ -477,11 +479,11 @@ wheels = [ [[package]] name = "dnspython" -version = "2.7.0" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] [[package]] @@ -525,27 +527,27 @@ wheels = [ [[package]] name = "fonttools" -version = "4.59.2" +version = "4.60.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/a5/fba25f9fbdab96e26dedcaeeba125e5f05a09043bf888e0305326e55685b/fonttools-4.59.2.tar.gz", hash = "sha256:e72c0749b06113f50bcb80332364c6be83a9582d6e3db3fe0b280f996dc2ef22", size = 3540889, upload-time = "2025-08-27T16:40:30.97Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/d9/4eabd956fe123651a1f0efe29d9758b3837b5ae9a98934bdb571117033bb/fonttools-4.60.0.tar.gz", hash = "sha256:8f5927f049091a0ca74d35cce7f78e8f7775c83a6901a8fbe899babcc297146a", size = 3553671, upload-time = "2025-09-17T11:34:01.504Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/53/742fcd750ae0bdc74de4c0ff923111199cc2f90a4ee87aaddad505b6f477/fonttools-4.59.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:511946e8d7ea5c0d6c7a53c4cb3ee48eda9ab9797cd9bf5d95829a398400354f", size = 2774961, upload-time = "2025-08-27T16:38:47.536Z" }, - { url = "https://files.pythonhosted.org/packages/57/2a/976f5f9fa3b4dd911dc58d07358467bec20e813d933bc5d3db1a955dd456/fonttools-4.59.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5e2682cf7be766d84f462ba8828d01e00c8751a8e8e7ce12d7784ccb69a30d", size = 2344690, upload-time = "2025-08-27T16:38:49.723Z" }, - { url = "https://files.pythonhosted.org/packages/c1/8f/b7eefc274fcf370911e292e95565c8253b0b87c82a53919ab3c795a4f50e/fonttools-4.59.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5729e12a982dba3eeae650de48b06f3b9ddb51e9aee2fcaf195b7d09a96250e2", size = 5026910, upload-time = "2025-08-27T16:38:51.904Z" }, - { url = "https://files.pythonhosted.org/packages/69/95/864726eaa8f9d4e053d0c462e64d5830ec7c599cbdf1db9e40f25ca3972e/fonttools-4.59.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c52694eae5d652361d59ecdb5a2246bff7cff13b6367a12da8499e9df56d148d", size = 4971031, upload-time = "2025-08-27T16:38:53.676Z" }, - { url = "https://files.pythonhosted.org/packages/24/4c/b8c4735ebdea20696277c70c79e0de615dbe477834e5a7c2569aa1db4033/fonttools-4.59.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f1bbc23ba1312bd8959896f46f667753b90216852d2a8cfa2d07e0cb234144", size = 5006112, upload-time = "2025-08-27T16:38:55.69Z" }, - { url = "https://files.pythonhosted.org/packages/3b/23/f9ea29c292aa2fc1ea381b2e5621ac436d5e3e0a5dee24ffe5404e58eae8/fonttools-4.59.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1a1bfe5378962825dabe741720885e8b9ae9745ec7ecc4a5ec1f1ce59a6062bf", size = 5117671, upload-time = "2025-08-27T16:38:58.984Z" }, - { url = "https://files.pythonhosted.org/packages/ba/07/cfea304c555bf06e86071ff2a3916bc90f7c07ec85b23bab758d4908c33d/fonttools-4.59.2-cp311-cp311-win32.whl", hash = "sha256:e937790f3c2c18a1cbc7da101550a84319eb48023a715914477d2e7faeaba570", size = 2218157, upload-time = "2025-08-27T16:39:00.75Z" }, - { url = "https://files.pythonhosted.org/packages/d7/de/35d839aa69db737a3f9f3a45000ca24721834d40118652a5775d5eca8ebb/fonttools-4.59.2-cp311-cp311-win_amd64.whl", hash = "sha256:9836394e2f4ce5f9c0a7690ee93bd90aa1adc6b054f1a57b562c5d242c903104", size = 2265846, upload-time = "2025-08-27T16:39:02.453Z" }, - { url = "https://files.pythonhosted.org/packages/ba/3d/1f45db2df51e7bfa55492e8f23f383d372200be3a0ded4bf56a92753dd1f/fonttools-4.59.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82906d002c349cad647a7634b004825a7335f8159d0d035ae89253b4abf6f3ea", size = 2769711, upload-time = "2025-08-27T16:39:04.423Z" }, - { url = "https://files.pythonhosted.org/packages/29/df/cd236ab32a8abfd11558f296e064424258db5edefd1279ffdbcfd4fd8b76/fonttools-4.59.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a10c1bd7644dc58f8862d8ba0cf9fb7fef0af01ea184ba6ce3f50ab7dfe74d5a", size = 2340225, upload-time = "2025-08-27T16:39:06.143Z" }, - { url = "https://files.pythonhosted.org/packages/98/12/b6f9f964fe6d4b4dd4406bcbd3328821c3de1f909ffc3ffa558fe72af48c/fonttools-4.59.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:738f31f23e0339785fd67652a94bc69ea49e413dfdb14dcb8c8ff383d249464e", size = 4912766, upload-time = "2025-08-27T16:39:08.138Z" }, - { url = "https://files.pythonhosted.org/packages/73/78/82bde2f2d2c306ef3909b927363170b83df96171f74e0ccb47ad344563cd/fonttools-4.59.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ec99f9bdfee9cdb4a9172f9e8fd578cce5feb231f598909e0aecf5418da4f25", size = 4955178, upload-time = "2025-08-27T16:39:10.094Z" }, - { url = "https://files.pythonhosted.org/packages/92/77/7de766afe2d31dda8ee46d7e479f35c7d48747e558961489a2d6e3a02bd4/fonttools-4.59.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0476ea74161322e08c7a982f83558a2b81b491509984523a1a540baf8611cc31", size = 4897898, upload-time = "2025-08-27T16:39:12.087Z" }, - { url = "https://files.pythonhosted.org/packages/c5/77/ce0e0b905d62a06415fda9f2b2e109a24a5db54a59502b769e9e297d2242/fonttools-4.59.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:95922a922daa1f77cc72611747c156cfb38030ead72436a2c551d30ecef519b9", size = 5049144, upload-time = "2025-08-27T16:39:13.84Z" }, - { url = "https://files.pythonhosted.org/packages/d9/ea/870d93aefd23fff2e07cbeebdc332527868422a433c64062c09d4d5e7fe6/fonttools-4.59.2-cp312-cp312-win32.whl", hash = "sha256:39ad9612c6a622726a6a130e8ab15794558591f999673f1ee7d2f3d30f6a3e1c", size = 2206473, upload-time = "2025-08-27T16:39:15.854Z" }, - { url = "https://files.pythonhosted.org/packages/61/c4/e44bad000c4a4bb2e9ca11491d266e857df98ab6d7428441b173f0fe2517/fonttools-4.59.2-cp312-cp312-win_amd64.whl", hash = "sha256:980fd7388e461b19a881d35013fec32c713ffea1fc37aef2f77d11f332dfd7da", size = 2254706, upload-time = "2025-08-27T16:39:17.893Z" }, - { url = "https://files.pythonhosted.org/packages/65/a4/d2f7be3c86708912c02571db0b550121caab8cd88a3c0aacb9cfa15ea66e/fonttools-4.59.2-py3-none-any.whl", hash = "sha256:8bd0f759020e87bb5d323e6283914d9bf4ae35a7307dafb2cbd1e379e720ad37", size = 1132315, upload-time = "2025-08-27T16:40:28.984Z" }, + { url = "https://files.pythonhosted.org/packages/da/3d/c57731fbbf204ef1045caca28d5176430161ead73cd9feac3e9d9ef77ee6/fonttools-4.60.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a9106c202d68ff5f9b4a0094c4d7ad2eaa7e9280f06427b09643215e706eb016", size = 2830883, upload-time = "2025-09-17T11:32:10.552Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2d/b7a6ebaed464ce441c755252cc222af11edc651d17c8f26482f429cc2c0e/fonttools-4.60.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9da3a4a3f2485b156bb429b4f8faa972480fc01f553f7c8c80d05d48f17eec89", size = 2356005, upload-time = "2025-09-17T11:32:13.248Z" }, + { url = "https://files.pythonhosted.org/packages/ee/c2/ea834e921324e2051403e125c1fe0bfbdde4951a7c1784e4ae6bdbd286cc/fonttools-4.60.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f84de764c6057b2ffd4feb50ddef481d92e348f0c70f2c849b723118d352bf3", size = 5041201, upload-time = "2025-09-17T11:32:15.373Z" }, + { url = "https://files.pythonhosted.org/packages/93/3c/1c64a338e9aa410d2d0728827d5bb1301463078cb225b94589f27558b427/fonttools-4.60.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800b3fa0d5c12ddff02179d45b035a23989a6c597a71c8035c010fff3b2ef1bb", size = 4977696, upload-time = "2025-09-17T11:32:17.674Z" }, + { url = "https://files.pythonhosted.org/packages/07/cc/c8c411a0d9732bb886b870e052f20658fec9cf91118314f253950d2c1d65/fonttools-4.60.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd68f60b030277f292a582d31c374edfadc60bb33d51ec7b6cd4304531819ba", size = 5020386, upload-time = "2025-09-17T11:32:20.089Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/1d3bc07cf92e7f4fc27f06d4494bf6078dc595b2e01b959157a4fd23df12/fonttools-4.60.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:53328e3ca9e5c8660ef6de07c35f8f312c189b757535e12141be7a8ec942de6e", size = 5131575, upload-time = "2025-09-17T11:32:22.582Z" }, + { url = "https://files.pythonhosted.org/packages/5a/16/08db3917ee19e89d2eb0ee637d37cd4136c849dc421ff63f406b9165c1a1/fonttools-4.60.0-cp311-cp311-win32.whl", hash = "sha256:d493c175ddd0b88a5376e61163e3e6fde3be8b8987db9b092e0a84650709c9e7", size = 2229297, upload-time = "2025-09-17T11:32:24.834Z" }, + { url = "https://files.pythonhosted.org/packages/d2/0b/76764da82c0dfcea144861f568d9e83f4b921e84f2be617b451257bb25a7/fonttools-4.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc2770c9dc49c2d0366e9683f4d03beb46c98042d7ccc8ddbadf3459ecb051a7", size = 2277193, upload-time = "2025-09-17T11:32:27.094Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9b/706ebf84b55ab03439c1f3a94d6915123c0d96099f4238b254fdacffe03a/fonttools-4.60.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8c68928a438d60dfde90e2f09aa7f848ed201176ca6652341744ceec4215859f", size = 2831953, upload-time = "2025-09-17T11:32:29.39Z" }, + { url = "https://files.pythonhosted.org/packages/76/40/782f485be450846e4f3aecff1f10e42af414fc6e19d235c70020f64278e1/fonttools-4.60.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b7133821249097cffabf0624eafd37f5a3358d5ce814febe9db688e3673e724e", size = 2351716, upload-time = "2025-09-17T11:32:31.46Z" }, + { url = "https://files.pythonhosted.org/packages/39/77/ad8d2a6ecc19716eb488c8cf118de10f7802e14bdf61d136d7b52358d6b1/fonttools-4.60.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3638905d3d77ac8791127ce181f7cb434f37e4204d8b2e31b8f1e154320b41f", size = 4922729, upload-time = "2025-09-17T11:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/6b/48/aa543037c6e7788e1bc36b3f858ac70a59d32d0f45915263d0b330a35140/fonttools-4.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7968a26ef010ae89aabbb2f8e9dec1e2709a2541bb8620790451ee8aeb4f6fbf", size = 4967188, upload-time = "2025-09-17T11:32:35.74Z" }, + { url = "https://files.pythonhosted.org/packages/ac/58/e407d2028adc6387947eff8f2940b31f4ed40b9a83c2c7bbc8b9255126e2/fonttools-4.60.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ef01ca7847c356b0fe026b7b92304bc31dc60a4218689ee0acc66652c1a36b2", size = 4910043, upload-time = "2025-09-17T11:32:38.054Z" }, + { url = "https://files.pythonhosted.org/packages/16/ef/e78519b3c296ef757a21b792fc6a785aa2ef9a2efb098083d8ed5f6ee2ba/fonttools-4.60.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3482d7ed7867edfcf785f77c1dffc876c4b2ddac19539c075712ff2a0703cf5", size = 5061980, upload-time = "2025-09-17T11:32:40.457Z" }, + { url = "https://files.pythonhosted.org/packages/00/4c/ad72444d1e3ef704ee90af8d5abf198016a39908d322bf41235562fb01a0/fonttools-4.60.0-cp312-cp312-win32.whl", hash = "sha256:8c937c4fe8addff575a984c9519433391180bf52cf35895524a07b520f376067", size = 2217750, upload-time = "2025-09-17T11:32:42.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/55/3e8ac21963e130242f5a9ea2ebc57f5726d704bf4dcca89088b5b637b2d3/fonttools-4.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:99b06d5d6f29f32e312adaed0367112f5ff2d300ea24363d377ec917daf9e8c5", size = 2266025, upload-time = "2025-09-17T11:32:44.8Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a4/247d3e54eb5ed59e94e09866cfc4f9567e274fbf310ba390711851f63b3b/fonttools-4.60.0-py3-none-any.whl", hash = "sha256:496d26e4d14dcccdd6ada2e937e4d174d3138e3d73f5c9b6ec6eb2fd1dab4f66", size = 1142186, upload-time = "2025-09-17T11:33:59.287Z" }, ] [[package]] @@ -637,10 +639,10 @@ name = "gymnasium" version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { 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'" }, + { name = "cloudpickle" }, + { name = "farama-notifications" }, + { name = "numpy" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fd/17/c2a0e15c2cd5a8e788389b280996db927b923410de676ec5c7b2695e9261/gymnasium-1.2.0.tar.gz", hash = "sha256:344e87561012558f603880baf264ebc97f8a5c997a957b0c9f910281145534b0", size = 821142, upload-time = "2025-06-27T08:21:20.262Z" } wheels = [ @@ -737,11 +739,11 @@ wheels = [ [[package]] name = "kaitaistruct" -version = "0.10" +version = "0.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/04/dd60b9cb65d580ef6cb6eaee975ad1bdd22d46a3f51b07a1e0606710ea88/kaitaistruct-0.10.tar.gz", hash = "sha256:a044dee29173d6afbacf27bcac39daf89b654dd418cfa009ab82d9178a9ae52a", size = 7061, upload-time = "2022-07-09T00:34:06.729Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/b8/ca7319556912f68832daa4b81425314857ec08dfccd8dbc8c0f65c992108/kaitaistruct-0.11.tar.gz", hash = "sha256:053ee764288e78b8e53acf748e9733268acbd579b8d82a427b1805453625d74b", size = 11519, upload-time = "2025-09-08T15:46:25.037Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/bf/88ad23efc08708bda9a2647169828e3553bb2093a473801db61f75356395/kaitaistruct-0.10-py2.py3-none-any.whl", hash = "sha256:a97350919adbf37fda881f75e9365e2fb88d04832b7a4e57106ec70119efb235", size = 7013, upload-time = "2022-07-09T00:34:03.905Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/cf14bf3b1f5ffb13c69cf5f0ea78031247790558ee88984a8bdd22fae60d/kaitaistruct-0.11-py2.py3-none-any.whl", hash = "sha256:5c6ce79177b4e193a577ecd359e26516d1d6d000a0bffd6e1010f2a46a62a561", size = 11372, upload-time = "2025-09-08T15:46:23.635Z" }, ] [[package]] @@ -842,11 +844,11 @@ wheels = [ [[package]] name = "markdown" -version = "3.8.2" +version = "3.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071, upload-time = "2025-06-19T17:12:44.483Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/37/02347f6d6d8279247a5837082ebc26fc0d5aaeaf75aa013fcbb433c777ab/markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a", size = 364585, upload-time = "2025-09-04T20:25:22.885Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827, upload-time = "2025-06-19T17:12:42.994Z" }, + { url = "https://files.pythonhosted.org/packages/70/ae/44c4a6a4cbb496d93c6257954260fe3a6e91b7bed2240e5dad2a717f5111/markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280", size = 107441, upload-time = "2025-09-04T20:25:21.784Z" }, ] [[package]] @@ -927,22 +929,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", 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'" }, + { 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" }, ] 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" }, @@ -1128,28 +1130,28 @@ wheels = [ [[package]] name = "mypy" -version = "1.17.1" +version = "1.18.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, - { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, - { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, - { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, - { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, - { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, - { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, - { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, - { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, + { url = "https://files.pythonhosted.org/packages/88/87/cafd3ae563f88f94eec33f35ff722d043e09832ea8530ef149ec1efbaf08/mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f", size = 12731198, upload-time = "2025-09-19T00:09:44.857Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e0/1e96c3d4266a06d4b0197ace5356d67d937d8358e2ee3ffac71faa843724/mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341", size = 11817879, upload-time = "2025-09-19T00:09:47.131Z" }, + { url = "https://files.pythonhosted.org/packages/72/ef/0c9ba89eb03453e76bdac5a78b08260a848c7bfc5d6603634774d9cd9525/mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d", size = 12427292, upload-time = "2025-09-19T00:10:22.472Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/ec4a061dd599eb8179d5411d99775bec2a20542505988f40fc2fee781068/mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86", size = 13163750, upload-time = "2025-09-19T00:09:51.472Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5f/2cf2ceb3b36372d51568f2208c021870fe7834cf3186b653ac6446511839/mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37", size = 13351827, upload-time = "2025-09-19T00:09:58.311Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/2697b930179e7277529eaaec1513f8de622818696857f689e4a5432e5e27/mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8", size = 9757983, upload-time = "2025-09-19T00:10:09.071Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" }, + { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" }, + { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" }, + { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" }, + { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" }, ] [[package]] @@ -1172,39 +1174,39 @@ wheels = [ [[package]] name = "numpy" -version = "2.3.2" +version = "2.3.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/7d/3fec4199c5ffb892bed55cff901e4f39a58c81df9c44c280499e92cad264/numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48", size = 20489306, upload-time = "2025-07-24T21:32:07.553Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/26/1320083986108998bd487e2931eed2aeedf914b6e8905431487543ec911d/numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9", size = 21259016, upload-time = "2025-07-24T20:24:35.214Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2b/792b341463fa93fc7e55abbdbe87dac316c5b8cb5e94fb7a59fb6fa0cda5/numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168", size = 14451158, upload-time = "2025-07-24T20:24:58.397Z" }, - { url = "https://files.pythonhosted.org/packages/b7/13/e792d7209261afb0c9f4759ffef6135b35c77c6349a151f488f531d13595/numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b", size = 5379817, upload-time = "2025-07-24T20:25:07.746Z" }, - { url = "https://files.pythonhosted.org/packages/49/ce/055274fcba4107c022b2113a213c7287346563f48d62e8d2a5176ad93217/numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8", size = 6913606, upload-time = "2025-07-24T20:25:18.84Z" }, - { url = "https://files.pythonhosted.org/packages/17/f2/e4d72e6bc5ff01e2ab613dc198d560714971900c03674b41947e38606502/numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d", size = 14589652, upload-time = "2025-07-24T20:25:40.356Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b0/fbeee3000a51ebf7222016e2939b5c5ecf8000a19555d04a18f1e02521b8/numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3", size = 16938816, upload-time = "2025-07-24T20:26:05.721Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ec/2f6c45c3484cc159621ea8fc000ac5a86f1575f090cac78ac27193ce82cd/numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f", size = 16370512, upload-time = "2025-07-24T20:26:30.545Z" }, - { url = "https://files.pythonhosted.org/packages/b5/01/dd67cf511850bd7aefd6347aaae0956ed415abea741ae107834aae7d6d4e/numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097", size = 18884947, upload-time = "2025-07-24T20:26:58.24Z" }, - { url = "https://files.pythonhosted.org/packages/a7/17/2cf60fd3e6a61d006778735edf67a222787a8c1a7842aed43ef96d777446/numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220", size = 6599494, upload-time = "2025-07-24T20:27:09.786Z" }, - { url = "https://files.pythonhosted.org/packages/d5/03/0eade211c504bda872a594f045f98ddcc6caef2b7c63610946845e304d3f/numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170", size = 13087889, upload-time = "2025-07-24T20:27:29.558Z" }, - { url = "https://files.pythonhosted.org/packages/13/32/2c7979d39dafb2a25087e12310fc7f3b9d3c7d960df4f4bc97955ae0ce1d/numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89", size = 10459560, upload-time = "2025-07-24T20:27:46.803Z" }, - { url = "https://files.pythonhosted.org/packages/00/6d/745dd1c1c5c284d17725e5c802ca4d45cfc6803519d777f087b71c9f4069/numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b", size = 20956420, upload-time = "2025-07-24T20:28:18.002Z" }, - { url = "https://files.pythonhosted.org/packages/bc/96/e7b533ea5740641dd62b07a790af5d9d8fec36000b8e2d0472bd7574105f/numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f", size = 14184660, upload-time = "2025-07-24T20:28:39.522Z" }, - { url = "https://files.pythonhosted.org/packages/2b/53/102c6122db45a62aa20d1b18c9986f67e6b97e0d6fbc1ae13e3e4c84430c/numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0", size = 5113382, upload-time = "2025-07-24T20:28:48.544Z" }, - { url = "https://files.pythonhosted.org/packages/2b/21/376257efcbf63e624250717e82b4fae93d60178f09eb03ed766dbb48ec9c/numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b", size = 6647258, upload-time = "2025-07-24T20:28:59.104Z" }, - { url = "https://files.pythonhosted.org/packages/91/ba/f4ebf257f08affa464fe6036e13f2bf9d4642a40228781dc1235da81be9f/numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370", size = 14281409, upload-time = "2025-07-24T20:40:30.298Z" }, - { url = "https://files.pythonhosted.org/packages/59/ef/f96536f1df42c668cbacb727a8c6da7afc9c05ece6d558927fb1722693e1/numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73", size = 16641317, upload-time = "2025-07-24T20:40:56.625Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a7/af813a7b4f9a42f498dde8a4c6fcbff8100eed00182cc91dbaf095645f38/numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc", size = 16056262, upload-time = "2025-07-24T20:41:20.797Z" }, - { url = "https://files.pythonhosted.org/packages/8b/5d/41c4ef8404caaa7f05ed1cfb06afe16a25895260eacbd29b4d84dff2920b/numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be", size = 18579342, upload-time = "2025-07-24T20:41:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/9950e44c5a11636f4a3af6e825ec23003475cc9a466edb7a759ed3ea63bd/numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036", size = 6320610, upload-time = "2025-07-24T20:42:01.551Z" }, - { url = "https://files.pythonhosted.org/packages/7c/2f/244643a5ce54a94f0a9a2ab578189c061e4a87c002e037b0829dd77293b6/numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f", size = 12786292, upload-time = "2025-07-24T20:42:20.738Z" }, - { url = "https://files.pythonhosted.org/packages/54/cd/7b5f49d5d78db7badab22d8323c1b6ae458fbf86c4fdfa194ab3cd4eb39b/numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07", size = 10194071, upload-time = "2025-07-24T20:42:36.657Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ea/50ebc91d28b275b23b7128ef25c3d08152bc4068f42742867e07a870a42a/numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15", size = 21130338, upload-time = "2025-07-24T20:57:54.37Z" }, - { url = "https://files.pythonhosted.org/packages/9f/57/cdd5eac00dd5f137277355c318a955c0d8fb8aa486020c22afd305f8b88f/numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec", size = 14375776, upload-time = "2025-07-24T20:58:16.303Z" }, - { url = "https://files.pythonhosted.org/packages/83/85/27280c7f34fcd305c2209c0cdca4d70775e4859a9eaa92f850087f8dea50/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712", size = 5304882, upload-time = "2025-07-24T20:58:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/48/b4/6500b24d278e15dd796f43824e69939d00981d37d9779e32499e823aa0aa/numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c", size = 6818405, upload-time = "2025-07-24T20:58:37.341Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c9/142c1e03f199d202da8e980c2496213509291b6024fd2735ad28ae7065c7/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296", size = 14419651, upload-time = "2025-07-24T20:58:59.048Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/8023e87cbea31a750a6c00ff9427d65ebc5fef104a136bfa69f76266d614/numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981", size = 16760166, upload-time = "2025-07-24T21:28:56.38Z" }, - { url = "https://files.pythonhosted.org/packages/78/e3/6690b3f85a05506733c7e90b577e4762517404ea78bab2ca3a5cb1aeb78d/numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619", size = 12977811, upload-time = "2025-07-24T21:29:18.234Z" }, + { url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d", size = 21259253, upload-time = "2025-09-09T15:56:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569", size = 14450980, upload-time = "2025-09-09T15:56:05.926Z" }, + { url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f", size = 5379709, upload-time = "2025-09-09T15:56:07.95Z" }, + { url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125", size = 6913923, upload-time = "2025-09-09T15:56:09.443Z" }, + { url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48", size = 14589591, upload-time = "2025-09-09T15:56:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6", size = 16938714, upload-time = "2025-09-09T15:56:14.637Z" }, + { url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa", size = 16370592, upload-time = "2025-09-09T15:56:17.285Z" }, + { url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30", size = 18884474, upload-time = "2025-09-09T15:56:20.943Z" }, + { url = "https://files.pythonhosted.org/packages/45/fa/7f43ba10c77575e8be7b0138d107e4f44ca4a1ef322cd16980ea3e8b8222/numpy-2.3.3-cp311-cp311-win32.whl", hash = "sha256:eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57", size = 6599794, upload-time = "2025-09-09T15:56:23.258Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a2/a4f78cb2241fe5664a22a10332f2be886dcdea8784c9f6a01c272da9b426/numpy-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa", size = 13088104, upload-time = "2025-09-09T15:56:25.476Z" }, + { url = "https://files.pythonhosted.org/packages/79/64/e424e975adbd38282ebcd4891661965b78783de893b381cbc4832fb9beb2/numpy-2.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7", size = 10460772, upload-time = "2025-09-09T15:56:27.679Z" }, + { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, + { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, + { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, + { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, + { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, + { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e", size = 21131019, upload-time = "2025-09-09T15:58:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150", size = 14376288, upload-time = "2025-09-09T15:58:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3", size = 5305425, upload-time = "2025-09-09T15:58:48.6Z" }, + { url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0", size = 6819053, upload-time = "2025-09-09T15:58:50.401Z" }, + { url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e", size = 14420354, upload-time = "2025-09-09T15:58:52.704Z" }, + { url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db", size = 16760413, upload-time = "2025-09-09T15:58:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844, upload-time = "2025-09-09T15:58:57.359Z" }, ] [[package]] @@ -1261,7 +1263,6 @@ dependencies = [ { name = "cffi" }, { name = "crcmod" }, { name = "cython" }, - { name = "dearpygui" }, { name = "future-fstrings" }, { name = "inputs" }, { name = "json-rpc" }, @@ -1337,6 +1338,7 @@ testing = [ { name = "ruff" }, ] tools = [ + { name = "dearpygui" }, { name = "metadrive-simulator", marker = "platform_machine != 'aarch64'" }, ] @@ -1353,7 +1355,7 @@ requires-dist = [ { name = "crcmod" }, { name = "cython" }, { name = "dbus-next", marker = "extra == 'dev'" }, - { name = "dearpygui", specifier = ">=2.1.0" }, + { name = "dearpygui", marker = "extra == 'tools'", specifier = ">=2.1.0" }, { name = "dictdiffer", marker = "extra == 'dev'" }, { name = "future-fstrings" }, { name = "hypothesis", marker = "extra == 'testing'", specifier = "==6.47.*" }, @@ -1376,7 +1378,7 @@ requires-dist = [ { name = "psutil" }, { name = "pyaudio" }, { name = "pyautogui", marker = "extra == 'dev'" }, - { name = "pycapnp" }, + { name = "pycapnp", specifier = "==2.1.0" }, { name = "pycryptodome" }, { name = "pygame", marker = "extra == 'dev'" }, { name = "pyjwt" }, @@ -1397,7 +1399,7 @@ requires-dist = [ { name = "pywinctl", marker = "extra == 'dev'" }, { name = "pyzmq" }, { name = "qrcode" }, - { name = "raylib", marker = "extra == 'dev'" }, + { name = "raylib", marker = "extra == 'dev'", specifier = "<5.5.0.3" }, { name = "requests" }, { name = "ruff", marker = "extra == 'testing'" }, { name = "scons" }, @@ -1450,8 +1452,8 @@ name = "panda3d-gltf" version = "0.13" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "panda3d-simplepbr", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d" }, + { name = "panda3d-simplepbr" }, ] sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573, upload-time = "2021-05-21T05:46:32.738Z" } wheels = [ @@ -1463,8 +1465,8 @@ name = "panda3d-simplepbr" version = "0.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload-time = "2025-03-30T16:57:41.087Z" } wheels = [ @@ -1605,31 +1607,32 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.0" +version = "6.32.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, - { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, + { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, + { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, ] [[package]] name = "psutil" -version = "7.0.0" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/31/4723d756b59344b643542936e37a31d1d3204bcdc42a7daa8ee9eb06fb50/psutil-7.1.0.tar.gz", hash = "sha256:655708b3c069387c8b77b072fc429a57d0e214221d01c0a772df7dfedcb3bcd2", size = 497660, upload-time = "2025-09-17T20:14:52.902Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, + { url = "https://files.pythonhosted.org/packages/46/62/ce4051019ee20ce0ed74432dd73a5bb087a6704284a470bb8adff69a0932/psutil-7.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76168cef4397494250e9f4e73eb3752b146de1dd950040b29186d0cce1d5ca13", size = 245242, upload-time = "2025-09-17T20:14:56.126Z" }, + { url = "https://files.pythonhosted.org/packages/38/61/f76959fba841bf5b61123fbf4b650886dc4094c6858008b5bf73d9057216/psutil-7.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:5d007560c8c372efdff9e4579c2846d71de737e4605f611437255e81efcca2c5", size = 246682, upload-time = "2025-09-17T20:14:58.25Z" }, + { url = "https://files.pythonhosted.org/packages/88/7a/37c99d2e77ec30d63398ffa6a660450b8a62517cabe44b3e9bae97696e8d/psutil-7.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22e4454970b32472ce7deaa45d045b34d3648ce478e26a04c7e858a0a6e75ff3", size = 287994, upload-time = "2025-09-17T20:14:59.901Z" }, + { url = "https://files.pythonhosted.org/packages/9d/de/04c8c61232f7244aa0a4b9a9fbd63a89d5aeaf94b2fc9d1d16e2faa5cbb0/psutil-7.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c70e113920d51e89f212dd7be06219a9b88014e63a4cec69b684c327bc474e3", size = 291163, upload-time = "2025-09-17T20:15:01.481Z" }, + { url = "https://files.pythonhosted.org/packages/f4/58/c4f976234bf6d4737bc8c02a81192f045c307b72cf39c9e5c5a2d78927f6/psutil-7.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d4a113425c037300de3ac8b331637293da9be9713855c4fc9d2d97436d7259d", size = 293625, upload-time = "2025-09-17T20:15:04.492Z" }, + { url = "https://files.pythonhosted.org/packages/79/87/157c8e7959ec39ced1b11cc93c730c4fb7f9d408569a6c59dbd92ceb35db/psutil-7.1.0-cp37-abi3-win32.whl", hash = "sha256:09ad740870c8d219ed8daae0ad3b726d3bf9a028a198e7f3080f6a1888b99bca", size = 244812, upload-time = "2025-09-17T20:15:07.462Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e9/b44c4f697276a7a95b8e94d0e320a7bf7f3318521b23de69035540b39838/psutil-7.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:57f5e987c36d3146c0dd2528cd42151cf96cd359b9d67cfff836995cc5df9a3d", size = 247965, upload-time = "2025-09-17T20:15:09.673Z" }, + { url = "https://files.pythonhosted.org/packages/26/65/1070a6e3c036f39142c2820c4b52e9243246fcfc3f96239ac84472ba361e/psutil-7.1.0-cp37-abi3-win_arm64.whl", hash = "sha256:6937cb68133e7c97b6cc9649a570c9a18ba0efebed46d8c5dae4c07fa1b67a07", size = 244971, upload-time = "2025-09-17T20:15:12.262Z" }, ] [[package]] @@ -1662,47 +1665,43 @@ sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de7 [[package]] name = "pycapnp" -version = "2.0.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/fb/54b46b52c1fa2acd9afd81bd05810c61bb1b05c6084c9625b64bc6d41843/pycapnp-2.0.0.tar.gz", hash = "sha256:503ab9b7b16773590ee226f2460408972c6b1c2cb2d819037115b919bef682be", size = 574848, upload-time = "2024-04-12T15:35:44.019Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/86/a57e3c92acd3e1d2fc3dcad683ada191f722e4ac927e1a384b228ec2780a/pycapnp-2.1.0.tar.gz", hash = "sha256:69cc3d861fee1c9b26c73ad2e8a5d51e76ad87e4ff9be33a4fd2fc72f5846aec", size = 689734, upload-time = "2025-09-05T03:50:40.851Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/82/cf311b1a9800b605759a38a0c337a55a639b685427364294e98a0f9b7306/pycapnp-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:829c7eb4e5f23dbcac25466110faf72a691035cf87c5d46e5053da15790e428d", size = 1673673, upload-time = "2024-04-12T15:33:32.211Z" }, - { url = "https://files.pythonhosted.org/packages/ae/55/4c03ca95c568776a1f637db9ffdcf302fb63f46e4d2a4f13edd8cb1a5f90/pycapnp-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab60fbe3e4eaf99ec97918a0a776216c6c149b6d49261383d91c2201adb475d", size = 1513351, upload-time = "2024-04-12T15:33:35.156Z" }, - { url = "https://files.pythonhosted.org/packages/55/98/e4b2dea076f8a2575abc45cd879a91bc9aa975c69ae2ac1cab61d83c5087/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c48a0582078bb74d7326d28571db0b8e6919563365537a5a13e8f5360c12bfc", size = 4910666, upload-time = "2024-04-12T15:33:37.798Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ee/3b5a182588f89074f4002fa6247e3f963bb85bc808acd1ac8deed91f1fa7/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb5ab54aff857e3711d2c0cc934194aaffacdeb3481daa56863daef07d27941", size = 5007434, upload-time = "2024-04-12T15:33:40.362Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e9/515a2ca7fdc84d57c654280d0b71dfd782fd1773d384c0ec0d56dc6fc35b/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9600778036e6fe9dbea68f0c37678c5f4d561d2f2306b3cb741de5e1670ef2ae", size = 5188923, upload-time = "2024-04-12T15:33:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/70/60/5db346e238985a526ba7589ed24f92195dad39e7dec9d85b17a567600b6f/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7278ba0262fab8c398e77d634ae7ba026866d44b52cbfc27262be8d396ecacd1", size = 5048105, upload-time = "2024-04-12T15:33:44.294Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/02b4a87c9ff9793f26d8f3d95312d902d260c094f216d84e19528a506606/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b2458d43c82302980a96518c96df257429204d2cc02bfff0c8cb6ebb371e01", size = 5063172, upload-time = "2024-04-12T15:33:46.954Z" }, - { url = "https://files.pythonhosted.org/packages/10/a1/35a7e14d765f99cfdcdfdcebc69bdf382f27016944470daa7a03c557f681/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd7755cc3fedc2ad8cc7864a0729471ddeff10c184963fe0f3689e295130f1b2", size = 5408718, upload-time = "2024-04-12T15:33:49.587Z" }, - { url = "https://files.pythonhosted.org/packages/5c/59/8bc8a993c38808c6fd90b10becba8de4a54543e8441bd87ce44ef3b7eee4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d47baf6b3db9981625ffc5ff188e089f2ebca8e7e1afb97aa5eb7bebb7bf3650", size = 5596714, upload-time = "2024-04-12T15:33:51.518Z" }, - { url = "https://files.pythonhosted.org/packages/ea/7d/79c481ef77f29e81355e92bb250f0d2a37a76f5fe0ba9433bf6c6c88b6e4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b375be92d93fdb6f7ac127ea9390bcec0fed4e485db137b084f9e7114dde7c83", size = 5709896, upload-time = "2024-04-12T15:33:53.716Z" }, - { url = "https://files.pythonhosted.org/packages/59/8d/f2eceeea1e8cae8b8a70a4752af5b772916f455e2ed388d0887e2b57d080/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:959bfdf1cddb3e5528e2293c4a375382be9a1bf044b073bc2e7eca1eb6b3a9a2", size = 5594823, upload-time = "2024-04-12T15:33:55.573Z" }, - { url = "https://files.pythonhosted.org/packages/2c/86/f8284637b61f83232e5618dd561a66080dd98ce2272d7e3ae89335d4fd97/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d873af167cf5cc7578ce5432eefcb442f866c8f7a6c57d188baf8c5e709fa39d", size = 5572564, upload-time = "2024-04-12T15:33:59.112Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b2/7f99d28a9935d1e37ec6955922c57b2be24fe0b74fe25929643686cc11e5/pycapnp-2.0.0-cp311-cp311-win32.whl", hash = "sha256:40ca8018e0b7686d549b920f087049b92a3e6f06976d9f5a8112603fc560cac4", size = 1040268, upload-time = "2024-04-12T15:34:00.933Z" }, - { url = "https://files.pythonhosted.org/packages/1d/37/89ab98961f18cffeae20d98cfc24afcfa85024bc014ecc48b0c4ac264fe0/pycapnp-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d15cd8e46d541a899c84809095d7d7b3951f43642d1859e7a39bd91910778479", size = 1141758, upload-time = "2024-04-12T15:34:02.607Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d5/0ee84de3ce34a86c373b6cfbea17d5486c2ca942d51efa99a0069723c1e3/pycapnp-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0c111ef96676df25b8afef98f369d45f838ad4434e2898e48199eb43ef704efe", size = 1645816, upload-time = "2024-04-12T15:34:04.428Z" }, - { url = "https://files.pythonhosted.org/packages/35/1e/580572083165ba791fac5ae2d8917facb94db6e3f0500421673f55165dac/pycapnp-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d18906eb1fd1b9f206d93a9591ceedce1d52e7766b66e68f271453f104e9dca", size = 1507892, upload-time = "2024-04-12T15:34:06.933Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ed/46b3cc5d32c525b6a3acb67eb43de2cec692a62775ec1ab66dafe2b7d6ad/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5d1ed365ab1beabb8838068907a7190cc0b6f16de3499d783627e670fcc0eb2", size = 4707960, upload-time = "2024-04-12T15:34:08.771Z" }, - { url = "https://files.pythonhosted.org/packages/8e/51/0a0a4d4e44138adb84959478ea4966196c5ad32022f768b9b64d1590cb3e/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:495b39a7aa2629931bbca27ad743ce591c6c41e8f81792276be424742d9cd1c1", size = 4791780, upload-time = "2024-04-12T15:34:10.863Z" }, - { url = "https://files.pythonhosted.org/packages/28/71/2b59c6ddb253b25b3d01ee6f7b32b0297ac205c7272beeb6d13399054430/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50e814fbde072dcc3d868b5b5cbb9b7a66a70bff9ad03942f3be9baf3ca1cfc6", size = 4961068, upload-time = "2024-04-12T15:34:13.543Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b8/b64fdefa59d6d2802b5ee0a9439396c23a3e5954da6909be81f2722a234c/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:920fdda62d5fdef7a48339104dff0ceb9dcc21b138491f854457ba3a3d4d63ec", size = 4872917, upload-time = "2024-04-12T15:34:15.636Z" }, - { url = "https://files.pythonhosted.org/packages/c8/55/867595f575eb6cb3662e9a0b50a24b4be42df86f2938003e586f6c81606f/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f9142eb4714c152b09dda0b055ea9dd43fd8fd894132e7eb4fa235fb4915edd", size = 4912169, upload-time = "2024-04-12T15:34:17.758Z" }, - { url = "https://files.pythonhosted.org/packages/e4/11/0d36b45e5005ecdf8510081d16c6fb7b22b49651f64af36d138df97980cc/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c98f1d0c4d32109d03e42828ce3c65236afc895033633cbed3ca092993702e7b", size = 5201744, upload-time = "2024-04-12T15:34:20.468Z" }, - { url = "https://files.pythonhosted.org/packages/05/29/ad1357998656b7141939e55bb3aea727c7a5478026feed7f8ee8cf52c935/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4d3250c1875a309d67551843cd8bf3c5e7fccf159b7f5c118a92aee36c0e871c", size = 5351113, upload-time = "2024-04-12T15:34:23.173Z" }, - { url = "https://files.pythonhosted.org/packages/5a/b1/f4c442907948a29b6427dd7436f31d3732bb0d77f5c1dbcad749ba56dac0/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:174e6babe01f5507111c0ed226cd0b5e9325a9d2850751cfe4a57c1670f13881", size = 5472055, upload-time = "2024-04-12T15:34:25.799Z" }, - { url = "https://files.pythonhosted.org/packages/c1/06/a6eceb8b8015f518c0ccae1de5d1a6e18ed73b62b4b111aff54ce5f4f566/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:ed38ece414341285695526792e020f391f29f5064b2126d0367c8bdeef28e3e9", size = 5395743, upload-time = "2024-04-12T15:34:28.134Z" }, - { url = "https://files.pythonhosted.org/packages/e7/b0/63f2b0327853ae08158de61b4dfc7fa43ae5a5c00f1d28f769e7c30cdf55/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a20b7dc55ef83a1fa446bf12680bce25caeb8f81788b623b072c3ec820db50d", size = 5405076, upload-time = "2024-04-12T15:34:30.305Z" }, - { url = "https://files.pythonhosted.org/packages/7d/24/e025dd95f1abf34e373fbab8841ac8e5fa62afe3af4a4b0c61bd01354400/pycapnp-2.0.0-cp312-cp312-win32.whl", hash = "sha256:145eea66233fb5ac9152cd1c06b999ddb691815126f87f5cc37b9cda5d569f8a", size = 1030361, upload-time = "2024-04-12T15:34:32.25Z" }, - { url = "https://files.pythonhosted.org/packages/3f/70/a71108ee9d4db9a027b665a2c383202407207174f1956195d5be45aca705/pycapnp-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:b8b03000769b29b36a8810f458b931f0f706f42027ee6676821eff28092d7734", size = 1135121, upload-time = "2024-04-12T15:34:34.208Z" }, + { url = "https://files.pythonhosted.org/packages/68/7c/934750a0ca77431a22e68e11521dcc6b801bea3ff37331d6a519e5ad142e/pycapnp-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:efacc439ec287d9e8a0ebf01a515404eff795659401e65ba6f1819c7b24f4380", size = 1628855, upload-time = "2025-09-05T03:48:32.317Z" }, + { url = "https://files.pythonhosted.org/packages/2e/a2/fd2c10b3f2e5010c747aa946b27fe09f665d65d5dc2afdd31838a3ef2f5d/pycapnp-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3d8af535a8b44dfd71731a191386c6b821b8a4915806948893d18c79f547a8e", size = 1496942, upload-time = "2025-09-05T03:48:34.905Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8a/42bd0e4c094ef534ac6890d34adae580cbbf5b0497fc0a6340bea833a617/pycapnp-2.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:117d1d5ebfc08cc189aca4f771b34fedc1291a3f9417167bd2d9b2a4e607e640", size = 5200170, upload-time = "2025-09-05T03:48:36.502Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/2e92268383135082191c3dea4a9ad184d20b7fb2dda1477fd6ee520fd88e/pycapnp-2.1.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:d881ccc69e381863a88c7b6c7092a6baecb6dfc8c5558d66bc967c7f778fe7bc", size = 5684026, upload-time = "2025-09-05T03:48:38.063Z" }, + { url = "https://files.pythonhosted.org/packages/46/9c/bca1cbd7711c9c0f0f62ca95a49835369a61c4f6527a6900c8982045bf2f/pycapnp-2.1.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:8a4ea330e38ba83f6f03fbdc1f58642eb53e6f6f66734a426fa592dc988d70e9", size = 5709307, upload-time = "2025-09-05T03:48:40.127Z" }, + { url = "https://files.pythonhosted.org/packages/2d/29/cd14676d992c7b166baa7e022b369c15240d408b202410d105b23b25f737/pycapnp-2.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb2563de4619d359820de9d47b4704e4f7eda193ffc4a56e39cdcd2c8301c336", size = 5386505, upload-time = "2025-09-05T03:48:41.785Z" }, + { url = "https://files.pythonhosted.org/packages/ae/dd/2fc57cebe9be7e4cd3d6aec0b9c8a0db9772c1b17c37cfe4f04c050422cf/pycapnp-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5265d1ae34f9c089fa6983f6c1be404ce480c82b927017290bd703328fa3f5df", size = 6095180, upload-time = "2025-09-05T03:48:43.795Z" }, + { url = "https://files.pythonhosted.org/packages/5a/16/da8c1ada7770a532c859df475533eec5a1b2f5e81a269466a2fe670c5747/pycapnp-2.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b0a56370a868f752375a785bfb7e06b55cbe71605972615d1220c380bc452380", size = 6603414, upload-time = "2025-09-05T03:48:45.457Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/a36eacaf2da6a5ac9c6565600e559edf95115ff990aa3379aee8dd7ba4fe/pycapnp-2.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5d7403c25275cf4badf6f9d0c07b1cb94fcdd599df81aba9b621c32b3dcefae9", size = 6621440, upload-time = "2025-09-05T03:48:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/81/54/9150c03638cf4ecdf1664867382d0049146c658d6de30f189817c502df1a/pycapnp-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dea5d0d250fe4851b42cd380a207d773ebae76a990e542a888a5f1442f4c247e", size = 6354219, upload-time = "2025-09-05T03:48:49.336Z" }, + { url = "https://files.pythonhosted.org/packages/66/3e/e49ba2d74456d53b570c8d30a660c3b29ecfea075d5dd663132ff9049f19/pycapnp-2.1.0-cp311-cp311-win32.whl", hash = "sha256:593844c3cd92937eb5e7cd47ea3a62cde2d49a1fc05dba644f513c68f60f1318", size = 1053647, upload-time = "2025-09-05T03:48:51.108Z" }, + { url = "https://files.pythonhosted.org/packages/53/de/2b61908dc6abf25b17fed6b5a3b42a2226ec09467a3944f1d845ac29ef9b/pycapnp-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac13dd30062bb9985ae9ec4feca106af2b4fdac6468a09c7b74ad754f3921a06", size = 1208911, upload-time = "2025-09-05T03:48:53.219Z" }, + { url = "https://files.pythonhosted.org/packages/74/0e/66b41ba600e5f2523e900b7cc0d2e8496b397a1f2d6a5b7b323ab83418b7/pycapnp-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d2ec561bc948d11f64f43bf9601bede5d6a603d105ae311bd5583c7130624a4", size = 1619223, upload-time = "2025-09-05T03:48:54.64Z" }, + { url = "https://files.pythonhosted.org/packages/40/6e/9bcb30180bd40cb0534124ff7f8ba8746a735018d593f608bf40c97821c0/pycapnp-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132cd97f57f6b6636323ca9b68d389dd90b96e87af38cde31e2b5c5a064f277e", size = 1484321, upload-time = "2025-09-05T03:48:55.85Z" }, + { url = "https://files.pythonhosted.org/packages/14/0a/9ee1c9ecaff499e4fd1df2f0335bc20f666ec6ce5cd80f8ab055007f3c9b/pycapnp-2.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:568e79268ba7c02a71fe558a8aec1ae3c0f0e6aff809ff618a46afe4964957d2", size = 5143502, upload-time = "2025-09-05T03:48:57.733Z" }, + { url = "https://files.pythonhosted.org/packages/4d/50/65837e1416f7a8861ca1e8fe4582a5aef37192d7ef5e2ecfe46880bfdf9c/pycapnp-2.1.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:bcbf6f882d78d368c8e4bb792295392f5c4d71ddffa13a48da27e7bd47b99e37", size = 5508134, upload-time = "2025-09-05T03:48:59.383Z" }, + { url = "https://files.pythonhosted.org/packages/a1/59/46df6db800e77dbc3cc940723fb3fd7bc837327c858edf464a0f904bf547/pycapnp-2.1.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:dc25b96e393410dde25c61c1df3ce644700ef94826c829426d58c2c6b3e2d2f5", size = 5631794, upload-time = "2025-09-05T03:49:03.511Z" }, + { url = "https://files.pythonhosted.org/packages/63/9d/18e978500d5f6bd8d152f4d6919e3cfb83ead8a71c14613bbb54322df8b9/pycapnp-2.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:48938e0436ab1be615fc0a41434119a2065490a6212b9a5e56949e89b0588b76", size = 5369378, upload-time = "2025-09-05T03:49:05.539Z" }, + { url = "https://files.pythonhosted.org/packages/96/dc/726f1917e9996dc29f9fd1cf30674a14546cdbdfa0777e1982b6bd1ad628/pycapnp-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0c20de0f6e0b3fa9fa1df3864cf46051db3511b63bc29514d1092af65f2b82a0", size = 5999140, upload-time = "2025-09-05T03:49:07.341Z" }, + { url = "https://files.pythonhosted.org/packages/fd/3a/3bbc4c5776fc32fbf8a59df5c7c5810efd292b933cd6545eb4b16d896268/pycapnp-2.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:18caca6527862475167c10ea0809531130585aa8a86cc76cd1629eb87ee30637", size = 6454308, upload-time = "2025-09-05T03:49:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/bf/dd/17e2d7808424f10ffddc47329b980488ed83ec716c504791787e593a7a93/pycapnp-2.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9dcc11237697007b66e3bfc500d2ad892bd79672c9b50d61fbf728c6aaf936de", size = 6544212, upload-time = "2025-09-05T03:49:10.675Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/68090013128d7853f34c43828dd4dc80a7c8516fd1b56057b134e1e4c2c0/pycapnp-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151edf78155b6416e7cb31e2e333d302d742ba52bb37d4dbdf71e75cc999d46", size = 6295279, upload-time = "2025-09-05T03:49:12.712Z" }, + { url = "https://files.pythonhosted.org/packages/5b/52/7d85212b4fcf127588888f71d3dbf5558ee7dc302eba760b12b1b325f9a3/pycapnp-2.1.0-cp312-cp312-win32.whl", hash = "sha256:c09b28419321dafafc644d60c57ff8ccaf3c3e686801b6060c612a7a3c580944", size = 1038995, upload-time = "2025-09-05T03:49:14.165Z" }, + { url = "https://files.pythonhosted.org/packages/f2/12/25d283ebf5c28717364647672e7494dc46196ca7a662f5420e4866f45687/pycapnp-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:560cb69cc02b0347e85b0629e4c2f0a316240900aa905392f9df6bab0a359989", size = 1176620, upload-time = "2025-09-05T03:49:15.545Z" }, ] [[package]] name = "pycparser" -version = "2.22" +version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] [[package]] @@ -1828,9 +1827,12 @@ wheels = [ [[package]] name = "pymsgbox" -version = "1.0.9" +version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/ff/4c6f31a4f08979f12a663f2aeb6c8b765d3bd592e66eaaac445f547bb875/PyMsgBox-1.0.9.tar.gz", hash = "sha256:2194227de8bff7a3d6da541848705a155dcbb2a06ee120d9f280a1d7f51263ff", size = 18829, upload-time = "2020-10-11T01:51:43.227Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/6a/e80da7594ee598a776972d09e2813df2b06b3bc29218f440631dfa7c78a8/pymsgbox-2.0.1.tar.gz", hash = "sha256:98d055c49a511dcc10fa08c3043e7102d468f5e4b3a83c6d3c61df722c7d798d", size = 20768, upload-time = "2025-09-09T00:38:56.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/3e/08c8cac81b2b2f7502746e6b9c8e5b0ec6432cd882c605560fc409aaf087/pymsgbox-2.0.1-py3-none-any.whl", hash = "sha256:5de8ec19bca2ca7e6c09d39c817c83f17c75cee80275235f43a9931db699f73b", size = 9994, upload-time = "2025-09-09T00:38:55.672Z" }, +] [[package]] name = "pyobjc" @@ -4201,9 +4203,9 @@ name = "pyopencl" version = "2025.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { 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'" }, + { name = "numpy" }, + { name = "platformdirs" }, + { name = "pytools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510, upload-time = "2025-01-22T00:16:58.421Z" } wheels = [ @@ -4233,18 +4235,21 @@ wheels = [ [[package]] name = "pyparsing" -version = "3.2.3" +version = "3.2.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/c9/b4594e6a81371dfa9eb7a2c110ad682acf985d96115ae8b25a1d63b4bf3b/pyparsing-3.2.4.tar.gz", hash = "sha256:fff89494f45559d0f2ce46613b419f632bbb6afbdaed49696d322bcf98a58e99", size = 1098809, upload-time = "2025-09-13T05:47:19.732Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" }, + { url = "https://files.pythonhosted.org/packages/53/b8/fbab973592e23ae313042d450fc26fa24282ebffba21ba373786e1ce63b4/pyparsing-3.2.4-py3-none-any.whl", hash = "sha256:91d0fcde680d42cd031daf3a6ba20da3107e08a75de50da58360e7d94ab24d36", size = 113869, upload-time = "2025-09-13T05:47:17.863Z" }, ] [[package]] name = "pyperclip" -version = "1.9.0" +version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961, upload-time = "2024-06-18T20:38:48.401Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/99/25f4898cf420efb6f45f519de018f4faea5391114a8618b16736ef3029f1/pyperclip-1.10.0.tar.gz", hash = "sha256:180c8346b1186921c75dfd14d9048a6b5d46bfc499778811952c6dd6eb1ca6be", size = 12193, upload-time = "2025-09-18T00:54:00.384Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/bc/22540e73c5f5ae18f02924cd3954a6c9a4aa6b713c841a94c98335d333a1/pyperclip-1.10.0-py3-none-any.whl", hash = "sha256:596fbe55dc59263bff26e61d2afbe10223e2fccb5210c9c96a28d6887cfcc7ec", size = 11062, upload-time = "2025-09-18T00:53:59.252Z" }, +] [[package]] name = "pyprof2calltree" @@ -4278,7 +4283,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.1" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -4287,21 +4292,22 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] name = "pytest-asyncio" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] @@ -4318,26 +4324,26 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.14.1" +version = "3.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, ] [[package]] name = "pytest-randomly" -version = "3.16.0" +version = "4.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367, upload-time = "2024-10-25T15:45:34.274Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/1d/258a4bf1109258c00c35043f40433be5c16647387b6e7cd5582d638c116b/pytest_randomly-4.0.1.tar.gz", hash = "sha256:174e57bb12ac2c26f3578188490bd333f0e80620c3f47340158a86eca0593cd8", size = 14130, upload-time = "2025-09-12T15:23:00.085Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396, upload-time = "2024-10-25T15:45:32.78Z" }, + { url = "https://files.pythonhosted.org/packages/33/3e/a4a9227807b56869790aad3e24472a554b585974fe7e551ea350f50897ae/pytest_randomly-4.0.1-py3-none-any.whl", hash = "sha256:e0dfad2fd4f35e07beff1e47c17fbafcf98f9bf4531fd369d9260e2f858bfcb7", size = 8304, upload-time = "2025-09-12T15:22:58.946Z" }, ] [[package]] @@ -4379,7 +4385,7 @@ wheels = [ [[package]] name = "pytest-xdist" -version = "3.7.1.dev24+g2b4372b" +version = "3.7.1.dev24+g2b4372bd6" source = { git = "https://github.com/sshane/pytest-xdist?rev=2b4372bd62699fb412c4fe2f95bf9f01bd2018da#2b4372bd62699fb412c4fe2f95bf9f01bd2018da" } dependencies = [ { name = "execnet" }, @@ -4421,9 +4427,9 @@ name = "pytools" version = "2024.1.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { 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'" }, + { name = "platformdirs" }, + { name = "siphash24" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741, upload-time = "2024-07-17T18:47:38.287Z" } wheels = [ @@ -4521,38 +4527,38 @@ wheels = [ [[package]] name = "pyzmq" -version = "27.0.2" +version = "27.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/66/159f38d184f08b5f971b467f87b1ab142ab1320d5200825c824b32b84b66/pyzmq-27.0.2.tar.gz", hash = "sha256:b398dd713b18de89730447347e96a0240225e154db56e35b6bb8447ffdb07798", size = 281440, upload-time = "2025-08-21T04:23:26.334Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/73/034429ab0f4316bf433eb6c20c3f49d1dc13b2ed4e4d951b283d300a0f35/pyzmq-27.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:063845960df76599ad4fad69fa4d884b3ba38304272104fdcd7e3af33faeeb1d", size = 1333169, upload-time = "2025-08-21T04:21:12.483Z" }, - { url = "https://files.pythonhosted.org/packages/35/02/c42b3b526eb03a570c889eea85a5602797f800a50ba8b09ddbf7db568b78/pyzmq-27.0.2-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:845a35fb21b88786aeb38af8b271d41ab0967985410f35411a27eebdc578a076", size = 909176, upload-time = "2025-08-21T04:21:13.835Z" }, - { url = "https://files.pythonhosted.org/packages/1b/35/a1c0b988fabbdf2dc5fe94b7c2bcfd61e3533e5109297b8e0daf1d7a8d2d/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:515d20b5c3c86db95503faa989853a8ab692aab1e5336db011cd6d35626c4cb1", size = 668972, upload-time = "2025-08-21T04:21:15.315Z" }, - { url = "https://files.pythonhosted.org/packages/a0/63/908ac865da32ceaeecea72adceadad28ca25b23a2ca5ff018e5bff30116f/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:862aedec0b0684a5050cdb5ec13c2da96d2f8dffda48657ed35e312a4e31553b", size = 856962, upload-time = "2025-08-21T04:21:16.652Z" }, - { url = "https://files.pythonhosted.org/packages/2f/5a/90b3cc20b65cdf9391896fcfc15d8db21182eab810b7ea05a2986912fbe2/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5bcfc51c7a4fce335d3bc974fd1d6a916abbcdd2b25f6e89d37b8def25f57", size = 1657712, upload-time = "2025-08-21T04:21:18.666Z" }, - { url = "https://files.pythonhosted.org/packages/c4/3c/32a5a80f9be4759325b8d7b22ce674bb87e586b4c80c6a9d77598b60d6f0/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:38ff75b2a36e3a032e9fef29a5871e3e1301a37464e09ba364e3c3193f62982a", size = 2035054, upload-time = "2025-08-21T04:21:20.073Z" }, - { url = "https://files.pythonhosted.org/packages/13/61/71084fe2ff2d7dc5713f8740d735336e87544845dae1207a8e2e16d9af90/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a5709abe8d23ca158a9d0a18c037f4193f5b6afeb53be37173a41e9fb885792", size = 1894010, upload-time = "2025-08-21T04:21:21.96Z" }, - { url = "https://files.pythonhosted.org/packages/cb/6b/77169cfb13b696e50112ca496b2ed23c4b7d8860a1ec0ff3e4b9f9926221/pyzmq-27.0.2-cp311-cp311-win32.whl", hash = "sha256:47c5dda2018c35d87be9b83de0890cb92ac0791fd59498847fc4eca6ff56671d", size = 566819, upload-time = "2025-08-21T04:21:23.31Z" }, - { url = "https://files.pythonhosted.org/packages/37/cd/86c4083e0f811f48f11bc0ddf1e7d13ef37adfd2fd4f78f2445f1cc5dec0/pyzmq-27.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:f54ca3e98f8f4d23e989c7d0edcf9da7a514ff261edaf64d1d8653dd5feb0a8b", size = 633264, upload-time = "2025-08-21T04:21:24.761Z" }, - { url = "https://files.pythonhosted.org/packages/a0/69/5b8bb6a19a36a569fac02153a9e083738785892636270f5f68a915956aea/pyzmq-27.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:2ef3067cb5b51b090fb853f423ad7ed63836ec154374282780a62eb866bf5768", size = 559316, upload-time = "2025-08-21T04:21:26.1Z" }, - { url = "https://files.pythonhosted.org/packages/68/69/b3a729e7b03e412bee2b1823ab8d22e20a92593634f664afd04c6c9d9ac0/pyzmq-27.0.2-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:5da05e3c22c95e23bfc4afeee6ff7d4be9ff2233ad6cb171a0e8257cd46b169a", size = 1305910, upload-time = "2025-08-21T04:21:27.609Z" }, - { url = "https://files.pythonhosted.org/packages/15/b7/f6a6a285193d489b223c340b38ee03a673467cb54914da21c3d7849f1b10/pyzmq-27.0.2-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4520577971d01d47e2559bb3175fce1be9103b18621bf0b241abe0a933d040", size = 895507, upload-time = "2025-08-21T04:21:29.005Z" }, - { url = "https://files.pythonhosted.org/packages/17/e6/c4ed2da5ef9182cde1b1f5d0051a986e76339d71720ec1a00be0b49275ad/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d7de7bf73165b90bd25a8668659ccb134dd28449116bf3c7e9bab5cf8a8ec9", size = 652670, upload-time = "2025-08-21T04:21:30.71Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d781ab0636570d32c745c4e389b1c6b713115905cca69ab6233508622edd/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340e7cddc32f147c6c00d116a3f284ab07ee63dbd26c52be13b590520434533c", size = 840581, upload-time = "2025-08-21T04:21:32.008Z" }, - { url = "https://files.pythonhosted.org/packages/a6/df/f24790caf565d72544f5c8d8500960b9562c1dc848d6f22f3c7e122e73d4/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba95693f9df8bb4a9826464fb0fe89033936f35fd4a8ff1edff09a473570afa0", size = 1641931, upload-time = "2025-08-21T04:21:33.371Z" }, - { url = "https://files.pythonhosted.org/packages/65/65/77d27b19fc5e845367f9100db90b9fce924f611b14770db480615944c9c9/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:ca42a6ce2d697537da34f77a1960d21476c6a4af3e539eddb2b114c3cf65a78c", size = 2021226, upload-time = "2025-08-21T04:21:35.301Z" }, - { url = "https://files.pythonhosted.org/packages/5b/65/1ed14421ba27a4207fa694772003a311d1142b7f543179e4d1099b7eb746/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e44e665d78a07214b2772ccbd4b9bcc6d848d7895f1b2d7653f047b6318a4f6", size = 1878047, upload-time = "2025-08-21T04:21:36.749Z" }, - { url = "https://files.pythonhosted.org/packages/dd/dc/e578549b89b40dc78a387ec471c2a360766690c0a045cd8d1877d401012d/pyzmq-27.0.2-cp312-abi3-win32.whl", hash = "sha256:272d772d116615397d2be2b1417b3b8c8bc8671f93728c2f2c25002a4530e8f6", size = 558757, upload-time = "2025-08-21T04:21:38.2Z" }, - { url = "https://files.pythonhosted.org/packages/b5/89/06600980aefcc535c758414da969f37a5194ea4cdb73b745223f6af3acfb/pyzmq-27.0.2-cp312-abi3-win_amd64.whl", hash = "sha256:734be4f44efba0aa69bf5f015ed13eb69ff29bf0d17ea1e21588b095a3147b8e", size = 619281, upload-time = "2025-08-21T04:21:39.909Z" }, - { url = "https://files.pythonhosted.org/packages/30/84/df8a5c089552d17c9941d1aea4314b606edf1b1622361dae89aacedc6467/pyzmq-27.0.2-cp312-abi3-win_arm64.whl", hash = "sha256:41f0bd56d9279392810950feb2785a419c2920bbf007fdaaa7f4a07332ae492d", size = 552680, upload-time = "2025-08-21T04:21:41.571Z" }, - { url = "https://files.pythonhosted.org/packages/c7/60/027d0032a1e3b1aabcef0e309b9ff8a4099bdd5a60ab38b36a676ff2bd7b/pyzmq-27.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e297784aea724294fe95e442e39a4376c2f08aa4fae4161c669f047051e31b02", size = 836007, upload-time = "2025-08-21T04:23:00.447Z" }, - { url = "https://files.pythonhosted.org/packages/25/20/2ed1e6168aaea323df9bb2c451309291f53ba3af372ffc16edd4ce15b9e5/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3659a79ded9745bc9c2aef5b444ac8805606e7bc50d2d2eb16dc3ab5483d91f", size = 799932, upload-time = "2025-08-21T04:23:02.052Z" }, - { url = "https://files.pythonhosted.org/packages/fd/25/5c147307de546b502c9373688ce5b25dc22288d23a1ebebe5d587bf77610/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3dba49ff037d02373a9306b58d6c1e0be031438f822044e8767afccfdac4c6b", size = 567459, upload-time = "2025-08-21T04:23:03.593Z" }, - { url = "https://files.pythonhosted.org/packages/71/06/0dc56ffc615c8095cd089c9b98ce5c733e990f09ce4e8eea4aaf1041a532/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de84e1694f9507b29e7b263453a2255a73e3d099d258db0f14539bad258abe41", size = 747088, upload-time = "2025-08-21T04:23:05.334Z" }, - { url = "https://files.pythonhosted.org/packages/06/f6/4a50187e023b8848edd3f0a8e197b1a7fb08d261d8c60aae7cb6c3d71612/pyzmq-27.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f0944d65ba2b872b9fcece08411d6347f15a874c775b4c3baae7f278550da0fb", size = 544639, upload-time = "2025-08-21T04:23:07.279Z" }, + { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328, upload-time = "2025-09-08T23:07:45.946Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803, upload-time = "2025-09-08T23:07:47.551Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836, upload-time = "2025-09-08T23:07:49.436Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038, upload-time = "2025-09-08T23:07:51.234Z" }, + { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531, upload-time = "2025-09-08T23:07:52.795Z" }, + { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786, upload-time = "2025-09-08T23:07:55.047Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220, upload-time = "2025-09-08T23:07:57.172Z" }, + { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155, upload-time = "2025-09-08T23:07:59.05Z" }, + { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428, upload-time = "2025-09-08T23:08:00.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497, upload-time = "2025-09-08T23:08:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265, upload-time = "2025-09-08T23:09:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208, upload-time = "2025-09-08T23:09:51.073Z" }, + { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747, upload-time = "2025-09-08T23:09:52.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371, upload-time = "2025-09-08T23:09:54.563Z" }, + { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" }, ] [[package]] @@ -4655,28 +4661,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.11" +version = "0.13.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/55/16ab6a7d88d93001e1ae4c34cbdcfb376652d761799459ff27c1dc20f6fa/ruff-0.12.11.tar.gz", hash = "sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d", size = 5347103, upload-time = "2025-08-28T13:59:08.87Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/33/c8e89216845615d14d2d42ba2bee404e7206a8db782f33400754f3799f05/ruff-0.13.1.tar.gz", hash = "sha256:88074c3849087f153d4bb22e92243ad4c1b366d7055f98726bc19aa08dc12d51", size = 5397987, upload-time = "2025-09-18T19:52:44.33Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/a2/3b3573e474de39a7a475f3fbaf36a25600bfeb238e1a90392799163b64a0/ruff-0.12.11-py3-none-linux_armv6l.whl", hash = "sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065", size = 11979885, upload-time = "2025-08-28T13:58:26.654Z" }, - { url = "https://files.pythonhosted.org/packages/76/e4/235ad6d1785a2012d3ded2350fd9bc5c5af8c6f56820e696b0118dfe7d24/ruff-0.12.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93", size = 12742364, upload-time = "2025-08-28T13:58:30.256Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/15b72c5fe6b1e402a543aa9d8960e0a7e19dfb079f5b0b424db48b7febab/ruff-0.12.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd", size = 11920111, upload-time = "2025-08-28T13:58:33.677Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c0/f66339d7893798ad3e17fa5a1e587d6fd9806f7c1c062b63f8b09dda6702/ruff-0.12.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee", size = 12160060, upload-time = "2025-08-28T13:58:35.74Z" }, - { url = "https://files.pythonhosted.org/packages/03/69/9870368326db26f20c946205fb2d0008988aea552dbaec35fbacbb46efaa/ruff-0.12.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0", size = 11799848, upload-time = "2025-08-28T13:58:38.051Z" }, - { url = "https://files.pythonhosted.org/packages/25/8c/dd2c7f990e9b3a8a55eee09d4e675027d31727ce33cdb29eab32d025bdc9/ruff-0.12.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644", size = 13536288, upload-time = "2025-08-28T13:58:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/7a/30/d5496fa09aba59b5e01ea76775a4c8897b13055884f56f1c35a4194c2297/ruff-0.12.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211", size = 14490633, upload-time = "2025-08-28T13:58:42.285Z" }, - { url = "https://files.pythonhosted.org/packages/9b/2f/81f998180ad53445d403c386549d6946d0748e536d58fce5b5e173511183/ruff-0.12.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793", size = 13888430, upload-time = "2025-08-28T13:58:44.641Z" }, - { url = "https://files.pythonhosted.org/packages/87/71/23a0d1d5892a377478c61dbbcffe82a3476b050f38b5162171942a029ef3/ruff-0.12.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee", size = 12913133, upload-time = "2025-08-28T13:58:47.039Z" }, - { url = "https://files.pythonhosted.org/packages/80/22/3c6cef96627f89b344c933781ed38329bfb87737aa438f15da95907cbfd5/ruff-0.12.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8", size = 13169082, upload-time = "2025-08-28T13:58:49.157Z" }, - { url = "https://files.pythonhosted.org/packages/05/b5/68b3ff96160d8b49e8dd10785ff3186be18fd650d356036a3770386e6c7f/ruff-0.12.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f", size = 13139490, upload-time = "2025-08-28T13:58:51.593Z" }, - { url = "https://files.pythonhosted.org/packages/59/b9/050a3278ecd558f74f7ee016fbdf10591d50119df8d5f5da45a22c6afafc/ruff-0.12.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000", size = 11958928, upload-time = "2025-08-28T13:58:53.943Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bc/93be37347db854806904a43b0493af8d6873472dfb4b4b8cbb27786eb651/ruff-0.12.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2", size = 11764513, upload-time = "2025-08-28T13:58:55.976Z" }, - { url = "https://files.pythonhosted.org/packages/7a/a1/1471751e2015a81fd8e166cd311456c11df74c7e8769d4aabfbc7584c7ac/ruff-0.12.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39", size = 12745154, upload-time = "2025-08-28T13:58:58.16Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/2542b14890d0f4872dd81b7b2a6aed3ac1786fae1ce9b17e11e6df9e31e3/ruff-0.12.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9", size = 13227653, upload-time = "2025-08-28T13:59:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/22/16/2fbfc61047dbfd009c58a28369a693a1484ad15441723be1cd7fe69bb679/ruff-0.12.11-py3-none-win32.whl", hash = "sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3", size = 11944270, upload-time = "2025-08-28T13:59:02.347Z" }, - { url = "https://files.pythonhosted.org/packages/08/a5/34276984705bfe069cd383101c45077ee029c3fe3b28225bf67aa35f0647/ruff-0.12.11-py3-none-win_amd64.whl", hash = "sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd", size = 13046600, upload-time = "2025-08-28T13:59:04.751Z" }, - { url = "https://files.pythonhosted.org/packages/84/a8/001d4a7c2b37623a3fd7463208267fb906df40ff31db496157549cfd6e72/ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea", size = 12135290, upload-time = "2025-08-28T13:59:06.933Z" }, + { url = "https://files.pythonhosted.org/packages/f3/41/ca37e340938f45cfb8557a97a5c347e718ef34702546b174e5300dbb1f28/ruff-0.13.1-py3-none-linux_armv6l.whl", hash = "sha256:b2abff595cc3cbfa55e509d89439b5a09a6ee3c252d92020bd2de240836cf45b", size = 12304308, upload-time = "2025-09-18T19:51:56.253Z" }, + { url = "https://files.pythonhosted.org/packages/ff/84/ba378ef4129415066c3e1c80d84e539a0d52feb250685091f874804f28af/ruff-0.13.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4ee9f4249bf7f8bb3984c41bfaf6a658162cdb1b22e3103eabc7dd1dc5579334", size = 12937258, upload-time = "2025-09-18T19:52:00.184Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b6/ec5e4559ae0ad955515c176910d6d7c93edcbc0ed1a3195a41179c58431d/ruff-0.13.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c5da4af5f6418c07d75e6f3224e08147441f5d1eac2e6ce10dcce5e616a3bae", size = 12214554, upload-time = "2025-09-18T19:52:02.753Z" }, + { url = "https://files.pythonhosted.org/packages/70/d6/cb3e3b4f03b9b0c4d4d8f06126d34b3394f6b4d764912fe80a1300696ef6/ruff-0.13.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80524f84a01355a59a93cef98d804e2137639823bcee2931f5028e71134a954e", size = 12448181, upload-time = "2025-09-18T19:52:05.279Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ea/bf60cb46d7ade706a246cd3fb99e4cfe854efa3dfbe530d049c684da24ff/ruff-0.13.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff7f5ce8d7988767dd46a148192a14d0f48d1baea733f055d9064875c7d50389", size = 12104599, upload-time = "2025-09-18T19:52:07.497Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3e/05f72f4c3d3a69e65d55a13e1dd1ade76c106d8546e7e54501d31f1dc54a/ruff-0.13.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c55d84715061f8b05469cdc9a446aa6c7294cd4bd55e86a89e572dba14374f8c", size = 13791178, upload-time = "2025-09-18T19:52:10.189Z" }, + { url = "https://files.pythonhosted.org/packages/81/e7/01b1fc403dd45d6cfe600725270ecc6a8f8a48a55bc6521ad820ed3ceaf8/ruff-0.13.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ac57fed932d90fa1624c946dc67a0a3388d65a7edc7d2d8e4ca7bddaa789b3b0", size = 14814474, upload-time = "2025-09-18T19:52:12.866Z" }, + { url = "https://files.pythonhosted.org/packages/fa/92/d9e183d4ed6185a8df2ce9faa3f22e80e95b5f88d9cc3d86a6d94331da3f/ruff-0.13.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c366a71d5b4f41f86a008694f7a0d75fe409ec298685ff72dc882f882d532e36", size = 14217531, upload-time = "2025-09-18T19:52:15.245Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4a/6ddb1b11d60888be224d721e01bdd2d81faaf1720592858ab8bac3600466/ruff-0.13.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4ea9d1b5ad3e7a83ee8ebb1229c33e5fe771e833d6d3dcfca7b77d95b060d38", size = 13265267, upload-time = "2025-09-18T19:52:17.649Z" }, + { url = "https://files.pythonhosted.org/packages/81/98/3f1d18a8d9ea33ef2ad508f0417fcb182c99b23258ec5e53d15db8289809/ruff-0.13.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f70202996055b555d3d74b626406476cc692f37b13bac8828acff058c9966a", size = 13243120, upload-time = "2025-09-18T19:52:20.332Z" }, + { url = "https://files.pythonhosted.org/packages/8d/86/b6ce62ce9c12765fa6c65078d1938d2490b2b1d9273d0de384952b43c490/ruff-0.13.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f8cff7a105dad631085d9505b491db33848007d6b487c3c1979dd8d9b2963783", size = 13443084, upload-time = "2025-09-18T19:52:23.032Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6e/af7943466a41338d04503fb5a81b2fd07251bd272f546622e5b1599a7976/ruff-0.13.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9761e84255443316a258dd7dfbd9bfb59c756e52237ed42494917b2577697c6a", size = 12295105, upload-time = "2025-09-18T19:52:25.263Z" }, + { url = "https://files.pythonhosted.org/packages/3f/97/0249b9a24f0f3ebd12f007e81c87cec6d311de566885e9309fcbac5b24cc/ruff-0.13.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3d376a88c3102ef228b102211ef4a6d13df330cb0f5ca56fdac04ccec2a99700", size = 12072284, upload-time = "2025-09-18T19:52:27.478Z" }, + { url = "https://files.pythonhosted.org/packages/f6/85/0b64693b2c99d62ae65236ef74508ba39c3febd01466ef7f354885e5050c/ruff-0.13.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cbefd60082b517a82c6ec8836989775ac05f8991715d228b3c1d86ccc7df7dae", size = 12970314, upload-time = "2025-09-18T19:52:30.212Z" }, + { url = "https://files.pythonhosted.org/packages/96/fc/342e9f28179915d28b3747b7654f932ca472afbf7090fc0c4011e802f494/ruff-0.13.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd16b9a5a499fe73f3c2ef09a7885cb1d97058614d601809d37c422ed1525317", size = 13422360, upload-time = "2025-09-18T19:52:32.676Z" }, + { url = "https://files.pythonhosted.org/packages/37/54/6177a0dc10bce6f43e392a2192e6018755473283d0cf43cc7e6afc182aea/ruff-0.13.1-py3-none-win32.whl", hash = "sha256:55e9efa692d7cb18580279f1fbb525146adc401f40735edf0aaeabd93099f9a0", size = 12178448, upload-time = "2025-09-18T19:52:35.545Z" }, + { url = "https://files.pythonhosted.org/packages/64/51/c6a3a33d9938007b8bdc8ca852ecc8d810a407fb513ab08e34af12dc7c24/ruff-0.13.1-py3-none-win_amd64.whl", hash = "sha256:3a3fb595287ee556de947183489f636b9f76a72f0fa9c028bdcabf5bab2cc5e5", size = 13286458, upload-time = "2025-09-18T19:52:38.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/04/afc078a12cf68592345b1e2d6ecdff837d286bac023d7a22c54c7a698c5b/ruff-0.13.1-py3-none-win_arm64.whl", hash = "sha256:c0bae9ffd92d54e03c2bf266f466da0a65e145f298ee5b5846ed435f6a00518a", size = 12437893, upload-time = "2025-09-18T19:52:41.283Z" }, ] [[package]] @@ -4690,47 +4696,46 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.35.2" +version = "2.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/79/0ecb942f3f1ad26c40c27f81ff82392d85c01d26a45e3c72c2b37807e680/sentry_sdk-2.35.2.tar.gz", hash = "sha256:e9e8f3c795044beb59f2c8f4c6b9b0f9779e5e604099882df05eec525e782cc6", size = 343377, upload-time = "2025-09-01T11:00:58.633Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/22/60fd703b34d94d216b2387e048ac82de3e86b63bc28869fb076f8bb0204a/sentry_sdk-2.38.0.tar.gz", hash = "sha256:792d2af45e167e2f8a3347143f525b9b6bac6f058fb2014720b40b84ccbeb985", size = 348116, upload-time = "2025-09-15T15:00:37.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/91/a43308dc82a0e32d80cd0dfdcfca401ecbd0f431ab45f24e48bb97b7800d/sentry_sdk-2.35.2-py2.py3-none-any.whl", hash = "sha256:38c98e3cbb620dd3dd80a8d6e39c753d453dd41f8a9df581b0584c19a52bc926", size = 363975, upload-time = "2025-09-01T11:00:56.574Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/bde4c4bbb269b71bc09316af8eb00da91f67814d40337cc12ef9c8742541/sentry_sdk-2.38.0-py2.py3-none-any.whl", hash = "sha256:2324aea8573a3fa1576df7fb4d65c4eb8d9929c8fa5939647397a07179eef8d0", size = 370346, upload-time = "2025-09-15T15:00:35.821Z" }, ] [[package]] name = "setproctitle" -version = "1.3.6" +version = "1.3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/af/56efe21c53ac81ac87e000b15e60b3d8104224b4313b6eacac3597bd183d/setproctitle-1.3.6.tar.gz", hash = "sha256:c9f32b96c700bb384f33f7cf07954bb609d35dd82752cef57fb2ee0968409169", size = 26889, upload-time = "2025-04-29T13:35:00.184Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/48/49393a96a2eef1ab418b17475fb92b8fcfad83d099e678751b05472e69de/setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e", size = 27002, upload-time = "2025-09-05T12:51:25.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/3b/8288d0cd969a63500dd62fc2c99ce6980f9909ccef0770ab1f86c361e0bf/setproctitle-1.3.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a1d856b0f4e4a33e31cdab5f50d0a14998f3a2d726a3fd5cb7c4d45a57b28d1b", size = 17412, upload-time = "2025-04-29T13:32:58.135Z" }, - { url = "https://files.pythonhosted.org/packages/39/37/43a5a3e25ca1048dbbf4db0d88d346226f5f1acd131bb8e660f4bfe2799f/setproctitle-1.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50706b9c0eda55f7de18695bfeead5f28b58aa42fd5219b3b1692d554ecbc9ec", size = 11963, upload-time = "2025-04-29T13:32:59.17Z" }, - { url = "https://files.pythonhosted.org/packages/5b/47/f103c40e133154783c91a10ab08ac9fc410ed835aa85bcf7107cb882f505/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af188f3305f0a65c3217c30c6d4c06891e79144076a91e8b454f14256acc7279", size = 31718, upload-time = "2025-04-29T13:33:00.36Z" }, - { url = "https://files.pythonhosted.org/packages/1f/13/7325dd1c008dd6c0ebd370ddb7505977054a87e406f142318e395031a792/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce0ed8b3f64c71c140f0ec244e5fdf8ecf78ddf8d2e591d4a8b6aa1c1214235", size = 33027, upload-time = "2025-04-29T13:33:01.499Z" }, - { url = "https://files.pythonhosted.org/packages/0c/0a/6075bfea05a71379d77af98a9ac61163e8b6e5ef1ae58cd2b05871b2079c/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70100e2087fe05359f249a0b5f393127b3a1819bf34dec3a3e0d4941138650c9", size = 30223, upload-time = "2025-04-29T13:33:03.259Z" }, - { url = "https://files.pythonhosted.org/packages/cc/41/fbf57ec52f4f0776193bd94334a841f0bc9d17e745f89c7790f336420c65/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1065ed36bd03a3fd4186d6c6de5f19846650b015789f72e2dea2d77be99bdca1", size = 31204, upload-time = "2025-04-29T13:33:04.455Z" }, - { url = "https://files.pythonhosted.org/packages/97/b5/f799fb7a00de29fb0ac1dfd015528dea425b9e31a8f1068a0b3df52d317f/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4adf6a0013fe4e0844e3ba7583ec203ca518b9394c6cc0d3354df2bf31d1c034", size = 31181, upload-time = "2025-04-29T13:33:05.697Z" }, - { url = "https://files.pythonhosted.org/packages/b5/b7/81f101b612014ec61723436022c31146178813d6ca6b947f7b9c84e9daf4/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb7452849f6615871eabed6560ffedfe56bc8af31a823b6be4ce1e6ff0ab72c5", size = 30101, upload-time = "2025-04-29T13:33:07.223Z" }, - { url = "https://files.pythonhosted.org/packages/67/23/681232eed7640eab96719daa8647cc99b639e3daff5c287bd270ef179a73/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a094b7ce455ca341b59a0f6ce6be2e11411ba6e2860b9aa3dbb37468f23338f4", size = 32438, upload-time = "2025-04-29T13:33:08.538Z" }, - { url = "https://files.pythonhosted.org/packages/19/f8/4d075a7bdc3609ac71535b849775812455e4c40aedfbf0778a6f123b1774/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad1c2c2baaba62823a7f348f469a967ece0062140ca39e7a48e4bbb1f20d54c4", size = 30625, upload-time = "2025-04-29T13:33:09.707Z" }, - { url = "https://files.pythonhosted.org/packages/5f/73/a2a8259ebee166aee1ca53eead75de0e190b3ddca4f716e5c7470ebb7ef6/setproctitle-1.3.6-cp311-cp311-win32.whl", hash = "sha256:8050c01331135f77ec99d99307bfbc6519ea24d2f92964b06f3222a804a3ff1f", size = 11488, upload-time = "2025-04-29T13:33:10.953Z" }, - { url = "https://files.pythonhosted.org/packages/c9/15/52cf5e1ff0727d53704cfdde2858eaf237ce523b0b04db65faa84ff83e13/setproctitle-1.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:9b73cf0fe28009a04a35bb2522e4c5b5176cc148919431dcb73fdbdfaab15781", size = 12201, upload-time = "2025-04-29T13:33:12.389Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fb/99456fd94d4207c5f6c40746a048a33a52b4239cd7d9c8d4889e2210ec82/setproctitle-1.3.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:af44bb7a1af163806bbb679eb8432fa7b4fb6d83a5d403b541b675dcd3798638", size = 17399, upload-time = "2025-04-29T13:33:13.406Z" }, - { url = "https://files.pythonhosted.org/packages/d5/48/9699191fe6062827683c43bfa9caac33a2c89f8781dd8c7253fa3dba85fd/setproctitle-1.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cca16fd055316a48f0debfcbfb6af7cea715429fc31515ab3fcac05abd527d8", size = 11966, upload-time = "2025-04-29T13:33:14.976Z" }, - { url = "https://files.pythonhosted.org/packages/33/03/b085d192b9ecb9c7ce6ad6ef30ecf4110b7f39430b58a56245569827fcf4/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea002088d5554fd75e619742cefc78b84a212ba21632e59931b3501f0cfc8f67", size = 32017, upload-time = "2025-04-29T13:33:16.163Z" }, - { url = "https://files.pythonhosted.org/packages/ae/68/c53162e645816f97212002111420d1b2f75bf6d02632e37e961dc2cd6d8b/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb465dd5825356c1191a038a86ee1b8166e3562d6e8add95eec04ab484cfb8a2", size = 33419, upload-time = "2025-04-29T13:33:18.239Z" }, - { url = "https://files.pythonhosted.org/packages/ac/0d/119a45d15a816a6cf5ccc61b19729f82620095b27a47e0a6838216a95fae/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2c8e20487b3b73c1fa72c56f5c89430617296cd380373e7af3a538a82d4cd6d", size = 30711, upload-time = "2025-04-29T13:33:19.571Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fb/5e9b5068df9e9f31a722a775a5e8322a29a638eaaa3eac5ea7f0b35e6314/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d6252098e98129a1decb59b46920d4eca17b0395f3d71b0d327d086fefe77d", size = 31742, upload-time = "2025-04-29T13:33:21.172Z" }, - { url = "https://files.pythonhosted.org/packages/35/88/54de1e73e8fce87d587889c7eedb48fc4ee2bbe4e4ca6331690d03024f86/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf355fbf0d4275d86f9f57be705d8e5eaa7f8ddb12b24ced2ea6cbd68fdb14dc", size = 31925, upload-time = "2025-04-29T13:33:22.427Z" }, - { url = "https://files.pythonhosted.org/packages/f3/01/65948d7badd66e63e3db247b923143da142790fa293830fdecf832712c2d/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e288f8a162d663916060beb5e8165a8551312b08efee9cf68302687471a6545d", size = 30981, upload-time = "2025-04-29T13:33:23.739Z" }, - { url = "https://files.pythonhosted.org/packages/22/20/c495e61786f1d38d5dc340b9d9077fee9be3dfc7e89f515afe12e1526dbc/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b2e54f4a2dc6edf0f5ea5b1d0a608d2af3dcb5aa8c8eeab9c8841b23e1b054fe", size = 33209, upload-time = "2025-04-29T13:33:24.915Z" }, - { url = "https://files.pythonhosted.org/packages/98/3f/a457b8550fbd34d5b482fe20b8376b529e76bf1fbf9a474a6d9a641ab4ad/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b6f4abde9a2946f57e8daaf1160b2351bcf64274ef539e6675c1d945dbd75e2a", size = 31587, upload-time = "2025-04-29T13:33:26.123Z" }, - { url = "https://files.pythonhosted.org/packages/44/fe/743517340e5a635e3f1c4310baea20c16c66202f96a6f4cead222ffd6d84/setproctitle-1.3.6-cp312-cp312-win32.whl", hash = "sha256:db608db98ccc21248370d30044a60843b3f0f3d34781ceeea67067c508cd5a28", size = 11487, upload-time = "2025-04-29T13:33:27.403Z" }, - { url = "https://files.pythonhosted.org/packages/60/9a/d88f1c1f0f4efff1bd29d9233583ee341114dda7d9613941453984849674/setproctitle-1.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:082413db8a96b1f021088e8ec23f0a61fec352e649aba20881895815388b66d3", size = 12208, upload-time = "2025-04-29T13:33:28.852Z" }, + { url = "https://files.pythonhosted.org/packages/04/cd/1b7ba5cad635510720ce19d7122154df96a2387d2a74217be552887c93e5/setproctitle-1.3.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a600eeb4145fb0ee6c287cb82a2884bd4ec5bbb076921e287039dcc7b7cc6dd0", size = 18085, upload-time = "2025-09-05T12:49:22.183Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1a/b2da0a620490aae355f9d72072ac13e901a9fec809a6a24fc6493a8f3c35/setproctitle-1.3.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97a090fed480471bb175689859532709e28c085087e344bca45cf318034f70c4", size = 13097, upload-time = "2025-09-05T12:49:23.322Z" }, + { url = "https://files.pythonhosted.org/packages/18/2e/bd03ff02432a181c1787f6fc2a678f53b7dacdd5ded69c318fe1619556e8/setproctitle-1.3.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1607b963e7b53e24ec8a2cb4e0ab3ae591d7c6bf0a160feef0551da63452b37f", size = 32191, upload-time = "2025-09-05T12:49:24.567Z" }, + { url = "https://files.pythonhosted.org/packages/28/78/1e62fc0937a8549f2220445ed2175daacee9b6764c7963b16148119b016d/setproctitle-1.3.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a20fb1a3974e2dab857870cf874b325b8705605cb7e7e8bcbb915bca896f52a9", size = 33203, upload-time = "2025-09-05T12:49:25.871Z" }, + { url = "https://files.pythonhosted.org/packages/a0/3c/65edc65db3fa3df400cf13b05e9d41a3c77517b4839ce873aa6b4043184f/setproctitle-1.3.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8d961bba676e07d77665204f36cffaa260f526e7b32d07ab3df6a2c1dfb44ba", size = 34963, upload-time = "2025-09-05T12:49:27.044Z" }, + { url = "https://files.pythonhosted.org/packages/a1/32/89157e3de997973e306e44152522385f428e16f92f3cf113461489e1e2ee/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:db0fd964fbd3a9f8999b502f65bd2e20883fdb5b1fae3a424e66db9a793ed307", size = 32398, upload-time = "2025-09-05T12:49:28.909Z" }, + { url = "https://files.pythonhosted.org/packages/4a/18/77a765a339ddf046844cb4513353d8e9dcd8183da9cdba6e078713e6b0b2/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:db116850fcf7cca19492030f8d3b4b6e231278e8fe097a043957d22ce1bdf3ee", size = 33657, upload-time = "2025-09-05T12:49:30.323Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/f0b6205c64d74d2a24a58644a38ec77bdbaa6afc13747e75973bf8904932/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:316664d8b24a5c91ee244460bdaf7a74a707adaa9e14fbe0dc0a53168bb9aba1", size = 31836, upload-time = "2025-09-05T12:49:32.309Z" }, + { url = "https://files.pythonhosted.org/packages/ba/51/e1277f9ba302f1a250bbd3eedbbee747a244b3cc682eb58fb9733968f6d8/setproctitle-1.3.7-cp311-cp311-win32.whl", hash = "sha256:b74774ca471c86c09b9d5037c8451fff06bb82cd320d26ae5a01c758088c0d5d", size = 12556, upload-time = "2025-09-05T12:49:33.529Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7b/822a23f17e9003dfdee92cd72758441ca2a3680388da813a371b716fb07f/setproctitle-1.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:acb9097213a8dd3410ed9f0dc147840e45ca9797785272928d4be3f0e69e3be4", size = 13243, upload-time = "2025-09-05T12:49:34.553Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f0/2dc88e842077719d7384d86cc47403e5102810492b33680e7dadcee64cd8/setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e", size = 18049, upload-time = "2025-09-05T12:49:36.241Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b4/50940504466689cda65680c9e9a1e518e5750c10490639fa687489ac7013/setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798", size = 13079, upload-time = "2025-09-05T12:49:38.088Z" }, + { url = "https://files.pythonhosted.org/packages/d0/99/71630546b9395b095f4082be41165d1078204d1696c2d9baade3de3202d0/setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629", size = 32932, upload-time = "2025-09-05T12:49:39.271Z" }, + { url = "https://files.pythonhosted.org/packages/50/22/cee06af4ffcfb0e8aba047bd44f5262e644199ae7527ae2c1f672b86495c/setproctitle-1.3.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6915964a6dda07920a1159321dcd6d94fc7fc526f815ca08a8063aeca3c204f1", size = 33736, upload-time = "2025-09-05T12:49:40.565Z" }, + { url = "https://files.pythonhosted.org/packages/5c/00/a5949a8bb06ef5e7df214fc393bb2fb6aedf0479b17214e57750dfdd0f24/setproctitle-1.3.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cff72899861c765bd4021d1ff1c68d60edc129711a2fdba77f9cb69ef726a8b6", size = 35605, upload-time = "2025-09-05T12:49:42.362Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3a/50caca532a9343828e3bf5778c7a84d6c737a249b1796d50dd680290594d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b7cb05bd446687ff816a3aaaf831047fc4c364feff7ada94a66024f1367b448c", size = 33143, upload-time = "2025-09-05T12:49:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ca/14/b843a251296ce55e2e17c017d6b9f11ce0d3d070e9265de4ecad948b913d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3a57b9a00de8cae7e2a1f7b9f0c2ac7b69372159e16a7708aa2f38f9e5cc987a", size = 34434, upload-time = "2025-09-05T12:49:45.31Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b7/06145c238c0a6d2c4bc881f8be230bb9f36d2bf51aff7bddcb796d5eed67/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d8828b356114f6b308b04afe398ed93803d7fca4a955dd3abe84430e28d33739", size = 32795, upload-time = "2025-09-05T12:49:46.419Z" }, + { url = "https://files.pythonhosted.org/packages/ef/dc/ef76a81fac9bf27b84ed23df19c1f67391a753eed6e3c2254ebcb5133f56/setproctitle-1.3.7-cp312-cp312-win32.whl", hash = "sha256:b0304f905efc845829ac2bc791ddebb976db2885f6171f4a3de678d7ee3f7c9f", size = 12552, upload-time = "2025-09-05T12:49:47.635Z" }, + { url = "https://files.pythonhosted.org/packages/e2/5b/a9fe517912cd6e28cf43a212b80cb679ff179a91b623138a99796d7d18a0/setproctitle-1.3.7-cp312-cp312-win_amd64.whl", hash = "sha256:9888ceb4faea3116cf02a920ff00bfbc8cc899743e4b4ac914b03625bdc3c300", size = 13247, upload-time = "2025-09-05T12:49:49.16Z" }, + { url = "https://files.pythonhosted.org/packages/c3/5b/5e1c117ac84e3cefcf8d7a7f6b2461795a87e20869da065a5c087149060b/setproctitle-1.3.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b1cac6a4b0252b8811d60b6d8d0f157c0fdfed379ac89c25a914e6346cf355a1", size = 12587, upload-time = "2025-09-05T12:51:21.195Z" }, + { url = "https://files.pythonhosted.org/packages/73/02/b9eadc226195dcfa90eed37afe56b5dd6fa2f0e5220ab8b7867b8862b926/setproctitle-1.3.7-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1704c9e041f2b1dc38f5be4552e141e1432fba3dd52c72eeffd5bc2db04dc65", size = 14286, upload-time = "2025-09-05T12:51:22.61Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/1be1d2a53c2a91ec48fa2ff4a409b395f836798adf194d99de9c059419ea/setproctitle-1.3.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b08b61976ffa548bd5349ce54404bf6b2d51bd74d4f1b241ed1b0f25bce09c3a", size = 13282, upload-time = "2025-09-05T12:51:24.094Z" }, ] [[package]] @@ -4747,7 +4752,7 @@ name = "shapely" version = "2.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } wheels = [ @@ -4771,22 +4776,22 @@ wheels = [ [[package]] name = "siphash24" -version = "1.7" +version = "1.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/be/f0a0ffbb00c51c5633b41459b5ce9b017c025a9256b4403e648c18e70850/siphash24-1.7.tar.gz", hash = "sha256:6e90fee5f199ea25b4e7303646b31872a437174fe885a93dbd4cf7784eb48164", size = 19801, upload-time = "2024-10-15T13:41:51.924Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/a2/e049b6fccf7a94bd1b2f68b3059a7d6a7aea86a808cac80cb9ae71ab6254/siphash24-1.8.tar.gz", hash = "sha256:aa932f0af4a7335caef772fdaf73a433a32580405c41eb17ff24077944b0aa97", size = 19946, upload-time = "2025-09-02T20:42:04.856Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/67/4ffd23a848739966e1b314ef99f6410035bccee00be14261313787b8f506/siphash24-1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de75488e93f1cd12c8d5004efd1ebd958c0265205a9d73e8dd8b071900838841", size = 80493, upload-time = "2024-10-15T13:41:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/56/bd/ec198a8c7aef65e967ae84f633bd9950d784c9e527d738c9a3e4bccc34a5/siphash24-1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffca9908450f9f346e97a223185fcd16217d67d84c6f246f3080c4224f41a514", size = 75350, upload-time = "2024-10-15T13:41:16.262Z" }, - { url = "https://files.pythonhosted.org/packages/50/5a/77838c916bd15addfc2e51286db4c442cb12e25eb4f8d296c394c2280240/siphash24-1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8ff44ce166452993fea267ea1b2fd089d8e7f103b13d360da441f12b0df121d", size = 100567, upload-time = "2024-10-15T13:41:17.435Z" }, - { url = "https://files.pythonhosted.org/packages/f0/aa/736a0a2efae9a6f69ac1ee4d28c2274fcad2150349fac752d6c525c4e06e/siphash24-1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4062548dcb1eef13bbe0356d6f8675bfe4571ef38d7103445daa82ba167240d1", size = 105630, upload-time = "2024-10-15T13:41:18.578Z" }, - { url = "https://files.pythonhosted.org/packages/79/52/1afbd70142d3db093d49197e3abe15ca2f1a14678299327ba776944b4771/siphash24-1.7-cp311-cp311-win32.whl", hash = "sha256:7b4ea29376b688fbcc3d25707c15a9dfe7b4ebbc4322878d75bb77e199210a39", size = 67648, upload-time = "2024-10-15T13:41:19.606Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1d/bedcd04c2d1d199c9f6b3e61a6caae0e17257696c9f49594e49856b17a99/siphash24-1.7-cp311-cp311-win_amd64.whl", hash = "sha256:ec06104e6ef1e512ee30f1b8aeae2b83c0f55f12a94042f0df5a87d43a1f4c52", size = 80046, upload-time = "2024-10-15T13:41:20.654Z" }, - { url = "https://files.pythonhosted.org/packages/3e/62/93e552af9535a416f684327f870143ee42fc9e816091672467cdfd62cce6/siphash24-1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:76a64ff0cdd192e4d0a391956d9b121c56ed56e773c5ab7eb7c3e035fd16e8cb", size = 82084, upload-time = "2024-10-15T13:41:21.776Z" }, - { url = "https://files.pythonhosted.org/packages/59/3e/b0791ab53aa9ac191b71a021eab2e75baa7c27d7feb7ec148d7961d148ba/siphash24-1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ca649bc7437d614f758891deade3b187832792a853269219e77f10509f82fe", size = 76233, upload-time = "2024-10-15T13:41:22.787Z" }, - { url = "https://files.pythonhosted.org/packages/29/4c/4c1b809bf302e9b60f3ec09ba115b2a4ac1ff6755735ee8884924fcdb45e/siphash24-1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc37dd0aed23f76bd257fbd2953fd5d954b329d7463c6ff57263a2699c52dde6", size = 98188, upload-time = "2024-10-15T13:41:24.327Z" }, - { url = "https://files.pythonhosted.org/packages/96/bf/e6b49f8ff88130bd224f291ea77d30fdde4df5f6572c519aca5d8fc8a27c/siphash24-1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eea490a200891905856b6ad0f9c56d4ec787876220bcb34c49441b2566b97887", size = 102946, upload-time = "2024-10-15T13:41:25.633Z" }, - { url = "https://files.pythonhosted.org/packages/3d/75/45c831626013950fb2ea715c218c3397e5cf2328a67208bf5d8ff69aa9e6/siphash24-1.7-cp312-cp312-win32.whl", hash = "sha256:69eb8c2c112a738875bb283cd53ef5e86874bc5aed17f3020b38e9174208fb79", size = 68323, upload-time = "2024-10-15T13:41:27.349Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d3/39190c40a68defd19b99c1082dd7455543a52283803bfa111b0e45fae968/siphash24-1.7-cp312-cp312-win_amd64.whl", hash = "sha256:7459569ea4669b6feeaf7d299fc5157cc5c69ca1231dc0decb7a7da2397c782e", size = 81000, upload-time = "2024-10-15T13:41:28.364Z" }, + { url = "https://files.pythonhosted.org/packages/82/23/f53f5bd8866c6ea3abe434c9f208e76ea027210d8b75cd0e0dc849661c7a/siphash24-1.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4662ac616bce4d3c9d6003a0d398e56f8be408fc53a166b79fad08d4f34268e", size = 76930, upload-time = "2025-09-02T20:41:00.869Z" }, + { url = "https://files.pythonhosted.org/packages/0b/25/aebf246904424a06e7ffb7a40cfa9ea9e590ea0fac82e182e0f5d1f1d7ef/siphash24-1.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:53d6bed0951a99c6d2891fa6f8acfd5ca80c3e96c60bcee99f6fa01a04773b1c", size = 74315, upload-time = "2025-09-02T20:41:02.38Z" }, + { url = "https://files.pythonhosted.org/packages/59/3f/7010407c3416ef052d46550d54afb2581fb247018fc6500af8c66669eff2/siphash24-1.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d114c03648630e9e07dac2fe95442404e4607adca91640d274ece1a4fa71123e", size = 99756, upload-time = "2025-09-02T20:41:03.902Z" }, + { url = "https://files.pythonhosted.org/packages/d4/9f/09c734833e69badd7e3faed806b4372bd6564ae0946bd250d5239885914f/siphash24-1.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88c1a55ff82b127c5d3b96927a430d8859e6a98846a5b979833ac790682dd91b", size = 104044, upload-time = "2025-09-02T20:41:05.505Z" }, + { url = "https://files.pythonhosted.org/packages/24/30/56a26d9141a34433da221f732599e2b23d2d70a966c249a9f00feb9a2915/siphash24-1.8-cp311-cp311-win32.whl", hash = "sha256:9430255e6a1313470f52c07c4a4643c451a5b2853f6d4008e4dda05cafb6ce7c", size = 62196, upload-time = "2025-09-02T20:41:07.299Z" }, + { url = "https://files.pythonhosted.org/packages/47/b2/11b0ae63fd374652544e1b12f72ba2cc3fe6c93c1483bd8ff6935b0a8a4b/siphash24-1.8-cp311-cp311-win_amd64.whl", hash = "sha256:1e4b37e4ef0b4496169adce2a58b6c3f230b5852dfa5f7ad0b2d664596409e47", size = 77162, upload-time = "2025-09-02T20:41:08.878Z" }, + { url = "https://files.pythonhosted.org/packages/7f/82/ce3545ce8052ac7ca104b183415a27ec3335e5ed51978fdd7b433f3cfe5b/siphash24-1.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:df5ed437c6e6cc96196b38728e57cd30b0427df45223475a90e173f5015ef5ba", size = 78136, upload-time = "2025-09-02T20:41:10.083Z" }, + { url = "https://files.pythonhosted.org/packages/15/88/896c3b91bc9deb78c415448b1db67343917f35971a9e23a5967a9d323b8a/siphash24-1.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f4ef78abdf811325c7089a35504df339c48c0007d4af428a044431d329721e56", size = 74588, upload-time = "2025-09-02T20:41:11.251Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/8dad3f5601db485ba862e1c1f91a5d77fb563650856a6708e9acb40ee53c/siphash24-1.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:065eff55c4fefb3a29fd26afb2c072abf7f668ffd53b91d41f92a1c485fcbe5c", size = 98655, upload-time = "2025-09-02T20:41:12.45Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cc/e0c352624c1f2faad270aeb5cce6e173977ef66b9b5e918aa6f32af896bf/siphash24-1.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6fa84ebfd47677262aa0bcb0f5a70f796f5fc5704b287ee1b65a3bd4fb7a5d", size = 103217, upload-time = "2025-09-02T20:41:13.746Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f6/0b1675bea4d40affcae642d9c7337702a4138b93c544230280712403e968/siphash24-1.8-cp312-cp312-win32.whl", hash = "sha256:6582f73615552ca055e51e03cb02a28e570a641a7f500222c86c2d811b5037eb", size = 63114, upload-time = "2025-09-02T20:41:14.972Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/afefef85d72ed8b5cf1aa9283f712e3cd43c9682fabbc809dec54baa8452/siphash24-1.8-cp312-cp312-win_amd64.whl", hash = "sha256:44ea6d794a7cbe184e1e1da2df81c5ebb672ab3867935c3e87c08bb0c2fa4879", size = 76232, upload-time = "2025-09-02T20:41:16.112Z" }, ] [[package]] @@ -4833,9 +4838,9 @@ wheels = [ [[package]] name = "spidev" -version = "3.7" +version = "3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/99/dd50af8200e224ce9412ad01cdbeeb5b39b2d61acd72138f2b92c4a6d619/spidev-3.7.tar.gz", hash = "sha256:ce628a5ff489f45132679879bff5f455a66abf9751af01843850155b06ae92f0", size = 11616, upload-time = "2025-05-06T14:23:30.783Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/87/039b6eeea781598015b538691bc174cc0bf77df9d4d2d3b8bf9245c0de8c/spidev-3.8.tar.gz", hash = "sha256:2bc02fb8c6312d519ebf1f4331067427c0921d3f77b8bcaf05189a2e8b8382c0", size = 13893, upload-time = "2025-09-15T18:56:20.672Z" } [[package]] name = "sympy" @@ -4872,14 +4877,14 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.4.20250809" +version = "2.32.4.20250913" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/b0/9355adb86ec84d057fea765e4c49cce592aaf3d5117ce5609a95a7fc3dac/types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3", size = 23027, upload-time = "2025-08-09T03:17:10.664Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/6f/ec0012be842b1d888d46884ac5558fd62aeae1f0ec4f7a581433d890d4b5/types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163", size = 20644, upload-time = "2025-08-09T03:17:09.716Z" }, + { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" }, ] [[package]] @@ -4976,7 +4981,7 @@ name = "yapf" version = "0.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "platformdirs" }, ] sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload-time = "2024-11-14T00:11:41.584Z" } wheels = [ @@ -5033,42 +5038,42 @@ wheels = [ [[package]] name = "zstandard" -version = "0.24.0" +version = "0.25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/1b/c20b2ef1d987627765dcd5bf1dadb8ef6564f00a87972635099bb76b7a05/zstandard-0.24.0.tar.gz", hash = "sha256:fe3198b81c00032326342d973e526803f183f97aa9e9a98e3f897ebafe21178f", size = 905681, upload-time = "2025-08-17T18:36:36.352Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/1f/5c72806f76043c0ef9191a2b65281dacdf3b65b0828eb13bb2c987c4fb90/zstandard-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:addfc23e3bd5f4b6787b9ca95b2d09a1a67ad5a3c318daaa783ff90b2d3a366e", size = 795228, upload-time = "2025-08-17T18:21:46.978Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ba/3059bd5cd834666a789251d14417621b5c61233bd46e7d9023ea8bc1043a/zstandard-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b005bcee4be9c3984b355336283afe77b2defa76ed6b89332eced7b6fa68b68", size = 640520, upload-time = "2025-08-17T18:21:48.162Z" }, - { url = "https://files.pythonhosted.org/packages/57/07/f0e632bf783f915c1fdd0bf68614c4764cae9dd46ba32cbae4dd659592c3/zstandard-0.24.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:3f96a9130171e01dbb6c3d4d9925d604e2131a97f540e223b88ba45daf56d6fb", size = 5347682, upload-time = "2025-08-17T18:21:50.266Z" }, - { url = "https://files.pythonhosted.org/packages/a6/4c/63523169fe84773a7462cd090b0989cb7c7a7f2a8b0a5fbf00009ba7d74d/zstandard-0.24.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd0d3d16e63873253bad22b413ec679cf6586e51b5772eb10733899832efec42", size = 5057650, upload-time = "2025-08-17T18:21:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/c6/16/49013f7ef80293f5cebf4c4229535a9f4c9416bbfd238560edc579815dbe/zstandard-0.24.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b7a8c30d9bf4bd5e4dcfe26900bef0fcd9749acde45cdf0b3c89e2052fda9a13", size = 5404893, upload-time = "2025-08-17T18:21:54.54Z" }, - { url = "https://files.pythonhosted.org/packages/4d/38/78e8bcb5fc32a63b055f2b99e0be49b506f2351d0180173674f516cf8a7a/zstandard-0.24.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:52cd7d9fa0a115c9446abb79b06a47171b7d916c35c10e0c3aa6f01d57561382", size = 5452389, upload-time = "2025-08-17T18:21:56.822Z" }, - { url = "https://files.pythonhosted.org/packages/55/8a/81671f05619edbacd49bd84ce6899a09fc8299be20c09ae92f6618ccb92d/zstandard-0.24.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0f6fc2ea6e07e20df48752e7700e02e1892c61f9a6bfbacaf2c5b24d5ad504b", size = 5558888, upload-time = "2025-08-17T18:21:58.68Z" }, - { url = "https://files.pythonhosted.org/packages/49/cc/e83feb2d7d22d1f88434defbaeb6e5e91f42a4f607b5d4d2d58912b69d67/zstandard-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e46eb6702691b24ddb3e31e88b4a499e31506991db3d3724a85bd1c5fc3cfe4e", size = 5048038, upload-time = "2025-08-17T18:22:00.642Z" }, - { url = "https://files.pythonhosted.org/packages/08/c3/7a5c57ff49ef8943877f85c23368c104c2aea510abb339a2dc31ad0a27c3/zstandard-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5e3b9310fd7f0d12edc75532cd9a56da6293840c84da90070d692e0bb15f186", size = 5573833, upload-time = "2025-08-17T18:22:02.402Z" }, - { url = "https://files.pythonhosted.org/packages/f9/00/64519983cd92535ba4bdd4ac26ac52db00040a52d6c4efb8d1764abcc343/zstandard-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76cdfe7f920738ea871f035568f82bad3328cbc8d98f1f6988264096b5264efd", size = 4961072, upload-time = "2025-08-17T18:22:04.384Z" }, - { url = "https://files.pythonhosted.org/packages/72/ab/3a08a43067387d22994fc87c3113636aa34ccd2914a4d2d188ce365c5d85/zstandard-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f2fe35ec84908dddf0fbf66b35d7c2878dbe349552dd52e005c755d3493d61c", size = 5268462, upload-time = "2025-08-17T18:22:06.095Z" }, - { url = "https://files.pythonhosted.org/packages/49/cf/2abb3a1ad85aebe18c53e7eca73223f1546ddfa3bf4d2fb83fc5a064c5ca/zstandard-0.24.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:aa705beb74ab116563f4ce784fa94771f230c05d09ab5de9c397793e725bb1db", size = 5443319, upload-time = "2025-08-17T18:22:08.572Z" }, - { url = "https://files.pythonhosted.org/packages/40/42/0dd59fc2f68f1664cda11c3b26abdf987f4e57cb6b6b0f329520cd074552/zstandard-0.24.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:aadf32c389bb7f02b8ec5c243c38302b92c006da565e120dfcb7bf0378f4f848", size = 5822355, upload-time = "2025-08-17T18:22:10.537Z" }, - { url = "https://files.pythonhosted.org/packages/99/c0/ea4e640fd4f7d58d6f87a1e7aca11fb886ac24db277fbbb879336c912f63/zstandard-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e40cd0fc734aa1d4bd0e7ad102fd2a1aefa50ce9ef570005ffc2273c5442ddc3", size = 5365257, upload-time = "2025-08-17T18:22:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/27/a9/92da42a5c4e7e4003271f2e1f0efd1f37cfd565d763ad3604e9597980a1c/zstandard-0.24.0-cp311-cp311-win32.whl", hash = "sha256:cda61c46343809ecda43dc620d1333dd7433a25d0a252f2dcc7667f6331c7b61", size = 435559, upload-time = "2025-08-17T18:22:17.29Z" }, - { url = "https://files.pythonhosted.org/packages/e2/8e/2c8e5c681ae4937c007938f954a060fa7c74f36273b289cabdb5ef0e9a7e/zstandard-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b95fc06489aa9388400d1aab01a83652bc040c9c087bd732eb214909d7fb0dd", size = 505070, upload-time = "2025-08-17T18:22:14.808Z" }, - { url = "https://files.pythonhosted.org/packages/52/10/a2f27a66bec75e236b575c9f7b0d7d37004a03aa2dcde8e2decbe9ed7b4d/zstandard-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad9fd176ff6800a0cf52bcf59c71e5de4fa25bf3ba62b58800e0f84885344d34", size = 461507, upload-time = "2025-08-17T18:22:15.964Z" }, - { url = "https://files.pythonhosted.org/packages/26/e9/0bd281d9154bba7fc421a291e263911e1d69d6951aa80955b992a48289f6/zstandard-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2bda8f2790add22773ee7a4e43c90ea05598bffc94c21c40ae0a9000b0133c3", size = 795710, upload-time = "2025-08-17T18:22:19.189Z" }, - { url = "https://files.pythonhosted.org/packages/36/26/b250a2eef515caf492e2d86732e75240cdac9d92b04383722b9753590c36/zstandard-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cc76de75300f65b8eb574d855c12518dc25a075dadb41dd18f6322bda3fe15d5", size = 640336, upload-time = "2025-08-17T18:22:20.466Z" }, - { url = "https://files.pythonhosted.org/packages/79/bf/3ba6b522306d9bf097aac8547556b98a4f753dc807a170becaf30dcd6f01/zstandard-0.24.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d2b3b4bda1a025b10fe0269369475f420177f2cb06e0f9d32c95b4873c9f80b8", size = 5342533, upload-time = "2025-08-17T18:22:22.326Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ec/22bc75bf054e25accdf8e928bc68ab36b4466809729c554ff3a1c1c8bce6/zstandard-0.24.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b84c6c210684286e504022d11ec294d2b7922d66c823e87575d8b23eba7c81f", size = 5062837, upload-time = "2025-08-17T18:22:24.416Z" }, - { url = "https://files.pythonhosted.org/packages/48/cc/33edfc9d286e517fb5b51d9c3210e5bcfce578d02a675f994308ca587ae1/zstandard-0.24.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c59740682a686bf835a1a4d8d0ed1eefe31ac07f1c5a7ed5f2e72cf577692b00", size = 5393855, upload-time = "2025-08-17T18:22:26.786Z" }, - { url = "https://files.pythonhosted.org/packages/73/36/59254e9b29da6215fb3a717812bf87192d89f190f23817d88cb8868c47ac/zstandard-0.24.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6324fde5cf5120fbf6541d5ff3c86011ec056e8d0f915d8e7822926a5377193a", size = 5451058, upload-time = "2025-08-17T18:22:28.885Z" }, - { url = "https://files.pythonhosted.org/packages/9a/c7/31674cb2168b741bbbe71ce37dd397c9c671e73349d88ad3bca9e9fae25b/zstandard-0.24.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51a86bd963de3f36688553926a84e550d45d7f9745bd1947d79472eca27fcc75", size = 5546619, upload-time = "2025-08-17T18:22:31.115Z" }, - { url = "https://files.pythonhosted.org/packages/e6/01/1a9f22239f08c00c156f2266db857545ece66a6fc0303d45c298564bc20b/zstandard-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d82ac87017b734f2fb70ff93818c66f0ad2c3810f61040f077ed38d924e19980", size = 5046676, upload-time = "2025-08-17T18:22:33.077Z" }, - { url = "https://files.pythonhosted.org/packages/a7/91/6c0cf8fa143a4988a0361380ac2ef0d7cb98a374704b389fbc38b5891712/zstandard-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92ea7855d5bcfb386c34557516c73753435fb2d4a014e2c9343b5f5ba148b5d8", size = 5576381, upload-time = "2025-08-17T18:22:35.391Z" }, - { url = "https://files.pythonhosted.org/packages/e2/77/1526080e22e78871e786ccf3c84bf5cec9ed25110a9585507d3c551da3d6/zstandard-0.24.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3adb4b5414febf074800d264ddf69ecade8c658837a83a19e8ab820e924c9933", size = 4953403, upload-time = "2025-08-17T18:22:37.266Z" }, - { url = "https://files.pythonhosted.org/packages/6e/d0/a3a833930bff01eab697eb8abeafb0ab068438771fa066558d96d7dafbf9/zstandard-0.24.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6374feaf347e6b83ec13cc5dcfa70076f06d8f7ecd46cc71d58fac798ff08b76", size = 5267396, upload-time = "2025-08-17T18:22:39.757Z" }, - { url = "https://files.pythonhosted.org/packages/f3/5e/90a0db9a61cd4769c06374297ecfcbbf66654f74cec89392519deba64d76/zstandard-0.24.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:13fc548e214df08d896ee5f29e1f91ee35db14f733fef8eabea8dca6e451d1e2", size = 5433269, upload-time = "2025-08-17T18:22:42.131Z" }, - { url = "https://files.pythonhosted.org/packages/ce/58/fc6a71060dd67c26a9c5566e0d7c99248cbe5abfda6b3b65b8f1a28d59f7/zstandard-0.24.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0a416814608610abf5488889c74e43ffa0343ca6cf43957c6b6ec526212422da", size = 5814203, upload-time = "2025-08-17T18:22:44.017Z" }, - { url = "https://files.pythonhosted.org/packages/5c/6a/89573d4393e3ecbfa425d9a4e391027f58d7810dec5cdb13a26e4cdeef5c/zstandard-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0d66da2649bb0af4471699aeb7a83d6f59ae30236fb9f6b5d20fb618ef6c6777", size = 5359622, upload-time = "2025-08-17T18:22:45.802Z" }, - { url = "https://files.pythonhosted.org/packages/60/ff/2cbab815d6f02a53a9d8d8703bc727d8408a2e508143ca9af6c3cca2054b/zstandard-0.24.0-cp312-cp312-win32.whl", hash = "sha256:ff19efaa33e7f136fe95f9bbcc90ab7fb60648453b03f95d1de3ab6997de0f32", size = 435968, upload-time = "2025-08-17T18:22:49.493Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a3/8f96b8ddb7ad12344218fbd0fd2805702dafd126ae9f8a1fb91eef7b33da/zstandard-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc05f8a875eb651d1cc62e12a4a0e6afa5cd0cc231381adb830d2e9c196ea895", size = 505195, upload-time = "2025-08-17T18:22:47.193Z" }, - { url = "https://files.pythonhosted.org/packages/a3/4a/bfca20679da63bfc236634ef2e4b1b4254203098b0170e3511fee781351f/zstandard-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:b04c94718f7a8ed7cdd01b162b6caa1954b3c9d486f00ecbbd300f149d2b2606", size = 461605, upload-time = "2025-08-17T18:22:48.317Z" }, + { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, + { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, + { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, + { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, + { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, + { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, + { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, + { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, + { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, + { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, + { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, + { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, + { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, ] From 61d5a5053407c15e801303062bfa5ac1d9eec027 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 21 Sep 2025 22:44:14 -0700 Subject: [PATCH 036/341] Revert "fix `is_dirty` when switching branch with `updated` (#36162)" This reverts commit 30c388aea8961113555211201c6298099d53e4df. --- system/updated/updated.py | 2 -- system/version.py | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/system/updated/updated.py b/system/updated/updated.py index f9fad5f6f5..a80a663ec9 100755 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -113,7 +113,6 @@ def setup_git_options(cwd: str) -> None: ("protocol.version", "2"), ("gc.auto", "0"), ("gc.autoDetach", "false"), - ("remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"), ] for option, value in git_cfg: run(["git", "config", option, value], cwd) @@ -390,7 +389,6 @@ class Updater: cloudlog.info("git reset in progress") cmds = [ ["git", "checkout", "--force", "--no-recurse-submodules", "-B", branch, "FETCH_HEAD"], - ["git", "branch", "--set-upstream-to", f"origin/{branch}"], ["git", "reset", "--hard"], ["git", "clean", "-xdff"], ["git", "submodule", "sync"], diff --git a/system/version.py b/system/version.py index e32c3d6033..5e4fcf5c33 100755 --- a/system/version.py +++ b/system/version.py @@ -37,7 +37,9 @@ def is_prebuilt(path: str = BASEDIR) -> bool: @cache def is_dirty(cwd: str = BASEDIR) -> bool: - if not get_origin() or not get_short_branch(): + origin = get_origin() + branch = get_branch() + if not origin or not branch: return True dirty = False @@ -50,9 +52,6 @@ def is_dirty(cwd: str = BASEDIR) -> bool: except subprocess.CalledProcessError: pass - branch = get_branch() - if not branch: - return True dirty = (subprocess.call(["git", "diff-index", "--quiet", branch, "--"], cwd=cwd)) != 0 except subprocess.CalledProcessError: cloudlog.exception("git subprocess failed while checking dirty") From 073503a6f203b8dde08ff13ac1cd3891835b09c2 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 21 Sep 2025 23:53:07 -0700 Subject: [PATCH 037/341] fix `is_dirty` when fetching branch with `updated` (#36187) fix is_dirty --- system/updated/updated.py | 3 +++ system/version.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/system/updated/updated.py b/system/updated/updated.py index a80a663ec9..a4a1f8f34f 100755 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -382,6 +382,8 @@ class Updater: setup_git_options(OVERLAY_MERGED) + run(["git", "config", "--replace-all", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"], OVERLAY_MERGED) + branch = self.target_branch git_fetch_output = run(["git", "fetch", "origin", branch], OVERLAY_MERGED) cloudlog.info("git fetch success: %s", git_fetch_output) @@ -389,6 +391,7 @@ class Updater: cloudlog.info("git reset in progress") cmds = [ ["git", "checkout", "--force", "--no-recurse-submodules", "-B", branch, "FETCH_HEAD"], + ["git", "branch", "--set-upstream-to", f"origin/{branch}"], ["git", "reset", "--hard"], ["git", "clean", "-xdff"], ["git", "submodule", "sync"], diff --git a/system/version.py b/system/version.py index 5e4fcf5c33..e32c3d6033 100755 --- a/system/version.py +++ b/system/version.py @@ -37,9 +37,7 @@ def is_prebuilt(path: str = BASEDIR) -> bool: @cache def is_dirty(cwd: str = BASEDIR) -> bool: - origin = get_origin() - branch = get_branch() - if not origin or not branch: + if not get_origin() or not get_short_branch(): return True dirty = False @@ -52,6 +50,9 @@ def is_dirty(cwd: str = BASEDIR) -> bool: except subprocess.CalledProcessError: pass + branch = get_branch() + if not branch: + return True dirty = (subprocess.call(["git", "diff-index", "--quiet", branch, "--"], cwd=cwd)) != 0 except subprocess.CalledProcessError: cloudlog.exception("git subprocess failed while checking dirty") From cd335623791d78e73994d8c5237a65629c2201b6 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:43:26 -0700 Subject: [PATCH 038/341] [bot] Update Python packages (#36188) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- tinygrad_repo | 2 +- uv.lock | 128 +++++++++++++++++++++++++------------------------- 3 files changed, 67 insertions(+), 65 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index c70bd060c6..6894a012cd 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit c70bd060c6a410c1083186a1e4165e43a4eda0df +Subproject commit 6894a012cd7ec36678b8abd3d95dc79975d5dbe2 diff --git a/tinygrad_repo b/tinygrad_repo index 73c8dae60d..a6fd96f620 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 73c8dae60d0eaa3a27a252f7967d2dab8378109e +Subproject commit a6fd96f62050efd4a2fe7c885d1a11f87e3c5b0a diff --git a/uv.lock b/uv.lock index c24d9dbbbe..9c791b8110 100644 --- a/uv.lock +++ b/uv.lock @@ -798,48 +798,50 @@ wheels = [ [[package]] name = "lxml" -version = "6.0.1" +version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8f/bd/f9d01fd4132d81c6f43ab01983caea69ec9614b913c290a26738431a015d/lxml-6.0.1.tar.gz", hash = "sha256:2b3a882ebf27dd026df3801a87cf49ff791336e0f94b0fad195db77e01240690", size = 4070214, upload-time = "2025-08-22T10:37:53.525Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/c8/262c1d19339ef644cdc9eb5aad2e85bd2d1fa2d7c71cdef3ede1a3eed84d/lxml-6.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6acde83f7a3d6399e6d83c1892a06ac9b14ea48332a5fbd55d60b9897b9570a", size = 8422719, upload-time = "2025-08-22T10:32:24.848Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d4/1b0afbeb801468a310642c3a6f6704e53c38a4a6eb1ca6faea013333e02f/lxml-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d21c9cacb6a889cbb8eeb46c77ef2c1dd529cde10443fdeb1de847b3193c541", size = 4575763, upload-time = "2025-08-22T10:32:27.057Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c1/8db9b5402bf52ceb758618313f7423cd54aea85679fcf607013707d854a8/lxml-6.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:847458b7cd0d04004895f1fb2cca8e7c0f8ec923c49c06b7a72ec2d48ea6aca2", size = 4943244, upload-time = "2025-08-22T10:32:28.847Z" }, - { url = "https://files.pythonhosted.org/packages/e7/78/838e115358dd2369c1c5186080dd874a50a691fb5cd80db6afe5e816e2c6/lxml-6.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1dc13405bf315d008fe02b1472d2a9d65ee1c73c0a06de5f5a45e6e404d9a1c0", size = 5081725, upload-time = "2025-08-22T10:32:30.666Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b6/bdcb3a3ddd2438c5b1a1915161f34e8c85c96dc574b0ef3be3924f36315c/lxml-6.0.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f540c229a8c0a770dcaf6d5af56a5295e0fc314fc7ef4399d543328054bcea", size = 5021238, upload-time = "2025-08-22T10:32:32.49Z" }, - { url = "https://files.pythonhosted.org/packages/73/e5/1bfb96185dc1a64c7c6fbb7369192bda4461952daa2025207715f9968205/lxml-6.0.1-cp311-cp311-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d2f73aef768c70e8deb8c4742fca4fd729b132fda68458518851c7735b55297e", size = 5343744, upload-time = "2025-08-22T10:32:34.385Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ae/df3ea9ebc3c493b9c6bdc6bd8c554ac4e147f8d7839993388aab57ec606d/lxml-6.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7f4066b85a4fa25ad31b75444bd578c3ebe6b8ed47237896341308e2ce923c3", size = 5223477, upload-time = "2025-08-22T10:32:36.256Z" }, - { url = "https://files.pythonhosted.org/packages/37/b3/65e1e33600542c08bc03a4c5c9c306c34696b0966a424a3be6ffec8038ed/lxml-6.0.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0cce65db0cd8c750a378639900d56f89f7d6af11cd5eda72fde054d27c54b8ce", size = 4676626, upload-time = "2025-08-22T10:32:38.793Z" }, - { url = "https://files.pythonhosted.org/packages/7a/46/ee3ed8f3a60e9457d7aea46542d419917d81dbfd5700fe64b2a36fb5ef61/lxml-6.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c372d42f3eee5844b69dcab7b8d18b2f449efd54b46ac76970d6e06b8e8d9a66", size = 5066042, upload-time = "2025-08-22T10:32:41.134Z" }, - { url = "https://files.pythonhosted.org/packages/9c/b9/8394538e7cdbeb3bfa36bc74924be1a4383e0bb5af75f32713c2c4aa0479/lxml-6.0.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2e2b0e042e1408bbb1c5f3cfcb0f571ff4ac98d8e73f4bf37c5dd179276beedd", size = 4724714, upload-time = "2025-08-22T10:32:43.94Z" }, - { url = "https://files.pythonhosted.org/packages/b3/21/3ef7da1ea2a73976c1a5a311d7cde5d379234eec0968ee609517714940b4/lxml-6.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cc73bb8640eadd66d25c5a03175de6801f63c535f0f3cf50cac2f06a8211f420", size = 5247376, upload-time = "2025-08-22T10:32:46.263Z" }, - { url = "https://files.pythonhosted.org/packages/26/7d/0980016f124f00c572cba6f4243e13a8e80650843c66271ee692cddf25f3/lxml-6.0.1-cp311-cp311-win32.whl", hash = "sha256:7c23fd8c839708d368e406282d7953cee5134f4592ef4900026d84566d2b4c88", size = 3609499, upload-time = "2025-08-22T10:32:48.156Z" }, - { url = "https://files.pythonhosted.org/packages/b1/08/28440437521f265eff4413eb2a65efac269c4c7db5fd8449b586e75d8de2/lxml-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:2516acc6947ecd3c41a4a4564242a87c6786376989307284ddb115f6a99d927f", size = 4036003, upload-time = "2025-08-22T10:32:50.662Z" }, - { url = "https://files.pythonhosted.org/packages/7b/dc/617e67296d98099213a505d781f04804e7b12923ecd15a781a4ab9181992/lxml-6.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:cb46f8cfa1b0334b074f40c0ff94ce4d9a6755d492e6c116adb5f4a57fb6ad96", size = 3679662, upload-time = "2025-08-22T10:32:52.739Z" }, - { url = "https://files.pythonhosted.org/packages/b0/a9/82b244c8198fcdf709532e39a1751943a36b3e800b420adc739d751e0299/lxml-6.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c03ac546adaabbe0b8e4a15d9ad815a281afc8d36249c246aecf1aaad7d6f200", size = 8422788, upload-time = "2025-08-22T10:32:56.612Z" }, - { url = "https://files.pythonhosted.org/packages/c9/8d/1ed2bc20281b0e7ed3e6c12b0a16e64ae2065d99be075be119ba88486e6d/lxml-6.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33b862c7e3bbeb4ba2c96f3a039f925c640eeba9087a4dc7a572ec0f19d89392", size = 4593547, upload-time = "2025-08-22T10:32:59.016Z" }, - { url = "https://files.pythonhosted.org/packages/76/53/d7fd3af95b72a3493bf7fbe842a01e339d8f41567805cecfecd5c71aa5ee/lxml-6.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7a3ec1373f7d3f519de595032d4dcafae396c29407cfd5073f42d267ba32440d", size = 4948101, upload-time = "2025-08-22T10:33:00.765Z" }, - { url = "https://files.pythonhosted.org/packages/9d/51/4e57cba4d55273c400fb63aefa2f0d08d15eac021432571a7eeefee67bed/lxml-6.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03b12214fb1608f4cffa181ec3d046c72f7e77c345d06222144744c122ded870", size = 5108090, upload-time = "2025-08-22T10:33:03.108Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6e/5f290bc26fcc642bc32942e903e833472271614e24d64ad28aaec09d5dae/lxml-6.0.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:207ae0d5f0f03b30f95e649a6fa22aa73f5825667fee9c7ec6854d30e19f2ed8", size = 5021791, upload-time = "2025-08-22T10:33:06.972Z" }, - { url = "https://files.pythonhosted.org/packages/13/d4/2e7551a86992ece4f9a0f6eebd4fb7e312d30f1e372760e2109e721d4ce6/lxml-6.0.1-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:32297b09ed4b17f7b3f448de87a92fb31bb8747496623483788e9f27c98c0f00", size = 5358861, upload-time = "2025-08-22T10:33:08.967Z" }, - { url = "https://files.pythonhosted.org/packages/8a/5f/cb49d727fc388bf5fd37247209bab0da11697ddc5e976ccac4826599939e/lxml-6.0.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e18224ea241b657a157c85e9cac82c2b113ec90876e01e1f127312006233756", size = 5652569, upload-time = "2025-08-22T10:33:10.815Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b8/66c1ef8c87ad0f958b0a23998851e610607c74849e75e83955d5641272e6/lxml-6.0.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a07a994d3c46cd4020c1ea566345cf6815af205b1e948213a4f0f1d392182072", size = 5252262, upload-time = "2025-08-22T10:33:12.673Z" }, - { url = "https://files.pythonhosted.org/packages/1a/ef/131d3d6b9590e64fdbb932fbc576b81fcc686289da19c7cb796257310e82/lxml-6.0.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:2287fadaa12418a813b05095485c286c47ea58155930cfbd98c590d25770e225", size = 4710309, upload-time = "2025-08-22T10:33:14.952Z" }, - { url = "https://files.pythonhosted.org/packages/bc/3f/07f48ae422dce44902309aa7ed386c35310929dc592439c403ec16ef9137/lxml-6.0.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b4e597efca032ed99f418bd21314745522ab9fa95af33370dcee5533f7f70136", size = 5265786, upload-time = "2025-08-22T10:33:16.721Z" }, - { url = "https://files.pythonhosted.org/packages/11/c7/125315d7b14ab20d9155e8316f7d287a4956098f787c22d47560b74886c4/lxml-6.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9696d491f156226decdd95d9651c6786d43701e49f32bf23715c975539aa2b3b", size = 5062272, upload-time = "2025-08-22T10:33:18.478Z" }, - { url = "https://files.pythonhosted.org/packages/8b/c3/51143c3a5fc5168a7c3ee626418468ff20d30f5a59597e7b156c1e61fba8/lxml-6.0.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e4e3cd3585f3c6f87cdea44cda68e692cc42a012f0131d25957ba4ce755241a7", size = 4786955, upload-time = "2025-08-22T10:33:20.34Z" }, - { url = "https://files.pythonhosted.org/packages/11/86/73102370a420ec4529647b31c4a8ce8c740c77af3a5fae7a7643212d6f6e/lxml-6.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:45cbc92f9d22c28cd3b97f8d07fcefa42e569fbd587dfdac76852b16a4924277", size = 5673557, upload-time = "2025-08-22T10:33:22.282Z" }, - { url = "https://files.pythonhosted.org/packages/d7/2d/aad90afaec51029aef26ef773b8fd74a9e8706e5e2f46a57acd11a421c02/lxml-6.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f8c9bcfd2e12299a442fba94459adf0b0d001dbc68f1594439bfa10ad1ecb74b", size = 5254211, upload-time = "2025-08-22T10:33:24.15Z" }, - { url = "https://files.pythonhosted.org/packages/63/01/c9e42c8c2d8b41f4bdefa42ab05448852e439045f112903dd901b8fbea4d/lxml-6.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e9dc2b9f1586e7cd77753eae81f8d76220eed9b768f337dc83a3f675f2f0cf9", size = 5275817, upload-time = "2025-08-22T10:33:26.007Z" }, - { url = "https://files.pythonhosted.org/packages/bc/1f/962ea2696759abe331c3b0e838bb17e92224f39c638c2068bf0d8345e913/lxml-6.0.1-cp312-cp312-win32.whl", hash = "sha256:987ad5c3941c64031f59c226167f55a04d1272e76b241bfafc968bdb778e07fb", size = 3610889, upload-time = "2025-08-22T10:33:28.169Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/22c86a990b51b44442b75c43ecb2f77b8daba8c4ba63696921966eac7022/lxml-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:abb05a45394fd76bf4a60c1b7bec0e6d4e8dfc569fc0e0b1f634cd983a006ddc", size = 4010925, upload-time = "2025-08-22T10:33:29.874Z" }, - { url = "https://files.pythonhosted.org/packages/b2/21/dc0c73325e5eb94ef9c9d60dbb5dcdcb2e7114901ea9509735614a74e75a/lxml-6.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:c4be29bce35020d8579d60aa0a4e95effd66fcfce31c46ffddf7e5422f73a299", size = 3671922, upload-time = "2025-08-22T10:33:31.535Z" }, - { url = "https://files.pythonhosted.org/packages/41/37/41961f53f83ded57b37e65e4f47d1c6c6ef5fd02cb1d6ffe028ba0efa7d4/lxml-6.0.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b556aaa6ef393e989dac694b9c95761e32e058d5c4c11ddeef33f790518f7a5e", size = 3903412, upload-time = "2025-08-22T10:37:40.758Z" }, - { url = "https://files.pythonhosted.org/packages/3d/47/8631ea73f3dc776fb6517ccde4d5bd5072f35f9eacbba8c657caa4037a69/lxml-6.0.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:64fac7a05ebb3737b79fd89fe5a5b6c5546aac35cfcfd9208eb6e5d13215771c", size = 4224810, upload-time = "2025-08-22T10:37:42.839Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b8/39ae30ca3b1516729faeef941ed84bf8f12321625f2644492ed8320cb254/lxml-6.0.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:038d3c08babcfce9dc89aaf498e6da205efad5b7106c3b11830a488d4eadf56b", size = 4329221, upload-time = "2025-08-22T10:37:45.223Z" }, - { url = "https://files.pythonhosted.org/packages/9c/ea/048dea6cdfc7a72d40ae8ed7e7d23cf4a6b6a6547b51b492a3be50af0e80/lxml-6.0.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:445f2cee71c404ab4259bc21e20339a859f75383ba2d7fb97dfe7c163994287b", size = 4270228, upload-time = "2025-08-22T10:37:47.276Z" }, - { url = "https://files.pythonhosted.org/packages/6b/d4/c2b46e432377c45d611ae2f669aa47971df1586c1a5240675801d0f02bac/lxml-6.0.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e352d8578e83822d70bea88f3d08b9912528e4c338f04ab707207ab12f4b7aac", size = 4416077, upload-time = "2025-08-22T10:37:49.822Z" }, - { url = "https://files.pythonhosted.org/packages/b6/db/8f620f1ac62cf32554821b00b768dd5957ac8e3fd051593532be5b40b438/lxml-6.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:51bd5d1a9796ca253db6045ab45ca882c09c071deafffc22e06975b7ace36300", size = 3518127, upload-time = "2025-08-22T10:37:51.66Z" }, + { url = "https://files.pythonhosted.org/packages/77/d5/becbe1e2569b474a23f0c672ead8a29ac50b2dc1d5b9de184831bda8d14c/lxml-6.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:13e35cbc684aadf05d8711a5d1b5857c92e5e580efa9a0d2be197199c8def607", size = 8634365, upload-time = "2025-09-22T04:00:45.672Z" }, + { url = "https://files.pythonhosted.org/packages/28/66/1ced58f12e804644426b85d0bb8a4478ca77bc1761455da310505f1a3526/lxml-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b1675e096e17c6fe9c0e8c81434f5736c0739ff9ac6123c87c2d452f48fc938", size = 4650793, upload-time = "2025-09-22T04:00:47.783Z" }, + { url = "https://files.pythonhosted.org/packages/11/84/549098ffea39dfd167e3f174b4ce983d0eed61f9d8d25b7bf2a57c3247fc/lxml-6.0.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac6e5811ae2870953390452e3476694196f98d447573234592d30488147404d", size = 4944362, upload-time = "2025-09-22T04:00:49.845Z" }, + { url = "https://files.pythonhosted.org/packages/ac/bd/f207f16abf9749d2037453d56b643a7471d8fde855a231a12d1e095c4f01/lxml-6.0.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5aa0fc67ae19d7a64c3fe725dc9a1bb11f80e01f78289d05c6f62545affec438", size = 5083152, upload-time = "2025-09-22T04:00:51.709Z" }, + { url = "https://files.pythonhosted.org/packages/15/ae/bd813e87d8941d52ad5b65071b1affb48da01c4ed3c9c99e40abb266fbff/lxml-6.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de496365750cc472b4e7902a485d3f152ecf57bd3ba03ddd5578ed8ceb4c5964", size = 5023539, upload-time = "2025-09-22T04:00:53.593Z" }, + { url = "https://files.pythonhosted.org/packages/02/cd/9bfef16bd1d874fbe0cb51afb00329540f30a3283beb9f0780adbb7eec03/lxml-6.0.2-cp311-cp311-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:200069a593c5e40b8f6fc0d84d86d970ba43138c3e68619ffa234bc9bb806a4d", size = 5344853, upload-time = "2025-09-22T04:00:55.524Z" }, + { url = "https://files.pythonhosted.org/packages/b8/89/ea8f91594bc5dbb879734d35a6f2b0ad50605d7fb419de2b63d4211765cc/lxml-6.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d2de809c2ee3b888b59f995625385f74629707c9355e0ff856445cdcae682b7", size = 5225133, upload-time = "2025-09-22T04:00:57.269Z" }, + { url = "https://files.pythonhosted.org/packages/b9/37/9c735274f5dbec726b2db99b98a43950395ba3d4a1043083dba2ad814170/lxml-6.0.2-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:b2c3da8d93cf5db60e8858c17684c47d01fee6405e554fb55018dd85fc23b178", size = 4677944, upload-time = "2025-09-22T04:00:59.052Z" }, + { url = "https://files.pythonhosted.org/packages/20/28/7dfe1ba3475d8bfca3878365075abe002e05d40dfaaeb7ec01b4c587d533/lxml-6.0.2-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:442de7530296ef5e188373a1ea5789a46ce90c4847e597856570439621d9c553", size = 5284535, upload-time = "2025-09-22T04:01:01.335Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cf/5f14bc0de763498fc29510e3532bf2b4b3a1c1d5d0dff2e900c16ba021ef/lxml-6.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2593c77efde7bfea7f6389f1ab249b15ed4aa5bc5cb5131faa3b843c429fbedb", size = 5067343, upload-time = "2025-09-22T04:01:03.13Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b0/bb8275ab5472f32b28cfbbcc6db7c9d092482d3439ca279d8d6fa02f7025/lxml-6.0.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3e3cb08855967a20f553ff32d147e14329b3ae70ced6edc2f282b94afbc74b2a", size = 4725419, upload-time = "2025-09-22T04:01:05.013Z" }, + { url = "https://files.pythonhosted.org/packages/25/4c/7c222753bc72edca3b99dbadba1b064209bc8ed4ad448af990e60dcce462/lxml-6.0.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ed6c667fcbb8c19c6791bbf40b7268ef8ddf5a96940ba9404b9f9a304832f6c", size = 5275008, upload-time = "2025-09-22T04:01:07.327Z" }, + { url = "https://files.pythonhosted.org/packages/6c/8c/478a0dc6b6ed661451379447cdbec77c05741a75736d97e5b2b729687828/lxml-6.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b8f18914faec94132e5b91e69d76a5c1d7b0c73e2489ea8929c4aaa10b76bbf7", size = 5248906, upload-time = "2025-09-22T04:01:09.452Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d9/5be3a6ab2784cdf9accb0703b65e1b64fcdd9311c9f007630c7db0cfcce1/lxml-6.0.2-cp311-cp311-win32.whl", hash = "sha256:6605c604e6daa9e0d7f0a2137bdc47a2e93b59c60a65466353e37f8272f47c46", size = 3610357, upload-time = "2025-09-22T04:01:11.102Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7d/ca6fb13349b473d5732fb0ee3eec8f6c80fc0688e76b7d79c1008481bf1f/lxml-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e5867f2651016a3afd8dd2c8238baa66f1e2802f44bc17e236f547ace6647078", size = 4036583, upload-time = "2025-09-22T04:01:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a2/51363b5ecd3eab46563645f3a2c3836a2fc67d01a1b87c5017040f39f567/lxml-6.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:4197fb2534ee05fd3e7afaab5d8bfd6c2e186f65ea7f9cd6a82809c887bd1285", size = 3680591, upload-time = "2025-09-22T04:01:14.874Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c8/8ff2bc6b920c84355146cd1ab7d181bc543b89241cfb1ebee824a7c81457/lxml-6.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a59f5448ba2ceccd06995c95ea59a7674a10de0810f2ce90c9006f3cbc044456", size = 8661887, upload-time = "2025-09-22T04:01:17.265Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/9aae1008083bb501ef63284220ce81638332f9ccbfa53765b2b7502203cf/lxml-6.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e8113639f3296706fbac34a30813929e29247718e88173ad849f57ca59754924", size = 4667818, upload-time = "2025-09-22T04:01:19.688Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ca/31fb37f99f37f1536c133476674c10b577e409c0a624384147653e38baf2/lxml-6.0.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a8bef9b9825fa8bc816a6e641bb67219489229ebc648be422af695f6e7a4fa7f", size = 4950807, upload-time = "2025-09-22T04:01:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/da/87/f6cb9442e4bada8aab5ae7e1046264f62fdbeaa6e3f6211b93f4c0dd97f1/lxml-6.0.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:65ea18d710fd14e0186c2f973dc60bb52039a275f82d3c44a0e42b43440ea534", size = 5109179, upload-time = "2025-09-22T04:01:23.32Z" }, + { url = "https://files.pythonhosted.org/packages/c8/20/a7760713e65888db79bbae4f6146a6ae5c04e4a204a3c48896c408cd6ed2/lxml-6.0.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c371aa98126a0d4c739ca93ceffa0fd7a5d732e3ac66a46e74339acd4d334564", size = 5023044, upload-time = "2025-09-22T04:01:25.118Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b0/7e64e0460fcb36471899f75831509098f3fd7cd02a3833ac517433cb4f8f/lxml-6.0.2-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:700efd30c0fa1a3581d80a748157397559396090a51d306ea59a70020223d16f", size = 5359685, upload-time = "2025-09-22T04:01:27.398Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e1/e5df362e9ca4e2f48ed6411bd4b3a0ae737cc842e96877f5bf9428055ab4/lxml-6.0.2-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c33e66d44fe60e72397b487ee92e01da0d09ba2d66df8eae42d77b6d06e5eba0", size = 5654127, upload-time = "2025-09-22T04:01:29.629Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d1/232b3309a02d60f11e71857778bfcd4acbdb86c07db8260caf7d008b08f8/lxml-6.0.2-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90a345bbeaf9d0587a3aaffb7006aa39ccb6ff0e96a57286c0cb2fd1520ea192", size = 5253958, upload-time = "2025-09-22T04:01:31.535Z" }, + { url = "https://files.pythonhosted.org/packages/35/35/d955a070994725c4f7d80583a96cab9c107c57a125b20bb5f708fe941011/lxml-6.0.2-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:064fdadaf7a21af3ed1dcaa106b854077fbeada827c18f72aec9346847cd65d0", size = 4711541, upload-time = "2025-09-22T04:01:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/1e/be/667d17363b38a78c4bd63cfd4b4632029fd68d2c2dc81f25ce9eb5224dd5/lxml-6.0.2-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fbc74f42c3525ac4ffa4b89cbdd00057b6196bcefe8bce794abd42d33a018092", size = 5267426, upload-time = "2025-09-22T04:01:35.639Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/62c70aa4a1c26569bc958c9ca86af2bb4e1f614e8c04fb2989833874f7ae/lxml-6.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ddff43f702905a4e32bc24f3f2e2edfe0f8fde3277d481bffb709a4cced7a1f", size = 5064917, upload-time = "2025-09-22T04:01:37.448Z" }, + { url = "https://files.pythonhosted.org/packages/bd/55/6ceddaca353ebd0f1908ef712c597f8570cc9c58130dbb89903198e441fd/lxml-6.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6da5185951d72e6f5352166e3da7b0dc27aa70bd1090b0eb3f7f7212b53f1bb8", size = 4788795, upload-time = "2025-09-22T04:01:39.165Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e8/fd63e15da5e3fd4c2146f8bbb3c14e94ab850589beab88e547b2dbce22e1/lxml-6.0.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:57a86e1ebb4020a38d295c04fc79603c7899e0df71588043eb218722dabc087f", size = 5676759, upload-time = "2025-09-22T04:01:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/b3ec58dc5c374697f5ba37412cd2728f427d056315d124dd4b61da381877/lxml-6.0.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2047d8234fe735ab77802ce5f2297e410ff40f5238aec569ad7c8e163d7b19a6", size = 5255666, upload-time = "2025-09-22T04:01:43.363Z" }, + { url = "https://files.pythonhosted.org/packages/19/93/03ba725df4c3d72afd9596eef4a37a837ce8e4806010569bedfcd2cb68fd/lxml-6.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f91fd2b2ea15a6800c8e24418c0775a1694eefc011392da73bc6cef2623b322", size = 5277989, upload-time = "2025-09-22T04:01:45.215Z" }, + { url = "https://files.pythonhosted.org/packages/c6/80/c06de80bfce881d0ad738576f243911fccf992687ae09fd80b734712b39c/lxml-6.0.2-cp312-cp312-win32.whl", hash = "sha256:3ae2ce7d6fedfb3414a2b6c5e20b249c4c607f72cb8d2bb7cc9c6ec7c6f4e849", size = 3611456, upload-time = "2025-09-22T04:01:48.243Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d7/0cdfb6c3e30893463fb3d1e52bc5f5f99684a03c29a0b6b605cfae879cd5/lxml-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:72c87e5ee4e58a8354fb9c7c84cbf95a1c8236c127a5d1b7683f04bed8361e1f", size = 4011793, upload-time = "2025-09-22T04:01:50.042Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7b/93c73c67db235931527301ed3785f849c78991e2e34f3fd9a6663ffda4c5/lxml-6.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:61cb10eeb95570153e0c0e554f58df92ecf5109f75eacad4a95baa709e26c3d6", size = 3672836, upload-time = "2025-09-22T04:01:52.145Z" }, + { url = "https://files.pythonhosted.org/packages/0b/11/29d08bc103a62c0eba8016e7ed5aeebbf1e4312e83b0b1648dd203b0e87d/lxml-6.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1c06035eafa8404b5cf475bb37a9f6088b0aca288d4ccc9d69389750d5543700", size = 3949829, upload-time = "2025-09-22T04:04:45.608Z" }, + { url = "https://files.pythonhosted.org/packages/12/b3/52ab9a3b31e5ab8238da241baa19eec44d2ab426532441ee607165aebb52/lxml-6.0.2-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c7d13103045de1bdd6fe5d61802565f1a3537d70cd3abf596aa0af62761921ee", size = 4226277, upload-time = "2025-09-22T04:04:47.754Z" }, + { url = "https://files.pythonhosted.org/packages/a0/33/1eaf780c1baad88224611df13b1c2a9dfa460b526cacfe769103ff50d845/lxml-6.0.2-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a3c150a95fbe5ac91de323aa756219ef9cf7fde5a3f00e2281e30f33fa5fa4f", size = 4330433, upload-time = "2025-09-22T04:04:49.907Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c1/27428a2ff348e994ab4f8777d3a0ad510b6b92d37718e5887d2da99952a2/lxml-6.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60fa43be34f78bebb27812ed90f1925ec99560b0fa1decdb7d12b84d857d31e9", size = 4272119, upload-time = "2025-09-22T04:04:51.801Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d0/3020fa12bcec4ab62f97aab026d57c2f0cfd480a558758d9ca233bb6a79d/lxml-6.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21c73b476d3cfe836be731225ec3421fa2f048d84f6df6a8e70433dff1376d5a", size = 4417314, upload-time = "2025-09-22T04:04:55.024Z" }, + { url = "https://files.pythonhosted.org/packages/6c/77/d7f491cbc05303ac6801651aabeb262d43f319288c1ea96c66b1d2692ff3/lxml-6.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:27220da5be049e936c3aca06f174e8827ca6445a4353a1995584311487fc4e3e", size = 3518768, upload-time = "2025-09-22T04:04:57.097Z" }, ] [[package]] @@ -4235,11 +4237,11 @@ wheels = [ [[package]] name = "pyparsing" -version = "3.2.4" +version = "3.2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/c9/b4594e6a81371dfa9eb7a2c110ad682acf985d96115ae8b25a1d63b4bf3b/pyparsing-3.2.4.tar.gz", hash = "sha256:fff89494f45559d0f2ce46613b419f632bbb6afbdaed49696d322bcf98a58e99", size = 1098809, upload-time = "2025-09-13T05:47:19.732Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/b8/fbab973592e23ae313042d450fc26fa24282ebffba21ba373786e1ce63b4/pyparsing-3.2.4-py3-none-any.whl", hash = "sha256:91d0fcde680d42cd031daf3a6ba20da3107e08a75de50da58360e7d94ab24d36", size = 113869, upload-time = "2025-09-13T05:47:17.863Z" }, + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, ] [[package]] @@ -4626,28 +4628,28 @@ wheels = [ [[package]] name = "ruamel-yaml-clib" -version = "0.2.12" +version = "0.2.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/fd/32c91c4d301ebe7931a3c44f7593650613afe14854e3cc9d2764321b7a46/ruamel.yaml.clib-0.2.13.tar.gz", hash = "sha256:8d4f8d7853053a5a19171a0f515f1e14f503bd3e772c4da6bafe7d0893d4f299", size = 201264, upload-time = "2025-09-22T13:33:47.268Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload-time = "2024-10-20T10:12:45.162Z" }, - { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload-time = "2024-10-20T10:12:46.758Z" }, - { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload-time = "2024-10-20T10:12:48.605Z" }, - { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload-time = "2024-10-20T10:12:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload-time = "2024-10-21T11:26:41.438Z" }, - { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload-time = "2024-10-21T11:26:43.62Z" }, - { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload-time = "2024-12-11T19:58:15.592Z" }, - { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload-time = "2024-10-20T10:12:52.865Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload-time = "2024-10-20T10:12:54.652Z" }, - { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload-time = "2024-10-20T10:12:55.657Z" }, - { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload-time = "2024-10-20T10:12:57.155Z" }, - { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload-time = "2024-10-20T10:12:58.501Z" }, - { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload-time = "2024-10-20T10:13:00.211Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload-time = "2024-10-21T11:26:46.038Z" }, - { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload-time = "2024-10-21T11:26:47.487Z" }, - { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload-time = "2024-12-11T19:58:17.252Z" }, - { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload-time = "2024-10-20T10:13:01.395Z" }, - { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload-time = "2024-10-20T10:13:02.768Z" }, + { url = "https://files.pythonhosted.org/packages/bb/86/fa6be53f11d1c663a261d137d616bbb326cb937b361a55a00cacf9ba76aa/ruamel.yaml.clib-0.2.13-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:825a7483da63b9448fdad9778269a5d02e5f64040aa8326fec071e830c2fc49c", size = 136894, upload-time = "2025-09-22T13:32:56.684Z" }, + { url = "https://files.pythonhosted.org/packages/70/f1/d26dbf3c53befd01d8b341cb29c555c1e910ef29478b4ebd50d1d67fbc64/ruamel.yaml.clib-0.2.13-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:54707e2200b9e1c34a62af09edd3ea3469a722951365311398458abe36ff45d6", size = 640033, upload-time = "2025-09-22T13:33:00.01Z" }, + { url = "https://files.pythonhosted.org/packages/a8/80/119ce9e40690b7e0dfc6bec41369333593f8738fa4f58dbbffdb9b80339b/ruamel.yaml.clib-0.2.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:958b1fc6334cd745db7099a69da4f60503cfbbc05ce1412b1d06c5922cd8314b", size = 738065, upload-time = "2025-09-22T13:32:57.712Z" }, + { url = "https://files.pythonhosted.org/packages/0b/85/09625df6b3ccf0ae0ddc93c0ab4d89f785debc0ae1e7efa541696b788b6a/ruamel.yaml.clib-0.2.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d50c415df5ba918c483a2486a90dd5a3e6df634fca688f71d266e15a618d643f", size = 700721, upload-time = "2025-09-22T13:32:58.881Z" }, + { url = "https://files.pythonhosted.org/packages/e0/2e/c05050760b20fe10cb17b3fe7efd5d5653baefa862121f5b0825418f3d8b/ruamel.yaml.clib-0.2.13-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bbdb57e57a5bb3510bfb43496bab44857546c56c7f04bb7bfd0b248388d41c2e", size = 641589, upload-time = "2025-09-22T13:33:01.482Z" }, + { url = "https://files.pythonhosted.org/packages/d7/e0/6cfd5c61070f0e5556a724c753919b8758b86fe89f5b53eb9f53d0506b27/ruamel.yaml.clib-0.2.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df8ef03eb60fc2e1c0570612363091898b97a7b3181422e1b34c3ee22d7e0a9e", size = 743806, upload-time = "2025-09-22T13:33:02.682Z" }, + { url = "https://files.pythonhosted.org/packages/85/01/14964bd94bbe809497d1affc0625e428803cc9fd21e145b1b2edcbbc3c92/ruamel.yaml.clib-0.2.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89168de27694f7903a1317381525efdf7ea2c377b5b1eeeb927940870a7e8945", size = 769530, upload-time = "2025-09-22T13:33:04.044Z" }, + { url = "https://files.pythonhosted.org/packages/1d/c0/a39cc7de38b5acc81241dda9888ecabc1ee90fb3ebf9189ddfc65c08555d/ruamel.yaml.clib-0.2.13-cp311-cp311-win32.whl", hash = "sha256:8cdb4c720137bb7834cf2c09753d5f3468023eebb7f6ad2604ddf920328753dd", size = 100254, upload-time = "2025-09-22T13:33:06.243Z" }, + { url = "https://files.pythonhosted.org/packages/1f/7f/d5c1e0279df8dd9ca92138bec8387a07112d97598f668ccb3190d0bc06aa/ruamel.yaml.clib-0.2.13-cp311-cp311-win_amd64.whl", hash = "sha256:670d8a9c5b22af152863236b7a7fec471aafefc5e89b637b8f98d280cb92a0ce", size = 118235, upload-time = "2025-09-22T13:33:05.202Z" }, + { url = "https://files.pythonhosted.org/packages/50/ae/b6b3ed185206af04c779eec77a4a57587278b713b72034957d127307bbef/ruamel.yaml.clib-0.2.13-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:810500056a0e294131042eca6687a786e82aad817e8e0bc5ce059d2f95b67fef", size = 137955, upload-time = "2025-09-22T13:33:07.585Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2b/b0307fc587cebabd8faaaeb1476c825fe4dbb5ad2b5c152dfbcc31b258c4/ruamel.yaml.clib-0.2.13-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:f8e44b1b583f93a8f4d00e5285d9ea8ccd9524cdd4c54788db2880a11f21287d", size = 645916, upload-time = "2025-09-22T13:33:11.619Z" }, + { url = "https://files.pythonhosted.org/packages/74/a9/2ddad6eb03195206aca765b4ff7bd7098c46fbc65957d5ac53f96ef8d6f4/ruamel.yaml.clib-0.2.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d1ab027be86f7d5ccc9b777a22194fd3850012df413f216bf1608768b4daa2f", size = 753115, upload-time = "2025-09-22T13:33:08.793Z" }, + { url = "https://files.pythonhosted.org/packages/42/a0/ded38d60fc34f69afef6f3e379f896a80d34532bb6b4828e95f7003be610/ruamel.yaml.clib-0.2.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55f01d82d7d96213f0963609876ae841c81e217985c668100c5fb95cc9a564b3", size = 703451, upload-time = "2025-09-22T13:33:10.111Z" }, + { url = "https://files.pythonhosted.org/packages/3e/49/121067d5621a77913dc0fd473bdf2ec4a515c564806b107d3b1257cd5b14/ruamel.yaml.clib-0.2.13-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:031c7cdd5751cda19ddf90e42756ec2ee72a30791c2f65f5f800f9ee929862f0", size = 647403, upload-time = "2025-09-22T13:33:12.778Z" }, + { url = "https://files.pythonhosted.org/packages/15/fc/0845929db1840eec6aa6133b8c89941e251c3bc67180aee7d71a30bc1c80/ruamel.yaml.clib-0.2.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ba745411caabc994963729663a7c2d09398795c36dc4b5672ca4fdc445ab6544", size = 745860, upload-time = "2025-09-22T13:33:13.99Z" }, + { url = "https://files.pythonhosted.org/packages/d6/0a/dd0007d41a321edf64c4bda0aab70936281ee213406e6913105499f7d62a/ruamel.yaml.clib-0.2.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fb90267cc3748858236b9cedb580191be1d0d218f6cde64e954fa759d90fb08d", size = 770202, upload-time = "2025-09-22T13:33:15.181Z" }, + { url = "https://files.pythonhosted.org/packages/93/0d/eeed0a9824b14fa089a82247fca96de6b202af84e4093ba2a15afc0b4cb9/ruamel.yaml.clib-0.2.13-cp312-cp312-win32.whl", hash = "sha256:5188c3212c10fcd14a6de8ce787c0713a49fc5972054703f790cfd9f1d27a90b", size = 98831, upload-time = "2025-09-22T13:33:17.362Z" }, + { url = "https://files.pythonhosted.org/packages/25/45/c882a32e4b5c0ed6a5b4e0933af427fef48f3aad7259b87f38e33ee20536/ruamel.yaml.clib-0.2.13-cp312-cp312-win_amd64.whl", hash = "sha256:518b13f9fe601a559a759f3d21cdb2590cb85611934a7c0f09c09dc1a869ec83", size = 115567, upload-time = "2025-09-22T13:33:16.358Z" }, ] [[package]] From 6901e3417b5f4bdbb4dc705c23cea5277bae22b6 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 22 Sep 2025 15:18:43 -0700 Subject: [PATCH 039/341] add 3X release branch to `RELEASE_BRANCHES` (#36190) add --- Jenkinsfile | 2 +- system/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f3a63d3dec..ad8e85136b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -167,7 +167,7 @@ node { env.GIT_COMMIT = checkout(scm).GIT_COMMIT def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging', - 'release-tici', 'testing-closet*', 'hotfix-*'] + 'release-tici', 'release-tizi', 'testing-closet*', 'hotfix-*'] def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*') if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) { diff --git a/system/version.py b/system/version.py index e32c3d6033..9c5a8348f9 100755 --- a/system/version.py +++ b/system/version.py @@ -10,7 +10,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.swaglog import cloudlog from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date -RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'nightly'] +RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'release-tizi', 'nightly'] TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] BUILD_METADATA_FILENAME = "build.json" From 222e880561f8af4a3ae90d3d944353348f30f702 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Wed, 24 Sep 2025 15:24:15 -0400 Subject: [PATCH 040/341] Honda: Add 2021 Acura TLX to release (#36193) * bump opendbc * regen CARS.md * add to RELEASES.md --- RELEASES.md | 1 + docs/CARS.md | 3 ++- opendbc_repo | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index f5cf630607..966d5d3809 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,6 +5,7 @@ Version 0.10.1 (2025-09-08) * World Model: 2x the number of parameters * World Model: trained on 4x the number of segments * Driving Vision Model: trained on 4x the number of segments +* Acura TLX 2021 support thanks to MVL! * Honda City 2023 support thanks to vanillagorillaa and drFritz! * Honda N-Box 2018 support thanks to miettal! * Honda Odyssey 2021-25 support thanks to csouers and MVL! diff --git a/docs/CARS.md b/docs/CARS.md index 22cb0d46c8..3ea12f651e 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 324 Supported Cars +# 325 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -13,6 +13,7 @@ A supported vehicle is one that just works when you install a comma device. All |Acura|MDX 2025|All except Type S|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Acura|TLX 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index 6894a012cd..2eec1af104 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 6894a012cd7ec36678b8abd3d95dc79975d5dbe2 +Subproject commit 2eec1af104972b7784644bf38c4c5afb52fc070a From afc7ff1b7ad4f30c4fe7c2c832ad2d0ebfa89005 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 24 Sep 2025 17:14:06 -0700 Subject: [PATCH 041/341] raylib: fix multilang dialog height (#36196) * fix multilang dialog height * clean up --- system/ui/widgets/option_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index 3140f419f5..8f33124b5c 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -37,7 +37,7 @@ class MultiOptionDialog(Widget): options_y = content_rect.y + TITLE_FONT_SIZE + ITEM_SPACING options_h = content_rect.height - TITLE_FONT_SIZE - BUTTON_HEIGHT - 2 * ITEM_SPACING view_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, options_h) - content_h = len(self.options) * (ITEM_HEIGHT + 10) + content_h = len(self.options) * (ITEM_HEIGHT + LIST_ITEM_SPACING) list_content_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, content_h) # Scroll and render options From 6aecf59536e00168f68114054bce09647a5d64e3 Mon Sep 17 00:00:00 2001 From: Greg Hogan Date: Thu, 25 Sep 2025 17:42:11 -0700 Subject: [PATCH 042/341] add ssh hostname comma- prefix for convenience (#36199) --- tools/scripts/ssh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/scripts/ssh.py b/tools/scripts/ssh.py index 0429799a2e..e454afb691 100755 --- a/tools/scripts/ssh.py +++ b/tools/scripts/ssh.py @@ -50,7 +50,7 @@ if __name__ == "__main__": if args.debug: command += ["-v"] command += [ - f"comma@{dongle_id}", + f"comma@comma-{dongle_id}", ] if args.debug: print(" ".join([f"'{c}'" if " " in c else c for c in command])) From 54297487672fbf24ad4e7eb3e046e8aa00db17b5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 25 Sep 2025 19:27:13 -0700 Subject: [PATCH 043/341] raylib: fix button clicking on device (#36201) * fix button clicking on device * clean up --- system/ui/widgets/list_view.py | 37 +++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index a8d81a8ba2..e4ff4133aa 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -6,7 +6,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle +from openpilot.system.ui.widgets.button import Button, gui_button, ButtonStyle from openpilot.system.ui.widgets.toggle import Toggle, WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT ITEM_BASE_WIDTH = 600 @@ -73,21 +73,38 @@ class ButtonAction(ItemAction): def __init__(self, text: str | Callable[[], str], width: int = BUTTON_WIDTH, enabled: bool | Callable[[], bool] = True): super().__init__(width, enabled) self._text_source = text + self._pressed = False + + def pressed(): + self._pressed = True + + self._button = Button( + self.text, + font_size=BUTTON_FONT_SIZE, + font_weight=BUTTON_FONT_WEIGHT, + button_style=ButtonStyle.LIST_ACTION, + border_radius=BUTTON_BORDER_RADIUS, + click_callback=pressed, + ) + self._button.set_enabled(_resolve_value(enabled)) + + def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: + super().set_touch_valid_callback(touch_callback) + self._button.set_touch_valid_callback(touch_callback) @property def text(self): return _resolve_value(self._text_source, "Error") def _render(self, rect: rl.Rectangle) -> bool: - return gui_button( - rl.Rectangle(rect.x, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT), - self.text, - border_radius=BUTTON_BORDER_RADIUS, - font_weight=BUTTON_FONT_WEIGHT, - font_size=BUTTON_FONT_SIZE, - button_style=ButtonStyle.LIST_ACTION, - is_enabled=self.enabled, - ) == 1 + self._button.set_text(self.text) + button_rect = rl.Rectangle(rect.x, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT) + self._button.render(button_rect) + + # TODO: just use the generic Widget click callbacks everywhere, no returning from render + pressed = self._pressed + self._pressed = False + return pressed class TextAction(ItemAction): From 56c49b3b42d27afdd8ce7fe4e6e6021e9fa5f29d Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 25 Sep 2025 19:27:27 -0700 Subject: [PATCH 044/341] cleanup dead build flags --- SConstruct | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/SConstruct b/SConstruct index d8a390f5df..6f035e3045 100644 --- a/SConstruct +++ b/SConstruct @@ -31,26 +31,12 @@ AddOption('--ubsan', action='store_true', help='turn on UBSan') -AddOption('--coverage', - action='store_true', - help='build with test coverage options') - -AddOption('--clazy', - action='store_true', - help='build with clazy') - AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line') -AddOption('--external-sconscript', - action='store', - metavar='FILE', - dest='external_sconscript', - help='add an external SConscript to the build') - AddOption('--mutation', action='store_true', help='generate mutation-ready code') @@ -292,17 +278,6 @@ qt_env['CXXFLAGS'] += qt_flags qt_env['LIBPATH'] += ['#selfdrive/ui', ] qt_env['LIBS'] = qt_libs -if GetOption("clazy"): - checks = [ - "level0", - "level1", - "no-range-loop", - "no-non-pod-global-static", - ] - qt_env['CXX'] = 'clazy' - qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0] - qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks) - Export('env', 'qt_env', 'arch', 'real_arch') # Build common module @@ -352,7 +327,3 @@ if Dir('#tools/cabana/').exists() and GetOption('extras'): SConscript(['tools/replay/SConscript']) if arch != "larch64": SConscript(['tools/cabana/SConscript']) - -external_sconscript = GetOption('external_sconscript') -if external_sconscript: - SConscript([external_sconscript]) From 1ca9fe35c220f88ea0a6f3cc8c1b42b272c3442c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 25 Sep 2025 20:16:14 -0700 Subject: [PATCH 045/341] raylib: networking parity with QT (#36197) * match style * all this was not naught * cool can do this * fix toggle callback - also not for naught * always process callbacks * toggle stuff * cleaner * tethering password * clean up * todos for later * this is fineee * add metered options * wifi metered button * add hidden network buutton and fix instant modal to modal * damped filter * Revert "damped filter" This reverts commit f9f98d5d708fb15cf1ebef4bdace577f0e347658. * fix metered toggle when disconnected * fix tethering enabled * ohh * fix keyboard title * disable edit button temp * move here * proper disable * clean up * more * move for loop into enqueue function * flippy * got more :( * todo * clean up * mypy * rename * todo * rename * again * again * format --- selfdrive/ui/layouts/settings/settings.py | 4 +- selfdrive/ui/layouts/sidebar.py | 4 +- system/ui/lib/application.py | 8 +- system/ui/lib/networkmanager.py | 4 +- system/ui/lib/wifi_manager.py | 267 +++++++++++++++++++--- system/ui/widgets/button.py | 20 +- system/ui/widgets/keyboard.py | 7 +- system/ui/widgets/list_view.py | 23 +- system/ui/widgets/network.py | 209 +++++++++++++++-- system/ui/widgets/toggle.py | 7 + 10 files changed, 482 insertions(+), 71 deletions(-) diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index a731a9158c..d43382f199 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -11,7 +11,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wifi_manager import WifiManager from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.network import WifiManagerUI +from openpilot.system.ui.widgets.network import NetworkUI # Settings close button SETTINGS_CLOSE_TEXT = "×" @@ -59,7 +59,7 @@ class SettingsLayout(Widget): self._panels = { PanelType.DEVICE: PanelInfo("Device", DeviceLayout()), - PanelType.NETWORK: PanelInfo("Network", WifiManagerUI(wifi_manager)), + PanelType.NETWORK: PanelInfo("Network", NetworkUI(wifi_manager)), PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout()), PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout()), PanelType.FIREHOSE: PanelInfo("Firehose", FirehoseLayout()), diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index cdbfa4c04c..4d47a9878c 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -189,11 +189,11 @@ class Sidebar(Widget): # Draw colored left edge (clipped rounded rectangle) edge_rect = rl.Rectangle(metric_rect.x + 4, metric_rect.y + 4, 100, 118) rl.begin_scissor_mode(int(metric_rect.x + 4), int(metric_rect.y), 18, int(metric_rect.height)) - rl.draw_rectangle_rounded(edge_rect, 0.18, 10, metric.color) + rl.draw_rectangle_rounded(edge_rect, 0.3, 10, metric.color) rl.end_scissor_mode() # Draw border - rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.15, 10, 2, Colors.METRIC_BORDER) + rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.3, 10, 2, Colors.METRIC_BORDER) # Draw label and value labels = [metric.label, metric.value] diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 3f433e1fcb..5b35f7ac9d 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -269,11 +269,11 @@ class GuiApplication: raise Exception if result >= 0: - # Execute callback with the result and clear the overlay - if self._modal_overlay.callback is not None: - self._modal_overlay.callback(result) - + # Clear the overlay and execute the callback + original_modal = self._modal_overlay self._modal_overlay = ModalOverlay() + if original_modal.callback is not None: + original_modal.callback(result) else: yield diff --git a/system/ui/lib/networkmanager.py b/system/ui/lib/networkmanager.py index 07b5d42f4b..ffa2ff4db9 100644 --- a/system/ui/lib/networkmanager.py +++ b/system/ui/lib/networkmanager.py @@ -21,9 +21,11 @@ NM_ACCESS_POINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint' NM_SETTINGS_PATH = '/org/freedesktop/NetworkManager/Settings' NM_SETTINGS_IFACE = 'org.freedesktop.NetworkManager.Settings' NM_CONNECTION_IFACE = 'org.freedesktop.NetworkManager.Settings.Connection' +NM_ACTIVE_CONNECTION_IFACE = 'org.freedesktop.NetworkManager.Connection.Active' NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless' NM_PROPERTIES_IFACE = 'org.freedesktop.DBus.Properties' -NM_DEVICE_IFACE = "org.freedesktop.NetworkManager.Device" +NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device' +NM_IP4_CONFIG_IFACE = 'org.freedesktop.NetworkManager.IP4Config' NM_DEVICE_TYPE_WIFI = 2 NM_DEVICE_TYPE_MODEM = 8 diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index af9ae943ea..15aeb94ede 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -15,6 +15,7 @@ from jeepney.low_level import MessageType from jeepney.wrappers import Properties from openpilot.common.swaglog import cloudlog +from openpilot.common.params import Params from openpilot.system.ui.lib.networkmanager import (NM, NM_WIRELESS_IFACE, NM_802_11_AP_SEC_PAIR_WEP40, NM_802_11_AP_SEC_PAIR_WEP104, NM_802_11_AP_SEC_GROUP_WEP40, NM_802_11_AP_SEC_GROUP_WEP104, NM_802_11_AP_SEC_KEY_MGMT_PSK, @@ -23,8 +24,8 @@ from openpilot.system.ui.lib.networkmanager import (NM, NM_WIRELESS_IFACE, NM_80 NM_PATH, NM_IFACE, NM_ACCESS_POINT_IFACE, NM_SETTINGS_PATH, NM_SETTINGS_IFACE, NM_CONNECTION_IFACE, NM_DEVICE_IFACE, NM_DEVICE_TYPE_WIFI, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT, - NM_DEVICE_STATE_REASON_NEW_ACTIVATION, - NMDeviceState) + NM_DEVICE_STATE_REASON_NEW_ACTIVATION, NM_ACTIVE_CONNECTION_IFACE, + NM_IP4_CONFIG_IFACE, NMDeviceState) TETHERING_IP_ADDRESS = "192.168.43.1" DEFAULT_TETHERING_PASSWORD = "swagswagcomma" @@ -40,6 +41,12 @@ class SecurityType(IntEnum): UNSUPPORTED = 4 +class MeteredType(IntEnum): + UNKNOWN = 0 + YES = 1 + NO = 2 + + def get_security_type(flags: int, wpa_flags: int, rsn_flags: int) -> SecurityType: wpa_props = wpa_flags | rsn_flags @@ -114,7 +121,7 @@ class AccessPoint: class WifiManager: def __init__(self): - self._networks = [] # a network can be comprised of multiple APs + self._networks: list[Network] = [] # a network can be comprised of multiple APs self._active = True # used to not run when not in settings self._exit = False @@ -132,15 +139,23 @@ class WifiManager: # State self._connecting_to_ssid: str = "" + self._ipv4_address: str = "" + self._current_network_metered: MeteredType = MeteredType.UNKNOWN + self._tethering_password: str = "" self._last_network_update: float = 0.0 self._callback_queue: list[Callable] = [] + self._tethering_ssid = "weedle" + dongle_id = Params().get("DongleId") + if dongle_id: + self._tethering_ssid += "-" + dongle_id[:4] + # Callbacks - self._need_auth: Callable[[str], None] | None = None - self._activated: Callable[[], None] | None = None - self._forgotten: Callable[[], None] | None = None - self._networks_updated: Callable[[list[Network]], None] | None = None - self._disconnected: Callable[[], None] | None = None + self._need_auth: list[Callable[[str], None]] = [] + self._activated: list[Callable[[], None]] = [] + self._forgotten: list[Callable[[], None]] = [] + self._networks_updated: list[Callable[[list[Network]], None]] = [] + self._disconnected: list[Callable[[], None]] = [] self._lock = threading.Lock() @@ -152,19 +167,37 @@ class WifiManager: atexit.register(self.stop) - def set_callbacks(self, need_auth: Callable[[str], None], - activated: Callable[[], None] | None, - forgotten: Callable[[], None], - networks_updated: Callable[[list[Network]], None], - disconnected: Callable[[], None]): - self._need_auth = need_auth - self._activated = activated - self._forgotten = forgotten - self._networks_updated = networks_updated - self._disconnected = disconnected + def set_callbacks(self, need_auth: Callable[[str], None] | None = None, + activated: Callable[[], None] | None = None, + forgotten: Callable[[], None] | None = None, + networks_updated: Callable[[list[Network]], None] | None = None, + disconnected: Callable[[], None] | None = None): + if need_auth is not None: + self._need_auth.append(need_auth) + if activated is not None: + self._activated.append(activated) + if forgotten is not None: + self._forgotten.append(forgotten) + if networks_updated is not None: + self._networks_updated.append(networks_updated) + if disconnected is not None: + self._disconnected.append(disconnected) - def _enqueue_callback(self, cb: Callable, *args): - self._callback_queue.append(lambda: cb(*args)) + @property + def ipv4_address(self) -> str: + return self._ipv4_address + + @property + def current_network_metered(self) -> MeteredType: + return self._current_network_metered + + @property + def tethering_password(self) -> str: + return self._tethering_password + + def _enqueue_callbacks(self, cbs: list[Callable], *args): + for cb in cbs: + self._callback_queue.append(lambda _cb=cb: _cb(*args)) def process_callbacks(self): # Call from UI thread to run any pending callbacks @@ -180,10 +213,13 @@ class WifiManager: self._last_network_update = 0.0 def _monitor_state(self): + # TODO: make an initialize function to only wait in one place device_path = self._wait_for_wifi_device() if device_path is None: return + self._tethering_password = self._get_tethering_password() + rule = MatchRule( type="signal", interface=NM_DEVICE_IFACE, @@ -211,20 +247,18 @@ class WifiManager: # BAD PASSWORD if new_state == NMDeviceState.NEED_AUTH and change_reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT and len(self._connecting_to_ssid): self.forget_connection(self._connecting_to_ssid, block=True) - if self._need_auth is not None: - self._enqueue_callback(self._need_auth, self._connecting_to_ssid) + self._enqueue_callbacks(self._need_auth, self._connecting_to_ssid) self._connecting_to_ssid = "" elif new_state == NMDeviceState.ACTIVATED: - if self._activated is not None: + if len(self._activated): self._update_networks() - self._enqueue_callback(self._activated) + self._enqueue_callbacks(self._activated) self._connecting_to_ssid = "" elif new_state == NMDeviceState.DISCONNECTED and change_reason != NM_DEVICE_STATE_REASON_NEW_ACTIVATION: self._connecting_to_ssid = "" - if self._disconnected is not None: - self._enqueue_callback(self._disconnected) + self._enqueue_callbacks(self._forgotten) def _network_scanner(self): self._wait_for_wifi_device() @@ -285,14 +319,24 @@ class WifiManager: conns[ssid] = conn_path return conns - def connect_to_network(self, ssid: str, password: str): + def _get_active_connections(self): + return self._router_main.send_and_get_reply(Properties(self._nm).get('ActiveConnections')).body[0][1] + + # TODO: use this + def _get_connection_settings(self, conn_path: str) -> dict: + conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE) + reply = self._router_main.send_and_get_reply(new_method_call(conn_addr, 'GetSettings')) + if reply.header.message_type == MessageType.error: + cloudlog.warning(f'Failed to get connection settings: {reply}') + return {} + return dict(reply.body[0]) + + def connect_to_network(self, ssid: str, password: str, hidden: bool = False): def worker(): # Clear all connections that may already exist to the network we are connecting to self._connecting_to_ssid = ssid self.forget_connection(ssid, block=True) - is_hidden = False - connection = { 'connection': { 'type': ('s', '802-11-wireless'), @@ -302,7 +346,7 @@ class WifiManager: }, '802-11-wireless': { 'ssid': ('ay', ssid.encode("utf-8")), - 'hidden': ('b', is_hidden), + 'hidden': ('b', hidden), 'mode': ('s', 'infrastructure'), }, 'ipv4': { @@ -332,9 +376,9 @@ class WifiManager: conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE) self._router_main.send_and_get_reply(new_method_call(conn_addr, 'Delete')) - if self._forgotten is not None: + if len(self._forgotten): self._update_networks() - self._enqueue_callback(self._forgotten) + self._enqueue_callbacks(self._forgotten) if block: worker() @@ -358,6 +402,137 @@ class WifiManager: else: threading.Thread(target=worker, daemon=True).start() + def _deactivate_connection(self, ssid: str): + for conn_path in self._get_active_connections(): + conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_ACTIVE_CONNECTION_IFACE) + specific_obj_path = self._router_main.send_and_get_reply(Properties(conn_addr).get('SpecificObject')).body[0][1] + + if specific_obj_path != "/": + ap_addr = DBusAddress(specific_obj_path, bus_name=NM, interface=NM_ACCESS_POINT_IFACE) + ap_ssid = bytes(self._router_main.send_and_get_reply(Properties(ap_addr).get('Ssid')).body[0][1]).decode("utf-8", "replace") + + if ap_ssid == ssid: + self._router_main.send_and_get_reply(new_method_call(self._nm, 'DeactivateConnection', 'o', (conn_path,))) + return + + def is_tethering_active(self) -> bool: + for network in self._networks: + if network.is_connected: + return bool(network.ssid == self._tethering_ssid) + return False + + def set_tethering_password(self, password: str): + def worker(): + conn_path = self._get_connections().get(self._tethering_ssid, None) + if conn_path is None: + cloudlog.warning('No tethering connection found') + return + + settings = self._get_connection_settings(conn_path) + if len(settings) == 0: + cloudlog.warning(f'Failed to get tethering settings for {conn_path}') + return + + settings['802-11-wireless-security']['psk'] = ('s', password) + + conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE) + reply = self._router_main.send_and_get_reply(new_method_call(conn_addr, 'Update', 'a{sa{sv}}', (settings,))) + if reply.header.message_type == MessageType.error: + cloudlog.warning(f'Failed to update tethering settings: {reply}') + return + + self._tethering_password = password + if self.is_tethering_active(): + self.activate_connection(self._tethering_ssid, block=True) + + threading.Thread(target=worker, daemon=True).start() + + def _get_tethering_password(self) -> str: + conn_path = self._get_connections().get(self._tethering_ssid, None) + if conn_path is None: + cloudlog.warning('No tethering connection found') + return '' + + reply = self._router_main.send_and_get_reply(new_method_call( + DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE), + 'GetSecrets', 's', ('802-11-wireless-security',) + )) + + if reply.header.message_type == MessageType.error: + cloudlog.warning(f'Failed to get tethering password: {reply}') + return '' + + secrets = reply.body[0] + if '802-11-wireless-security' not in secrets: + return '' + + return str(secrets['802-11-wireless-security'].get('psk', ('s', ''))[1]) + + def set_tethering_active(self, active: bool): + def worker(): + if active: + self.activate_connection(self._tethering_ssid, block=True) + else: + self._deactivate_connection(self._tethering_ssid) + + threading.Thread(target=worker, daemon=True).start() + + def _update_current_network_metered(self) -> None: + if self._wifi_device is None: + cloudlog.warning("No WiFi device found") + return + + self._current_network_metered = MeteredType.UNKNOWN + for active_conn in self._get_active_connections(): + conn_addr = DBusAddress(active_conn, bus_name=NM, interface=NM_ACTIVE_CONNECTION_IFACE) + conn_type = self._router_main.send_and_get_reply(Properties(conn_addr).get('Type')).body[0][1] + + if conn_type == '802-11-wireless': + conn_path = self._router_main.send_and_get_reply(Properties(conn_addr).get('Connection')).body[0][1] + if conn_path == "/": + continue + + settings = self._get_connection_settings(conn_path) + + if len(settings) == 0: + cloudlog.warning(f'Failed to get connection settings for {conn_path}') + continue + + metered_prop = settings['connection'].get('metered', ('i', 0))[1] + if metered_prop == MeteredType.YES: + self._current_network_metered = MeteredType.YES + elif metered_prop == MeteredType.NO: + self._current_network_metered = MeteredType.NO + print('current_network_metered', self._current_network_metered) + return + + def set_current_network_metered(self, metered: MeteredType): + def worker(): + for active_conn in self._get_active_connections(): + conn_addr = DBusAddress(active_conn, bus_name=NM, interface=NM_ACTIVE_CONNECTION_IFACE) + conn_type = self._router_main.send_and_get_reply(Properties(conn_addr).get('Type')).body[0][1] + + if conn_type == '802-11-wireless' and not self.is_tethering_active(): + conn_path = self._router_main.send_and_get_reply(Properties(conn_addr).get('Connection')).body[0][1] + if conn_path == "/": + continue + + settings = self._get_connection_settings(conn_path) + + if len(settings) == 0: + cloudlog.warning(f'Failed to get connection settings for {conn_path}') + return + + settings['connection']['metered'] = ('i', int(metered)) + + conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE) + reply = self._router_main.send_and_get_reply(new_method_call(conn_addr, 'Update', 'a{sa{sv}}', (settings,))) + if reply.header.message_type == MessageType.error: + cloudlog.warning(f'Failed to update tethering settings: {reply}') + return + + threading.Thread(target=worker, daemon=True).start() + def _request_scan(self): if self._wifi_device is None: cloudlog.warning("No WiFi device found") @@ -409,8 +584,32 @@ class WifiManager: networks.sort(key=lambda n: (-n.is_connected, -n.strength, n.ssid.lower())) self._networks = networks - if self._networks_updated is not None: - self._enqueue_callback(self._networks_updated, self._networks) + self._update_ipv4_address() + self._update_current_network_metered() + + self._enqueue_callbacks(self._networks_updated, self._networks) + + def _update_ipv4_address(self): + if self._wifi_device is None: + cloudlog.warning("No WiFi device found") + return + + self._ipv4_address = "" + + for conn_path in self._get_active_connections(): + conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_ACTIVE_CONNECTION_IFACE) + conn_type = self._router_main.send_and_get_reply(Properties(conn_addr).get('Type')).body[0][1] + if conn_type == '802-11-wireless': + ip4config_path = self._router_main.send_and_get_reply(Properties(conn_addr).get('Ip4Config')).body[0][1] + + if ip4config_path != "/": + ip4config_addr = DBusAddress(ip4config_path, bus_name=NM, interface=NM_IP4_CONFIG_IFACE) + address_data = self._router_main.send_and_get_reply(Properties(ip4config_addr).get('AddressData')).body[0][1] + + for entry in address_data: + if 'address' in entry: + self._ipv4_address = entry['address'][1] + return def __del__(self): self.stop() diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index eb38f20597..1e3f28eb8e 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -14,6 +14,7 @@ class ButtonStyle(IntEnum): PRIMARY = 1 # For main actions DANGER = 2 # For critical actions, like reboot or delete TRANSPARENT = 3 # For buttons with transparent background and border + TRANSPARENT_WHITE = 3 # For buttons with transparent background and border ACTION = 4 LIST_ACTION = 5 # For list items with action buttons NO_EFFECT = 6 @@ -23,8 +24,6 @@ class ButtonStyle(IntEnum): ICON_PADDING = 15 DEFAULT_BUTTON_FONT_SIZE = 60 -BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) -BUTTON_DISABLED_BACKGROUND_COLOR = rl.Color(51, 51, 51, 255) ACTION_BUTTON_FONT_SIZE = 48 BUTTON_TEXT_COLOR = { @@ -32,6 +31,7 @@ BUTTON_TEXT_COLOR = { ButtonStyle.PRIMARY: rl.Color(228, 228, 228, 255), ButtonStyle.DANGER: rl.Color(228, 228, 228, 255), ButtonStyle.TRANSPARENT: rl.BLACK, + ButtonStyle.TRANSPARENT_WHITE: rl.WHITE, ButtonStyle.ACTION: rl.BLACK, ButtonStyle.LIST_ACTION: rl.Color(228, 228, 228, 255), ButtonStyle.NO_EFFECT: rl.Color(228, 228, 228, 255), @@ -39,11 +39,16 @@ BUTTON_TEXT_COLOR = { ButtonStyle.FORGET_WIFI: rl.Color(51, 51, 51, 255), } +BUTTON_DISABLED_TEXT_COLORS = { + ButtonStyle.TRANSPARENT_WHITE: rl.WHITE, +} + BUTTON_BACKGROUND_COLORS = { ButtonStyle.NORMAL: rl.Color(51, 51, 51, 255), ButtonStyle.PRIMARY: rl.Color(70, 91, 234, 255), ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, + ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, ButtonStyle.ACTION: rl.Color(189, 189, 189, 255), ButtonStyle.LIST_ACTION: rl.Color(57, 57, 57, 255), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), @@ -56,6 +61,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255), ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, + ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, ButtonStyle.ACTION: rl.Color(130, 130, 130, 255), ButtonStyle.LIST_ACTION: rl.Color(74, 74, 74, 74), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), @@ -63,6 +69,10 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.FORGET_WIFI: rl.Color(130, 130, 130, 255), } +BUTTON_DISABLED_BACKGROUND_COLORS = { + ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, +} + _pressed_buttons: set[str] = set() # Track mouse press state globally @@ -156,7 +166,7 @@ def gui_button( # Draw the button text if any if text: - color = BUTTON_TEXT_COLOR[button_style] if is_enabled else BUTTON_DISABLED_TEXT_COLOR + color = BUTTON_TEXT_COLOR[button_style] if is_enabled else BUTTON_DISABLED_TEXT_COLORS.get(button_style, rl.Color(228, 228, 228, 51)) rl.draw_text_ex(font, text, text_pos, font_size, 0, color) return result @@ -198,8 +208,8 @@ class Button(Widget): else: self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] elif self._button_style != ButtonStyle.NO_EFFECT: - self._background_color = BUTTON_DISABLED_BACKGROUND_COLOR - self._label.set_text_color(BUTTON_DISABLED_TEXT_COLOR) + self._background_color = BUTTON_DISABLED_BACKGROUND_COLORS.get(self._button_style, rl.Color(51, 51, 51, 255)) + self._label.set_text_color(BUTTON_DISABLED_TEXT_COLORS.get(self._button_style, rl.Color(228, 228, 228, 51))) def _render(self, _): roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 25843d1ce8..70f06f6b9d 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -104,6 +104,9 @@ class Keyboard(Widget): self._all_keys[CAPS_LOCK_KEY] = Button("", partial(self._key_callback, CAPS_LOCK_KEY), icon=self._key_icons[CAPS_LOCK_KEY], button_style=ButtonStyle.KEYBOARD, multi_touch=True) + def set_text(self, text: str): + self._input_box.text = text + @property def text(self): return self._input_box.text @@ -243,7 +246,9 @@ class Keyboard(Widget): if not self._caps_lock and self._layout_name == "uppercase": self._layout_name = "lowercase" - def reset(self): + def reset(self, min_text_size: int | None = None): + if min_text_size is not None: + self._min_text_size = min_text_size self._render_return_status = -1 self.clear() diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index e4ff4133aa..648ce47a6f 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -41,6 +41,9 @@ class ItemAction(Widget, ABC): self.set_rect(rl.Rectangle(0, 0, width, 0)) self._enabled_source = enabled + def set_enabled(self, enabled: bool | Callable[[], bool]): + self._enabled_source = enabled + @property def enabled(self): return _resolve_value(self._enabled_source, False) @@ -58,8 +61,9 @@ class ToggleAction(ItemAction): def _render(self, rect: rl.Rectangle) -> bool: self.toggle.set_enabled(self.enabled) - self.toggle.render(rl.Rectangle(rect.x, rect.y + (rect.height - TOGGLE_HEIGHT) / 2, self._rect.width, TOGGLE_HEIGHT)) - return False + clicked = self.toggle.render(rl.Rectangle(rect.x, rect.y + (rect.height - TOGGLE_HEIGHT) / 2, self._rect.width, TOGGLE_HEIGHT)) + self.state = self.toggle.get_state() + return bool(clicked) def set_state(self, state: bool): self.state = state @@ -86,7 +90,7 @@ class ButtonAction(ItemAction): border_radius=BUTTON_BORDER_RADIUS, click_callback=pressed, ) - self._button.set_enabled(_resolve_value(enabled)) + self.set_enabled(enabled) def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: super().set_touch_valid_callback(touch_callback) @@ -98,6 +102,7 @@ class ButtonAction(ItemAction): def _render(self, rect: rl.Rectangle) -> bool: self._button.set_text(self.text) + self._button.set_enabled(_resolve_value(self.enabled)) button_rect = rl.Rectangle(rect.x, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT) self._button.render(button_rect) @@ -121,6 +126,10 @@ class TextAction(ItemAction): def text(self): return _resolve_value(self._text_source, "Error") + def _update_state(self): + text_width = measure_text_cached(self._font, self.text, ITEM_TEXT_FONT_SIZE).x + self._rect.width = int(text_width + TEXT_PADDING) + def _render(self, rect: rl.Rectangle) -> bool: current_text = self.text text_size = measure_text_cached(self._font, current_text, ITEM_TEXT_FONT_SIZE) @@ -183,7 +192,7 @@ class MultipleButtonAction(ItemAction): # Check button state mouse_pos = rl.get_mouse_position() - is_hovered = rl.check_collision_point_rec(mouse_pos, button_rect) + is_hovered = rl.check_collision_point_rec(mouse_pos, button_rect) and self.enabled is_pressed = is_hovered and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and self.is_pressed is_selected = i == self.selected_button @@ -195,6 +204,9 @@ class MultipleButtonAction(ItemAction): else: bg_color = rl.Color(57, 57, 57, 255) # Gray + if not self.enabled: + bg_color = rl.Color(bg_color.r, bg_color.g, bg_color.b, 150) # Dim + # Draw button rl.draw_rectangle_rounded(button_rect, 1.0, 20, bg_color) @@ -202,7 +214,8 @@ class MultipleButtonAction(ItemAction): text_size = measure_text_cached(self._font, text, 40) text_x = button_x + (self.button_width - text_size.x) / 2 text_y = button_y + (BUTTON_HEIGHT - text_size.y) / 2 - rl.draw_text_ex(self._font, text, rl.Vector2(text_x, text_y), 40, 0, rl.Color(228, 228, 228, 255)) + text_color = rl.Color(228, 228, 228, 255) if self.enabled else rl.Color(150, 150, 150, 255) + rl.draw_text_ex(self._font, text, rl.Vector2(text_x, text_y), 40, 0, text_color) # Handle click if is_hovered and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and self.is_pressed: diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 3e6317a49c..7a7215b48e 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -3,14 +3,17 @@ from functools import partial from typing import cast import pyray as rl -from openpilot.system.ui.lib.application import gui_app +from openpilot.common.filter_simple import FirstOrderFilter +from openpilot.system.ui.lib.application import gui_app, DEFAULT_FPS from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel -from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network +from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.label import TextAlignment, gui_label +from openpilot.system.ui.widgets.scroller import Scroller +from openpilot.system.ui.widgets.list_view import text_item, button_item, ListItem, ToggleAction, MultipleButtonAction, ButtonAction NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 @@ -26,6 +29,11 @@ STRENGTH_ICONS = [ ] +class PanelType(IntEnum): + WIFI = 0 + ADVANCED = 1 + + class UIState(IntEnum): IDLE = 0 CONNECTING = 1 @@ -34,10 +42,179 @@ class UIState(IntEnum): FORGETTING = 4 +class NavButton(Widget): + def __init__(self, text: str): + super().__init__() + self.text = text + self.set_rect(rl.Rectangle(0, 0, 400, 100)) + self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) + self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) + + def set_position(self, x: float, y: float) -> None: + x = self._x_pos_filter.update(x) + y = self._y_pos_filter.update(y) + changed = (self._rect.x != x or self._rect.y != y) + self._rect.x, self._rect.y = x, y + if changed: + self._update_layout_rects() + + def _render(self, _): + color = rl.Color(74, 74, 74, 255) if self.is_pressed else rl.Color(57, 57, 57, 255) + rl.draw_rectangle_rounded(self._rect, 0.6, 10, color) + gui_label(self.rect, self.text, font_size=60, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + + +class NetworkUI(Widget): + def __init__(self, wifi_manager: WifiManager): + super().__init__() + self._wifi_manager = wifi_manager + self._current_panel: PanelType = PanelType.WIFI + self._wifi_panel = WifiManagerUI(wifi_manager) + self._advanced_panel = AdvancedNetworkSettings(wifi_manager) + self._nav_button = NavButton("Advanced") + self._nav_button.set_click_callback(self._cycle_panel) + + def _update_state(self): + self._wifi_manager.process_callbacks() + + def show_event(self): + self._set_current_panel(PanelType.WIFI) + self._wifi_panel.show_event() + + def hide_event(self): + self._wifi_panel.hide_event() + + def _cycle_panel(self): + if self._current_panel == PanelType.WIFI: + self._set_current_panel(PanelType.ADVANCED) + else: + self._set_current_panel(PanelType.WIFI) + + def _render(self, _): + # subtract button + content_rect = rl.Rectangle(self._rect.x, self._rect.y + self._nav_button.rect.height + 20, + self._rect.width, self._rect.height - self._nav_button.rect.height - 20) + if self._current_panel == PanelType.WIFI: + self._nav_button.text = "Advanced" + self._nav_button.set_position(self._rect.x + self._rect.width - self._nav_button.rect.width, self._rect.y + 10) + self._wifi_panel.render(content_rect) + else: + self._nav_button.text = "Back" + self._nav_button.set_position(self._rect.x, self._rect.y + 10) + self._advanced_panel.render(content_rect) + + self._nav_button.render() + + def _set_current_panel(self, panel: PanelType): + self._current_panel = panel + + +class AdvancedNetworkSettings(Widget): + def __init__(self, wifi_manager: WifiManager): + super().__init__() + self._wifi_manager = wifi_manager + self._wifi_manager.set_callbacks(networks_updated=self._on_network_updated) + + self._keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) + + # Tethering + self._tethering_action = ToggleAction(initial_state=False) + self._tethering_btn = ListItem(title="Enable Tethering", action_item=self._tethering_action, callback=self._toggle_tethering) + + # Tethering Password + self._tethering_password_action = ButtonAction(text="EDIT") + self._tethering_password_btn = ListItem(title="Tethering Password", action_item=self._tethering_password_action, callback=self._edit_tethering_password) + + # TODO: Roaming toggle, edit APN settings, and cellular metered toggle + + # Metered + self._wifi_metered_action = MultipleButtonAction(["default", "metered", "unmetered"], 255, 0, callback=self._toggle_wifi_metered) + self._wifi_metered_btn = ListItem(title="Wi-Fi Network Metered", description="Prevent large data uploads when on a metered Wi-Fi connection", + action_item=self._wifi_metered_action) + + items: list[Widget] = [ + self._tethering_btn, + self._tethering_password_btn, + text_item("IP Address", lambda: self._wifi_manager.ipv4_address), + self._wifi_metered_btn, + button_item("Hidden Network", "CONNECT", callback=self._connect_to_hidden_network), + ] + + self._scroller = Scroller(items, line_separator=True, spacing=0) + + def _on_network_updated(self, networks: list[Network]): + self._tethering_action.set_enabled(True) + self._tethering_action.set_state(self._wifi_manager.is_tethering_active()) + self._tethering_password_action.set_enabled(True) + + if self._wifi_manager.is_tethering_active() or self._wifi_manager.ipv4_address == "": + self._wifi_metered_action.set_enabled(False) + self._wifi_metered_action.selected_button = 0 + elif self._wifi_manager.ipv4_address != "": + metered = self._wifi_manager.current_network_metered + self._wifi_metered_action.set_enabled(True) + self._wifi_metered_action.selected_button = int(metered) if metered in (MeteredType.UNKNOWN, MeteredType.YES, MeteredType.NO) else 0 + + def _toggle_tethering(self): + checked = self._tethering_action.state + self._tethering_action.set_enabled(False) + if checked: + self._wifi_metered_action.set_enabled(False) + self._wifi_manager.set_tethering_active(checked) + + def _toggle_wifi_metered(self, metered): + metered_type = {0: MeteredType.UNKNOWN, 1: MeteredType.YES, 2: MeteredType.NO}.get(metered, MeteredType.UNKNOWN) + self._wifi_metered_action.set_enabled(False) + self._wifi_manager.set_current_network_metered(metered_type) + + def _connect_to_hidden_network(self): + def connect_hidden(result): + if result != 1: + return + + ssid = self._keyboard.text + if not ssid: + return + + def enter_password(result): + password = self._keyboard.text + if password == "": + # connect without password + self._wifi_manager.connect_to_network(ssid, "", hidden=True) + return + + self._wifi_manager.connect_to_network(ssid, password, hidden=True) + + self._keyboard.reset(min_text_size=0) + self._keyboard.set_title("Enter password", f"for \"{ssid}\"") + gui_app.set_modal_overlay(self._keyboard, enter_password) + + self._keyboard.reset(min_text_size=1) + self._keyboard.set_title("Enter SSID", "") + gui_app.set_modal_overlay(self._keyboard, connect_hidden) + + def _edit_tethering_password(self): + def update_password(result): + if result != 1: + return + + password = self._keyboard.text + self._wifi_manager.set_tethering_password(password) + self._tethering_password_action.set_enabled(False) + + self._keyboard.reset(min_text_size=MIN_PASSWORD_LENGTH) + self._keyboard.set_title("Enter new tethering password", "") + self._keyboard.set_text(self._wifi_manager.tethering_password) + gui_app.set_modal_overlay(self._keyboard, update_password) + + def _render(self, _): + self._scroller.render(self._rect) + + class WifiManagerUI(Widget): def __init__(self, wifi_manager: WifiManager): super().__init__() - self.wifi_manager = wifi_manager + self._wifi_manager = wifi_manager self.state: UIState = UIState.IDLE self._state_network: Network | None = None # for CONNECTING / NEEDS_AUTH / SHOW_FORGET_CONFIRM / FORGETTING self._password_retry: bool = False # for NEEDS_AUTH @@ -51,33 +228,31 @@ class WifiManagerUI(Widget): self._forget_networks_buttons: dict[str, Button] = {} self._confirm_dialog = ConfirmDialog("", "Forget", "Cancel") - self.wifi_manager.set_callbacks(need_auth=self._on_need_auth, - activated=self._on_activated, - forgotten=self._on_forgotten, - networks_updated=self._on_network_updated, - disconnected=self._on_disconnected) + self._wifi_manager.set_callbacks(need_auth=self._on_need_auth, + activated=self._on_activated, + forgotten=self._on_forgotten, + networks_updated=self._on_network_updated, + disconnected=self._on_disconnected) def show_event(self): # start/stop scanning when widget is visible - self.wifi_manager.set_active(True) + self._wifi_manager.set_active(True) def hide_event(self): - self.wifi_manager.set_active(False) + self._wifi_manager.set_active(False) def _load_icons(self): for icon in STRENGTH_ICONS + ["icons/checkmark.png", "icons/circled_slash.png", "icons/lock_closed.png"]: gui_app.texture(icon, ICON_SIZE, ICON_SIZE) def _render(self, rect: rl.Rectangle): - self.wifi_manager.process_callbacks() - if not self._networks: gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) return if self.state == UIState.NEEDS_AUTH and self._state_network: self.keyboard.set_title("Wrong password" if self._password_retry else "Enter password", f"for {self._state_network.ssid}") - self.keyboard.reset() + self.keyboard.reset(min_text_size=MIN_PASSWORD_LENGTH) gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(cast(Network, self._state_network), result)) elif self.state == UIState.SHOW_FORGET_CONFIRM and self._state_network: self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{self._state_network.ssid}"?') @@ -200,20 +375,20 @@ class WifiManagerUI(Widget): self.state = UIState.CONNECTING self._state_network = network if network.is_saved and not password: - self.wifi_manager.activate_connection(network.ssid) + self._wifi_manager.activate_connection(network.ssid) else: - self.wifi_manager.connect_to_network(network.ssid, password) + self._wifi_manager.connect_to_network(network.ssid, password) def forget_network(self, network: Network): self.state = UIState.FORGETTING self._state_network = network - self.wifi_manager.forget_connection(network.ssid) + self._wifi_manager.forget_connection(network.ssid) def _on_network_updated(self, networks: list[Network]): self._networks = networks for n in self._networks: self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT, - button_style=ButtonStyle.NO_EFFECT) + button_style=ButtonStyle.TRANSPARENT_WHITE) self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI, font_size=45) diff --git a/system/ui/widgets/toggle.py b/system/ui/widgets/toggle.py index eba5ef9e58..968afda9c8 100644 --- a/system/ui/widgets/toggle.py +++ b/system/ui/widgets/toggle.py @@ -20,6 +20,7 @@ class Toggle(Widget): self._enabled = True self._progress = 1.0 if initial_state else 0.0 self._target = self._progress + self._clicked = False def set_rect(self, rect: rl.Rectangle): self._rect = rl.Rectangle(rect.x, rect.y, WIDTH, HEIGHT) @@ -28,6 +29,7 @@ class Toggle(Widget): if not self._enabled: return + self._clicked = True self._state = not self._state self._target = 1.0 if self._state else 0.0 @@ -66,5 +68,10 @@ class Toggle(Widget): knob_y = self._rect.y + HEIGHT / 2 rl.draw_circle(int(knob_x), int(knob_y), HEIGHT / 2, knob_color) + # TODO: use click callback + clicked = self._clicked + self._clicked = False + return clicked + def _blend_color(self, c1, c2, t): return rl.Color(int(c1.r + (c2.r - c1.r) * t), int(c1.g + (c2.g - c1.g) * t), int(c1.b + (c2.b - c1.b) * t), 255) From 2c377e534fa3f0857440415c3c3056d8f6ee4018 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 25 Sep 2025 20:48:12 -0700 Subject: [PATCH 046/341] raylib: wifi manager initialize function (#36203) * init func * rm print * rm * use get_conn settings in another place --- system/ui/lib/wifi_manager.py | 95 +++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 32 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 15aeb94ede..3ce5c839e8 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -158,15 +158,26 @@ class WifiManager: self._disconnected: list[Callable[[], None]] = [] self._lock = threading.Lock() - self._scan_thread = threading.Thread(target=self._network_scanner, daemon=True) - self._scan_thread.start() - self._state_thread = threading.Thread(target=self._monitor_state, daemon=True) - self._state_thread.start() - + self._initialize() atexit.register(self.stop) + def _initialize(self): + def worker(): + self._wait_for_wifi_device() + + self._scan_thread.start() + self._state_thread.start() + + if self._tethering_ssid not in self._get_connections(): + self._add_tethering_connection() + + self._tethering_password = self._get_tethering_password() + cloudlog.debug("WifiManager initialized") + + threading.Thread(target=worker, daemon=True).start() + def set_callbacks(self, need_auth: Callable[[str], None] | None = None, activated: Callable[[], None] | None = None, forgotten: Callable[[], None] | None = None, @@ -213,18 +224,11 @@ class WifiManager: self._last_network_update = 0.0 def _monitor_state(self): - # TODO: make an initialize function to only wait in one place - device_path = self._wait_for_wifi_device() - if device_path is None: - return - - self._tethering_password = self._get_tethering_password() - rule = MatchRule( type="signal", interface=NM_DEVICE_IFACE, member="StateChanged", - path=device_path, + path=self._wifi_device, ) # Filter for StateChanged signal @@ -261,8 +265,6 @@ class WifiManager: self._enqueue_callbacks(self._forgotten) def _network_scanner(self): - self._wait_for_wifi_device() - while not self._exit: if self._active: if time.monotonic() - self._last_network_update > SCAN_PERIOD_SECONDS: @@ -273,15 +275,12 @@ class WifiManager: self._last_network_update = time.monotonic() time.sleep(1 / 2.) - def _wait_for_wifi_device(self) -> str | None: - with self._lock: - device_path: str | None = None - while not self._exit: - device_path = self._get_wifi_device() - if device_path is not None: - break - time.sleep(1) - return device_path + def _wait_for_wifi_device(self): + while not self._exit: + device_path = self._get_wifi_device() + if device_path is not None: + break + time.sleep(1) def _get_wifi_device(self) -> str | None: if self._wifi_device is not None: @@ -304,15 +303,12 @@ class WifiManager: conns: dict[str, str] = {} for conn_path in known_connections: - conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE) - reply = self._router_main.send_and_get_reply(new_method_call(conn_addr, "GetSettings")) + settings = self._get_connection_settings(conn_path) - # ignore connections removed during iteration (need auth, etc.) - if reply.header.message_type == MessageType.error: - cloudlog.warning(f"Failed to get connection properties for {conn_path}") + if len(settings) == 0: + cloudlog.warning(f'Failed to get connection settings for {conn_path}') continue - settings = reply.body[0] if "802-11-wireless" in settings: ssid = settings['802-11-wireless']['ssid'][1].decode("utf-8", "replace") if ssid != "": @@ -322,7 +318,6 @@ class WifiManager: def _get_active_connections(self): return self._router_main.send_and_get_reply(Properties(self._nm).get('ActiveConnections')).body[0][1] - # TODO: use this def _get_connection_settings(self, conn_path: str) -> dict: conn_addr = DBusAddress(conn_path, bus_name=NM, interface=NM_CONNECTION_IFACE) reply = self._router_main.send_and_get_reply(new_method_call(conn_addr, 'GetSettings')) @@ -331,6 +326,43 @@ class WifiManager: return {} return dict(reply.body[0]) + def _add_tethering_connection(self): + connection = { + 'connection': { + 'type': ('s', '802-11-wireless'), + 'uuid': ('s', str(uuid.uuid4())), + 'id': ('s', 'Hotspot'), + 'autoconnect-retries': ('i', 0), + 'interface-name': ('s', 'wlan0'), + 'autoconnect': ('b', False), + }, + '802-11-wireless': { + 'band': ('s', 'bg'), + 'mode': ('s', 'ap'), + 'ssid': ('ay', self._tethering_ssid.encode("utf-8")), + }, + '802-11-wireless-security': { + 'group': ('as', ['ccmp']), + 'key-mgmt': ('s', 'wpa-psk'), + 'pairwise': ('as', ['ccmp']), + 'proto': ('as', ['rsn']), + 'psk': ('s', DEFAULT_TETHERING_PASSWORD), + }, + 'ipv4': { + 'method': ('s', 'shared'), + 'address-data': ('aa{sv}', [[ + ('address', ('s', TETHERING_IP_ADDRESS)), + ('prefix', ('u', 24)), + ]]), + 'gateway': ('s', TETHERING_IP_ADDRESS), + 'never-default': ('b', True), + }, + 'ipv6': {'method': ('s', 'ignore')}, + } + + settings_addr = DBusAddress(NM_SETTINGS_PATH, bus_name=NM, interface=NM_SETTINGS_IFACE) + self._router_main.send_and_get_reply(new_method_call(settings_addr, 'AddConnection', 'a{sa{sv}}', (connection,))) + def connect_to_network(self, ssid: str, password: str, hidden: bool = False): def worker(): # Clear all connections that may already exist to the network we are connecting to @@ -503,7 +535,6 @@ class WifiManager: self._current_network_metered = MeteredType.YES elif metered_prop == MeteredType.NO: self._current_network_metered = MeteredType.NO - print('current_network_metered', self._current_network_metered) return def set_current_network_metered(self, metered: MeteredType): From cf5b743de64f4a95827c6da9bf6c42d7ae9ebf3a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 25 Sep 2025 20:55:14 -0700 Subject: [PATCH 047/341] build system cleanups (#36202) * it's all common * never getting fixed * it's just tici * reorders * qcom2 -> tici * Revert "qcom2 -> tici" This reverts commit f4d849b2952cb0e662975805db6a1d32511ed392. * Reapply "qcom2 -> tici" This reverts commit 58b193cb8de872830f8a7821a339edca14e4a337. * is tici * lil more * Revert "is tici" This reverts commit a169be18d3fdcb3ef8317a63a89d8becadabfad8. * Revert "Reapply "qcom2 -> tici"" This reverts commit 26f9c0e7d068fc8a1a5f07383b3616e619cd4e8c. * qcom2 -> __tici__ * lil more * mv lenv * clean that up * lil more] * fix * lil more --- SConstruct | 250 ++++++++++--------------- common/SConscript | 11 +- selfdrive/modeld/SConscript | 4 +- selfdrive/ui/qt/qt_window.cc | 2 +- selfdrive/ui/qt/qt_window.h | 2 +- selfdrive/ui/qt/widgets/cameraview.cc | 12 +- selfdrive/ui/qt/widgets/cameraview.h | 4 +- system/camerad/SConscript | 4 +- system/camerad/cameras/camera_qcom2.cc | 2 +- system/hardware/hw.h | 2 +- system/loggerd/encoderd.cc | 2 +- 11 files changed, 117 insertions(+), 178 deletions(-) diff --git a/SConstruct b/SConstruct index 6f035e3045..80273db106 100644 --- a/SConstruct +++ b/SConstruct @@ -3,144 +3,52 @@ import subprocess import sys import sysconfig import platform +import shlex import numpy as np import SCons.Errors SCons.Warnings.warningAsException(True) -# pending upstream fix - https://github.com/SCons/scons/issues/4461 -#SetOption('warn', 'all') - -TICI = os.path.isfile('/TICI') -AGNOS = TICI - Decider('MD5-timestamp') SetOption('num_jobs', max(1, int(os.cpu_count()/2))) -AddOption('--kaitai', - action='store_true', - help='Regenerate kaitai struct parsers') - -AddOption('--asan', - action='store_true', - help='turn on ASAN') - -AddOption('--ubsan', - action='store_true', - help='turn on UBSan') - -AddOption('--ccflags', - action='store', - type='string', - default='', - help='pass arbitrary flags over the command line') - -AddOption('--mutation', - action='store_true', - help='generate mutation-ready code') - +AddOption('--kaitai', action='store_true', help='Regenerate kaitai struct parsers') +AddOption('--asan', action='store_true', help='turn on ASAN') +AddOption('--ubsan', action='store_true', help='turn on UBSan') +AddOption('--mutation', action='store_true', help='generate mutation-ready code') +AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line') AddOption('--minimal', action='store_false', dest='extras', default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS) help='the minimum build to run openpilot. no tests, tools, etc.') -## Architecture name breakdown (arch) -## - larch64: linux tici aarch64 -## - aarch64: linux pc aarch64 -## - x86_64: linux pc x64 -## - Darwin: mac x64 or arm64 -real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() +# Detect platform +arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() if platform.system() == "Darwin": arch = "Darwin" brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip() -elif arch == "aarch64" and AGNOS: +elif arch == "aarch64" and os.path.isfile('/TICI'): arch = "larch64" -assert arch in ["larch64", "aarch64", "x86_64", "Darwin"] - -lenv = { - "PATH": os.environ['PATH'], - "PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath, - - "ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath, - "ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath, - "TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer" -} - -rpath = [] - -if arch == "larch64": - cpppath = [ - "#third_party/opencl/include", - ] - - libpath = [ - "/usr/local/lib", - "/system/vendor/lib64", - f"#third_party/acados/{arch}/lib", - ] - - libpath += [ - "#third_party/libyuv/larch64/lib", - "/usr/lib/aarch64-linux-gnu" - ] - cflags = ["-DQCOM2", "-mcpu=cortex-a57"] - cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"] - rpath += ["/usr/local/lib"] -else: - cflags = [] - cxxflags = [] - cpppath = [] - rpath += [] - - # MacOS - if arch == "Darwin": - libpath = [ - f"#third_party/libyuv/{arch}/lib", - f"#third_party/acados/{arch}/lib", - f"{brew_prefix}/lib", - f"{brew_prefix}/opt/openssl@3.0/lib", - f"{brew_prefix}/opt/llvm/lib/c++", - "/System/Library/Frameworks/OpenGL.framework/Libraries", - ] - - cflags += ["-DGL_SILENCE_DEPRECATION"] - cxxflags += ["-DGL_SILENCE_DEPRECATION"] - cpppath += [ - f"{brew_prefix}/include", - f"{brew_prefix}/opt/openssl@3.0/include", - ] - # Linux - else: - libpath = [ - f"#third_party/acados/{arch}/lib", - f"#third_party/libyuv/{arch}/lib", - "/usr/lib", - "/usr/local/lib", - ] - -if GetOption('asan'): - ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"] - ldflags = ["-fsanitize=address"] -elif GetOption('ubsan'): - ccflags = ["-fsanitize=undefined"] - ldflags = ["-fsanitize=undefined"] -else: - ccflags = [] - ldflags = [] - -# no --as-needed on mac linker -if arch != "Darwin": - ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"] - -ccflags_option = GetOption('ccflags') -if ccflags_option: - ccflags += ccflags_option.split(' ') +assert arch in [ + "larch64", # linux tici arm64 + "aarch64", # linux pc arm64 + "x86_64", # linux pc x64 + "Darwin", # macOS arm64 (x86 not supported) +] env = Environment( - ENV=lenv, + ENV={ + "PATH": os.environ['PATH'], + "PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath, + "ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath, + "ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath, + "TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer" + }, + CC='clang', + CXX='clang++', CCFLAGS=[ "-g", "-fPIC", @@ -153,36 +61,31 @@ env = Environment( "-Wno-c99-designator", "-Wno-reorder-init-list", "-Wno-vla-cxx-extension", - ] + cflags + ccflags, - - CPPPATH=cpppath + [ + ], + CFLAGS=["-std=gnu11"], + CXXFLAGS=["-std=c++1z"], + CPPPATH=[ "#", + "#msgq", + "#third_party", + "#third_party/json11", + "#third_party/linux/include", "#third_party/acados/include", "#third_party/acados/include/blasfeo/include", "#third_party/acados/include/hpipm/include", "#third_party/catch2/include", "#third_party/libyuv/include", - "#third_party/json11", - "#third_party/linux/include", - "#third_party", - "#msgq", ], - - CC='clang', - CXX='clang++', - LINKFLAGS=ldflags, - - RPATH=rpath, - - CFLAGS=["-std=gnu11"] + cflags, - CXXFLAGS=["-std=c++1z"] + cxxflags, - LIBPATH=libpath + [ + LIBPATH=[ + "#common", "#msgq_repo", "#third_party", "#selfdrive/pandad", - "#common", "#rednose/helpers", + f"#third_party/libyuv/{arch}/lib", + f"#third_party/acados/{arch}/lib", ], + RPATH=[], CYTHONCFILESUFFIX=".cpp", COMPILATIONDB_USE_ABSPATH=True, REDNOSE_ROOT="#", @@ -190,29 +93,63 @@ env = Environment( toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"], ) -if arch == "Darwin": - # RPATH is not supported on macOS, instead use the linker flags - darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]] - env["LINKFLAGS"] += darwin_rpath_link_flags +# Arch-specific flags and paths +if arch == "larch64": + env.Append(CPPPATH=["#third_party/opencl/include"]) + env.Append(LIBPATH=[ + "/usr/local/lib", + "/system/vendor/lib64", + "/usr/lib/aarch64-linux-gnu", + ]) + arch_flags = ["-D__TICI__", "-mcpu=cortex-a57"] + env.Append(CCFLAGS=arch_flags) + env.Append(CXXFLAGS=arch_flags) +elif arch == "Darwin": + env.Append(LIBPATH=[ + f"{brew_prefix}/lib", + f"{brew_prefix}/opt/openssl@3.0/lib", + f"{brew_prefix}/opt/llvm/lib/c++", + "/System/Library/Frameworks/OpenGL.framework/Libraries", + ]) + env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"]) + env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"]) + env.Append(CPPPATH=[ + f"{brew_prefix}/include", + f"{brew_prefix}/opt/openssl@3.0/include", + ]) +else: + env.Append(LIBPATH=[ + "/usr/lib", + "/usr/local/lib", + ]) -env.CompilationDatabase('compile_commands.json') +# Sanitizers and extra CCFLAGS from CLI +if GetOption('asan'): + env.Append(CCFLAGS=["-fsanitize=address", "-fno-omit-frame-pointer"]) + env.Append(LINKFLAGS=["-fsanitize=address"]) +elif GetOption('ubsan'): + env.Append(CCFLAGS=["-fsanitize=undefined"]) + env.Append(LINKFLAGS=["-fsanitize=undefined"]) -# Setup cache dir -cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache' -CacheDir(cache_dir) -Clean(["."], cache_dir) +_extra_cc = shlex.split(GetOption('ccflags') or '') +if _extra_cc: + env.Append(CCFLAGS=_extra_cc) +# no --as-needed on mac linker +if arch != "Darwin": + env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"]) + +# progress output node_interval = 5 node_count = 0 def progress_function(node): global node_count node_count += node_interval sys.stderr.write("progress: %d\n" % node_count) - if os.environ.get('SCONS_PROGRESS'): Progress(progress_function, interval=node_interval) -# Cython build environment +# ********** Cython build environment ********** py_include = sysconfig.get_paths()['include'] envCython = env.Clone() envCython["CPPPATH"] += [py_include, np.get_include()] @@ -221,14 +158,14 @@ envCython["CCFLAGS"].remove("-Werror") envCython["LIBS"] = [] if arch == "Darwin": - envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags + envCython["LINKFLAGS"] = env["LINKFLAGS"] + ["-bundle", "-undefined", "dynamic_lookup"] else: envCython["LINKFLAGS"] = ["-pthread", "-shared"] np_version = SCons.Script.Value(np.__version__) Export('envCython', 'np_version') -# Qt build environment +# ********** Qt build environment ********** qt_env = env.Clone() qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "DBus", "Xml"] @@ -278,16 +215,20 @@ qt_env['CXXFLAGS'] += qt_flags qt_env['LIBPATH'] += ['#selfdrive/ui', ] qt_env['LIBS'] = qt_libs -Export('env', 'qt_env', 'arch', 'real_arch') +Export('env', 'qt_env', 'arch') + +# Setup cache dir +cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache' +CacheDir(cache_dir) +Clean(["."], cache_dir) + +# ********** start building stuff ********** # Build common module SConscript(['common/SConscript']) -Import('_common', '_gpucommon') - +Import('_common') common = [_common, 'json11', 'zmq'] -gpucommon = [_gpucommon] - -Export('common', 'gpucommon') +Export('common') # Build messaging (cereal + msgq + socketmaster + their dependencies) # Enable swaglog include in submodules @@ -327,3 +268,6 @@ if Dir('#tools/cabana/').exists() and GetOption('extras'): SConscript(['tools/replay/SConscript']) if arch != "larch64": SConscript(['tools/cabana/SConscript']) + + +env.CompilationDatabase('compile_commands.json') diff --git a/common/SConscript b/common/SConscript index 3cdb6fc5a2..0891b79039 100644 --- a/common/SConscript +++ b/common/SConscript @@ -5,17 +5,12 @@ common_libs = [ 'swaglog.cc', 'util.cc', 'watchdog.cc', - 'ratekeeper.cc' -] - -_common = env.Library('common', common_libs, LIBS="json11") - -files = [ + 'ratekeeper.cc', 'clutil.cc', ] -_gpucommon = env.Library('gpucommon', files) -Export('_common', '_gpucommon') +_common = env.Library('common', common_libs, LIBS="json11") +Export('_common') if GetOption('extras'): env.Program('tests/test_common', diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index 1a6df4bb9b..f20855c2cb 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -1,11 +1,11 @@ import os import glob -Import('env', 'envCython', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc', 'transformations') +Import('env', 'envCython', 'arch', 'cereal', 'messaging', 'common', 'visionipc', 'transformations') lenv = env.Clone() lenvCython = envCython.Clone() -libs = [cereal, messaging, visionipc, gpucommon, common, 'capnp', 'kj', 'pthread'] +libs = [cereal, messaging, visionipc, common, 'capnp', 'kj', 'pthread'] frameworks = [] common_src = [ diff --git a/selfdrive/ui/qt/qt_window.cc b/selfdrive/ui/qt/qt_window.cc index 8d3d7cf72e..cdd817ae2f 100644 --- a/selfdrive/ui/qt/qt_window.cc +++ b/selfdrive/ui/qt/qt_window.cc @@ -13,7 +13,7 @@ void setMainWindow(QWidget *w) { } w->show(); -#ifdef QCOM2 +#ifdef __TICI__ QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); wl_surface *s = reinterpret_cast(native->nativeResourceForWindow("surface", w->windowHandle())); wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270); diff --git a/selfdrive/ui/qt/qt_window.h b/selfdrive/ui/qt/qt_window.h index 6f16e00957..b9783477eb 100644 --- a/selfdrive/ui/qt/qt_window.h +++ b/selfdrive/ui/qt/qt_window.h @@ -6,7 +6,7 @@ #include #include -#ifdef QCOM2 +#ifdef __TICI__ #include #include #include diff --git a/selfdrive/ui/qt/widgets/cameraview.cc b/selfdrive/ui/qt/widgets/cameraview.cc index 81ef613393..5f533cd090 100644 --- a/selfdrive/ui/qt/widgets/cameraview.cc +++ b/selfdrive/ui/qt/widgets/cameraview.cc @@ -27,7 +27,7 @@ const char frame_vertex_shader[] = "}\n"; const char frame_fragment_shader[] = -#ifdef QCOM2 +#ifdef __TICI__ "#version 300 es\n" "#extension GL_OES_EGL_image_external_essl3 : enable\n" "precision mediump float;\n" @@ -79,7 +79,7 @@ CameraWidget::~CameraWidget() { glDeleteVertexArrays(1, &frame_vao); glDeleteBuffers(1, &frame_vbo); glDeleteBuffers(1, &frame_ibo); -#ifndef QCOM2 +#ifndef __TICI__ glDeleteTextures(2, textures); #endif } @@ -137,7 +137,7 @@ void CameraWidget::initializeGL() { glUseProgram(program->programId()); -#ifdef QCOM2 +#ifdef __TICI__ glUniform1i(program->uniformLocation("uTexture"), 0); #else glGenTextures(2, textures); @@ -165,7 +165,7 @@ void CameraWidget::stopVipcThread() { vipc_thread = nullptr; } -#ifdef QCOM2 +#ifdef __TICI__ EGLDisplay egl_display = eglGetCurrentDisplay(); assert(egl_display != EGL_NO_DISPLAY); for (auto &pair : egl_images) { @@ -226,7 +226,7 @@ void CameraWidget::paintGL() { glUseProgram(program->programId()); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); -#ifdef QCOM2 +#ifdef __TICI__ // no frame copy glActiveTexture(GL_TEXTURE0); glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_images[frame->idx]); @@ -263,7 +263,7 @@ void CameraWidget::vipcConnected(VisionIpcClient *vipc_client) { stream_height = vipc_client->buffers[0].height; stream_stride = vipc_client->buffers[0].stride; -#ifdef QCOM2 +#ifdef __TICI__ EGLDisplay egl_display = eglGetCurrentDisplay(); assert(egl_display != EGL_NO_DISPLAY); for (auto &pair : egl_images) { diff --git a/selfdrive/ui/qt/widgets/cameraview.h b/selfdrive/ui/qt/widgets/cameraview.h index 29aa8493c7..598603a08a 100644 --- a/selfdrive/ui/qt/widgets/cameraview.h +++ b/selfdrive/ui/qt/widgets/cameraview.h @@ -13,7 +13,7 @@ #include #include -#ifdef QCOM2 +#ifdef __TICI__ #define EGL_EGLEXT_PROTOTYPES #define EGL_NO_X11 #define GL_TEXTURE_EXTERNAL_OES 0x8D65 @@ -63,7 +63,7 @@ protected: std::unique_ptr program; QColor bg = QColor("#000000"); -#ifdef QCOM2 +#ifdef __TICI__ std::map egl_images; #endif diff --git a/system/camerad/SConscript b/system/camerad/SConscript index 734f748a2a..e288c6d8b0 100644 --- a/system/camerad/SConscript +++ b/system/camerad/SConscript @@ -1,6 +1,6 @@ -Import('env', 'arch', 'messaging', 'common', 'gpucommon', 'visionipc') +Import('env', 'arch', 'messaging', 'common', 'visionipc') -libs = [common, 'OpenCL', messaging, visionipc, gpucommon] +libs = [common, 'OpenCL', messaging, visionipc] if arch != "Darwin": camera_obj = env.Object(['cameras/camera_qcom2.cc', 'cameras/camera_common.cc', 'cameras/spectra.cc', diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 8c4602bb31..85f358977c 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -12,7 +12,7 @@ #include #include -#ifdef QCOM2 +#ifdef __TICI__ #include "CL/cl_ext_qcom.h" #else #define CL_PRIORITY_HINT_HIGH_QCOM NULL diff --git a/system/hardware/hw.h b/system/hardware/hw.h index d2083a5985..a9058401ce 100644 --- a/system/hardware/hw.h +++ b/system/hardware/hw.h @@ -5,7 +5,7 @@ #include "system/hardware/base.h" #include "common/util.h" -#if QCOM2 +#if __TICI__ #include "system/hardware/tici/hardware.h" #define Hardware HardwareTici #else diff --git a/system/loggerd/encoderd.cc b/system/loggerd/encoderd.cc index 3237d13074..9d4b81a3f9 100644 --- a/system/loggerd/encoderd.cc +++ b/system/loggerd/encoderd.cc @@ -3,7 +3,7 @@ #include "system/loggerd/loggerd.h" #include "system/loggerd/encoder/jpeg_encoder.h" -#ifdef QCOM2 +#ifdef __TICI__ #include "system/loggerd/encoder/v4l_encoder.h" #define Encoder V4LEncoder #else From 1fbec6f601515599a39e65372ae4e256c1d8e642 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 25 Sep 2025 21:02:26 -0700 Subject: [PATCH 048/341] remove .clang-tidy --- .clang-tidy | 19 ------------------- .dockerignore | 21 --------------------- .gitignore | 9 +-------- 3 files changed, 1 insertion(+), 48 deletions(-) delete mode 100644 .clang-tidy diff --git a/.clang-tidy b/.clang-tidy deleted file mode 100644 index cc55fefd25..0000000000 --- a/.clang-tidy +++ /dev/null @@ -1,19 +0,0 @@ ---- -Checks: ' -bugprone-*, --bugprone-integer-division, --bugprone-narrowing-conversions, -performance-*, -clang-analyzer-*, -misc-*, --misc-unused-parameters, -modernize-*, --modernize-avoid-c-arrays, --modernize-deprecated-headers, --modernize-use-auto, --modernize-use-using, --modernize-use-nullptr, --modernize-use-trailing-return-type, -' -CheckOptions: -... diff --git a/.dockerignore b/.dockerignore index cfc34e5848..631ea04840 100644 --- a/.dockerignore +++ b/.dockerignore @@ -13,27 +13,6 @@ *.o-* *.os *.os-* -*.so -*.a venv/ .venv/ - -notebooks -phone -massivemap -neos -installer -chffr/app2 -chffr/backend/env -selfdrive/nav -selfdrive/baseui -selfdrive/test/simulator2 -**/cache_data -xx/plus -xx/community -xx/projects -!xx/projects/eon_testing_master -!xx/projects/map3d -xx/ops -xx/junk diff --git a/.gitignore b/.gitignore index c594fb53d3..0832b3964e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ venv/ .overlay_init .overlay_consistent .sconsign.dblite -model2.png a.out .hypothesis .cache/ @@ -44,22 +43,15 @@ clcache compile_commands.json compare_runtime*.html -persist selfdrive/pandad/pandad cereal/services.h cereal/gen cereal/messaging/bridge -selfdrive/mapd/default_speeds_by_region.json selfdrive/ui/translations/tmp -selfdrive/test/longitudinal_maneuvers/out selfdrive/car/tests/cars_dump system/camerad/camerad system/camerad/test/ae_gray_test -notebooks -hyperthneed -provisioning - .coverage* coverage.xml htmlcov @@ -73,6 +65,7 @@ comma*.sh selfdrive/modeld/models/*.pkl +# openpilot log files *.bz2 *.zst From 33f01084d16301025a58a7eba42083c74998eb6a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 25 Sep 2025 23:44:12 -0700 Subject: [PATCH 049/341] raylib: implement cell settings (#36204) * get vibing * simplify * vibing is bad * simplify * fix that * now update * clean up * last two * cell is UpdateUnsaved so we don't need to disable * we only need actions * we only need actions * sort * stuff * dont deactivate * clean up * clean up * more * ipv4 fwd * warns * fixz * rm * clean up * one return point * format * top --- system/ui/lib/wifi_manager.py | 125 +++++++++++++++++++++++++++++----- system/ui/widgets/network.py | 82 +++++++++++++++++++--- 2 files changed, 180 insertions(+), 27 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 3ce5c839e8..e24686566d 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -2,6 +2,7 @@ import atexit import threading import time import uuid +import subprocess from collections.abc import Callable from dataclasses import dataclass from enum import IntEnum @@ -23,7 +24,7 @@ from openpilot.system.ui.lib.networkmanager import (NM, NM_WIRELESS_IFACE, NM_80 NM_802_11_AP_FLAGS_PRIVACY, NM_802_11_AP_FLAGS_WPS, NM_PATH, NM_IFACE, NM_ACCESS_POINT_IFACE, NM_SETTINGS_PATH, NM_SETTINGS_IFACE, NM_CONNECTION_IFACE, NM_DEVICE_IFACE, - NM_DEVICE_TYPE_WIFI, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT, + NM_DEVICE_TYPE_WIFI, NM_DEVICE_TYPE_MODEM, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT, NM_DEVICE_STATE_REASON_NEW_ACTIVATION, NM_ACTIVE_CONNECTION_IFACE, NM_IP4_CONFIG_IFACE, NMDeviceState) @@ -142,6 +143,8 @@ class WifiManager: self._ipv4_address: str = "" self._current_network_metered: MeteredType = MeteredType.UNKNOWN self._tethering_password: str = "" + self._ipv4_forward = False + self._last_network_update: float = 0.0 self._callback_queue: list[Callable] = [] @@ -277,25 +280,24 @@ class WifiManager: def _wait_for_wifi_device(self): while not self._exit: - device_path = self._get_wifi_device() + device_path = self._get_adapter(NM_DEVICE_TYPE_WIFI) if device_path is not None: + self._wifi_device = device_path break time.sleep(1) - def _get_wifi_device(self) -> str | None: - if self._wifi_device is not None: - return self._wifi_device - - device_paths = self._router_main.send_and_get_reply(new_method_call(self._nm, 'GetDevices')).body[0] - for device_path in device_paths: - dev_addr = DBusAddress(device_path, bus_name=NM, interface=NM_DEVICE_IFACE) - dev_type = self._router_main.send_and_get_reply(Properties(dev_addr).get('DeviceType')).body[0][1] - - if dev_type == NM_DEVICE_TYPE_WIFI: - self._wifi_device = device_path - break - - return self._wifi_device + def _get_adapter(self, adapter_type: int) -> str | None: + # Return the first NetworkManager device path matching adapter_type + try: + device_paths = self._router_main.send_and_get_reply(new_method_call(self._nm, 'GetDevices')).body[0] + for device_path in device_paths: + dev_addr = DBusAddress(device_path, bus_name=NM, interface=NM_DEVICE_IFACE) + dev_type = self._router_main.send_and_get_reply(Properties(dev_addr).get('DeviceType')).body[0][1] + if dev_type == adapter_type: + return str(device_path) + except Exception as e: + cloudlog.exception(f"Error getting adapter type {adapter_type}: {e}") + return None def _get_connections(self) -> dict[str, str]: settings_addr = DBusAddress(NM_SETTINGS_PATH, bus_name=NM, interface=NM_SETTINGS_IFACE) @@ -500,10 +502,18 @@ class WifiManager: return str(secrets['802-11-wireless-security'].get('psk', ('s', ''))[1]) + def set_ipv4_forward(self, enabled: bool): + self._ipv4_forward = enabled + def set_tethering_active(self, active: bool): def worker(): if active: self.activate_connection(self._tethering_ssid, block=True) + + if not self._ipv4_forward: + time.sleep(5) + cloudlog.warning("net.ipv4.ip_forward = 0") + subprocess.run(["sudo", "sysctl", "net.ipv4.ip_forward=0"], check=False) else: self._deactivate_connection(self._tethering_ssid) @@ -645,6 +655,89 @@ class WifiManager: def __del__(self): self.stop() + def update_gsm_settings(self, roaming: bool, apn: str, metered: bool): + """Update GSM settings for cellular connection""" + + def worker(): + try: + lte_connection_path = self._get_lte_connection_path() + if not lte_connection_path: + cloudlog.warning("No LTE connection found") + return + + settings = self._get_connection_settings(lte_connection_path) + + if len(settings) == 0: + cloudlog.warning(f"Failed to get connection settings for {lte_connection_path}") + return + + # Ensure dicts exist + if 'gsm' not in settings: + settings['gsm'] = {} + if 'connection' not in settings: + settings['connection'] = {} + + changes = False + auto_config = apn == "" + + if settings['gsm'].get('auto-config', ('b', False))[1] != auto_config: + cloudlog.warning(f'Changing gsm.auto-config to {auto_config}') + settings['gsm']['auto-config'] = ('b', auto_config) + changes = True + + if settings['gsm'].get('apn', ('s', ''))[1] != apn: + cloudlog.warning(f'Changing gsm.apn to {apn}') + settings['gsm']['apn'] = ('s', apn) + changes = True + + if settings['gsm'].get('home-only', ('b', False))[1] == roaming: + cloudlog.warning(f'Changing gsm.home-only to {not roaming}') + settings['gsm']['home-only'] = ('b', not roaming) + changes = True + + # Unknown means NetworkManager decides + metered_int = int(MeteredType.UNKNOWN if metered else MeteredType.NO) + if settings['connection'].get('metered', ('i', 0))[1] != metered_int: + cloudlog.warning(f'Changing connection.metered to {metered_int}') + settings['connection']['metered'] = ('i', metered_int) + changes = True + + if changes: + # Update the connection settings (temporary update) + conn_addr = DBusAddress(lte_connection_path, bus_name=NM, interface=NM_CONNECTION_IFACE) + reply = self._router_main.send_and_get_reply(new_method_call(conn_addr, 'UpdateUnsaved', 'a{sa{sv}}', (settings,))) + + if reply.header.message_type == MessageType.error: + cloudlog.warning(f"Failed to update GSM settings: {reply}") + return + + self._activate_modem_connection(lte_connection_path) + except Exception as e: + cloudlog.exception(f"Error updating GSM settings: {e}") + + threading.Thread(target=worker, daemon=True).start() + + def _get_lte_connection_path(self) -> str | None: + try: + settings_addr = DBusAddress(NM_SETTINGS_PATH, bus_name=NM, interface=NM_SETTINGS_IFACE) + known_connections = self._router_main.send_and_get_reply(new_method_call(settings_addr, 'ListConnections')).body[0] + + for conn_path in known_connections: + settings = self._get_connection_settings(conn_path) + if settings and settings.get('connection', {}).get('id', ('s', ''))[1] == 'lte': + return str(conn_path) + except Exception as e: + cloudlog.exception(f"Error finding LTE connection: {e}") + return None + + def _activate_modem_connection(self, connection_path: str): + try: + modem_device = self._get_adapter(NM_DEVICE_TYPE_MODEM) + if modem_device and connection_path: + self._router_main.send_and_get_reply(new_method_call(self._nm, 'ActivateConnection', 'ooo', (connection_path, modem_device, "/"))) + except Exception as e: + cloudlog.exception(f"Error activating modem connection: {e}") + def stop(self): if not self._exit: self._exit = True diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 7a7215b48e..119901da40 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -4,6 +4,7 @@ from typing import cast import pyray as rl from openpilot.common.filter_simple import FirstOrderFilter +from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app, DEFAULT_FPS from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType @@ -13,7 +14,9 @@ from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.label import TextAlignment, gui_label from openpilot.system.ui.widgets.scroller import Scroller -from openpilot.system.ui.widgets.list_view import text_item, button_item, ListItem, ToggleAction, MultipleButtonAction, ButtonAction +from openpilot.system.ui.widgets.list_view import ButtonAction, ListItem, MultipleButtonAction, ToggleAction, button_item, text_item +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.lib.prime_state import PrimeType NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 @@ -114,34 +117,54 @@ class AdvancedNetworkSettings(Widget): super().__init__() self._wifi_manager = wifi_manager self._wifi_manager.set_callbacks(networks_updated=self._on_network_updated) + self._params = Params() self._keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) # Tethering self._tethering_action = ToggleAction(initial_state=False) - self._tethering_btn = ListItem(title="Enable Tethering", action_item=self._tethering_action, callback=self._toggle_tethering) + tethering_btn = ListItem(title="Enable Tethering", action_item=self._tethering_action, callback=self._toggle_tethering) - # Tethering Password + # Edit tethering password self._tethering_password_action = ButtonAction(text="EDIT") - self._tethering_password_btn = ListItem(title="Tethering Password", action_item=self._tethering_password_action, callback=self._edit_tethering_password) + tethering_password_btn = ListItem(title="Tethering Password", action_item=self._tethering_password_action, callback=self._edit_tethering_password) - # TODO: Roaming toggle, edit APN settings, and cellular metered toggle + # Roaming toggle + roaming_enabled = self._params.get_bool("GsmRoaming") + self._roaming_action = ToggleAction(initial_state=roaming_enabled) + self._roaming_btn = ListItem(title="Enable Roaming", action_item=self._roaming_action, callback=self._toggle_roaming) - # Metered + # Cellular metered toggle + cellular_metered = self._params.get_bool("GsmMetered") + self._cellular_metered_action = ToggleAction(initial_state=cellular_metered) + self._cellular_metered_btn = ListItem(title="Cellular Metered", description="Prevent large data uploads when on a metered cellular connection", + action_item=self._cellular_metered_action, callback=self._toggle_cellular_metered) + + # APN setting + self._apn_btn = button_item("APN Setting", "EDIT", callback=self._edit_apn) + + # Wi-Fi metered toggle self._wifi_metered_action = MultipleButtonAction(["default", "metered", "unmetered"], 255, 0, callback=self._toggle_wifi_metered) - self._wifi_metered_btn = ListItem(title="Wi-Fi Network Metered", description="Prevent large data uploads when on a metered Wi-Fi connection", - action_item=self._wifi_metered_action) + wifi_metered_btn = ListItem(title="Wi-Fi Network Metered", description="Prevent large data uploads when on a metered Wi-Fi connection", + action_item=self._wifi_metered_action) items: list[Widget] = [ - self._tethering_btn, - self._tethering_password_btn, + tethering_btn, + tethering_password_btn, text_item("IP Address", lambda: self._wifi_manager.ipv4_address), - self._wifi_metered_btn, + self._roaming_btn, + self._apn_btn, + self._cellular_metered_btn, + wifi_metered_btn, button_item("Hidden Network", "CONNECT", callback=self._connect_to_hidden_network), ] self._scroller = Scroller(items, line_separator=True, spacing=0) + # Set initial config + metered = self._params.get_bool("GsmMetered") + self._wifi_manager.update_gsm_settings(roaming_enabled, self._params.get("GsmApn") or "", metered) + def _on_network_updated(self, networks: list[Network]): self._tethering_action.set_enabled(True) self._tethering_action.set_state(self._wifi_manager.is_tethering_active()) @@ -162,6 +185,35 @@ class AdvancedNetworkSettings(Widget): self._wifi_metered_action.set_enabled(False) self._wifi_manager.set_tethering_active(checked) + def _toggle_roaming(self): + roaming_state = self._roaming_action.state + self._params.put_bool("GsmRoaming", roaming_state) + self._wifi_manager.update_gsm_settings(roaming_state, self._params.get("GsmApn") or "", self._params.get_bool("GsmMetered")) + + def _edit_apn(self): + def update_apn(result): + if result != 1: + return + + apn = self._keyboard.text.strip() + if apn == "": + self._params.remove("GsmApn") + else: + self._params.put("GsmApn", apn) + + self._wifi_manager.update_gsm_settings(self._params.get_bool("GsmRoaming"), apn, self._params.get_bool("GsmMetered")) + + current_apn = self._params.get("GsmApn") or "" + self._keyboard.reset(min_text_size=0) + self._keyboard.set_title("Enter APN", "leave blank for automatic configuration") + self._keyboard.set_text(current_apn) + gui_app.set_modal_overlay(self._keyboard, update_apn) + + def _toggle_cellular_metered(self): + metered = self._cellular_metered_action.state + self._params.put_bool("GsmMetered", metered) + self._wifi_manager.update_gsm_settings(self._params.get_bool("GsmRoaming"), self._params.get("GsmApn") or "", metered) + def _toggle_wifi_metered(self, metered): metered_type = {0: MeteredType.UNKNOWN, 1: MeteredType.YES, 2: MeteredType.NO}.get(metered, MeteredType.UNKNOWN) self._wifi_metered_action.set_enabled(False) @@ -207,6 +259,14 @@ class AdvancedNetworkSettings(Widget): self._keyboard.set_text(self._wifi_manager.tethering_password) gui_app.set_modal_overlay(self._keyboard, update_password) + def _update_state(self): + # If not using prime SIM, show GSM settings and enable IPv4 forwarding + show_cell_settings = ui_state.prime_state.get_type() in (PrimeType.NONE, PrimeType.LITE) + self._wifi_manager.set_ipv4_forward(show_cell_settings) + self._roaming_btn.set_visible(show_cell_settings) + self._apn_btn.set_visible(show_cell_settings) + self._cellular_metered_btn.set_visible(show_cell_settings) + def _render(self, _): self._scroller.render(self._rect) From 0711160b1c1c88f939401b7ae6f5664b834d9c52 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 18:59:15 -0700 Subject: [PATCH 050/341] raylib: dismiss dialog on pair (#36205) * show for unknown * use Button to make clicking work * close on pair * close on pair * make widget! * dynamic pairing btn * whyyy * clean up * can do this * this button is also hard to tap --- selfdrive/ui/layouts/settings/device.py | 5 ++++- selfdrive/ui/lib/prime_state.py | 20 ++++++++++++-------- selfdrive/ui/ui.py | 9 ++++----- selfdrive/ui/widgets/pairing_dialog.py | 11 +++++++++-- selfdrive/ui/widgets/setup.py | 15 +++++++-------- system/ui/lib/application.py | 3 ++- system/ui/widgets/scroller.py | 11 ++++++++--- 7 files changed, 46 insertions(+), 28 deletions(-) diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index df8bba030c..14847df102 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -44,10 +44,13 @@ class DeviceLayout(Widget): dongle_id = self._params.get("DongleId") or "N/A" serial = self._params.get("HardwareSerial") or "N/A" + self._pair_device_btn = button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device) + self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired()) + items = [ text_item("Dongle ID", dongle_id), text_item("Serial", serial), - button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device), + self._pair_device_btn, button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt), regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), diff --git a/selfdrive/ui/lib/prime_state.py b/selfdrive/ui/lib/prime_state.py index be2132c1b7..30ad0f763a 100644 --- a/selfdrive/ui/lib/prime_state.py +++ b/selfdrive/ui/lib/prime_state.py @@ -11,14 +11,14 @@ from openpilot.selfdrive.ui.lib.api_helpers import get_token class PrimeType(IntEnum): - UNKNOWN = -2, - UNPAIRED = -1, - NONE = 0, - MAGENTA = 1, - LITE = 2, - BLUE = 3, - MAGENTA_NEW = 4, - PURPLE = 5, + UNKNOWN = -2 + UNPAIRED = -1 + NONE = 0 + MAGENTA = 1 + LITE = 2 + BLUE = 3 + MAGENTA_NEW = 4 + PURPLE = 5 class PrimeState: @@ -96,5 +96,9 @@ class PrimeState: with self._lock: return bool(self.prime_type > PrimeType.NONE) + def is_paired(self) -> bool: + with self._lock: + return self.prime_type > PrimeType.UNPAIRED + def __del__(self): self.stop() diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index bb2b431e03..0230e16a4f 100755 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -10,15 +10,14 @@ def main(): gui_app.init_window("UI") main_layout = MainLayout() main_layout.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) - for _ in gui_app.render(): + for showing_dialog in gui_app.render(): ui_state.update() - # TODO handle brigntness and awake state here - - main_layout.render() - kick_watchdog() + if not showing_dialog: + main_layout.render() + if __name__ == "__main__": main() diff --git a/selfdrive/ui/widgets/pairing_dialog.py b/selfdrive/ui/widgets/pairing_dialog.py index 79d05eaf42..55d53125d8 100644 --- a/selfdrive/ui/widgets/pairing_dialog.py +++ b/selfdrive/ui/widgets/pairing_dialog.py @@ -6,17 +6,20 @@ import time from openpilot.common.api import Api from openpilot.common.swaglog import cloudlog from openpilot.common.params import Params +from openpilot.system.ui.widgets import Widget from openpilot.system.ui.lib.application import FontWeight, gui_app from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.selfdrive.ui.ui_state import ui_state -class PairingDialog: +class PairingDialog(Widget): """Dialog for device pairing with QR code.""" QR_REFRESH_INTERVAL = 300 # 5 minutes in seconds def __init__(self): + super().__init__() self.params = Params() self.qr_texture: rl.Texture | None = None self.last_qr_generation = 0 @@ -60,7 +63,11 @@ class PairingDialog: self._generate_qr_code() self.last_qr_generation = current_time - def render(self, rect: rl.Rectangle) -> int: + def _update_state(self): + if ui_state.prime_state.is_paired(): + gui_app.set_modal_overlay(None) + + def _render(self, rect: rl.Rectangle) -> int: rl.clear_background(rl.Color(224, 224, 224, 255)) self._check_qr_refresh() diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index 35a7c4101c..bf6d113f62 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -1,11 +1,10 @@ import pyray as rl -from openpilot.selfdrive.ui.lib.prime_state import PrimeType from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle +from openpilot.system.ui.widgets.button import Button, ButtonStyle class SetupWidget(Widget): @@ -13,12 +12,15 @@ class SetupWidget(Widget): super().__init__() self._open_settings_callback = None self._pairing_dialog: PairingDialog | None = None + self._pair_device_btn = Button("Pair device", self._show_pairing, button_style=ButtonStyle.PRIMARY) + self._open_settings_btn = Button("Open", lambda: self._open_settings_callback() if self._open_settings_callback else None, + button_style=ButtonStyle.PRIMARY) def set_open_settings_callback(self, callback): self._open_settings_callback = callback def _render(self, rect: rl.Rectangle): - if ui_state.prime_state.get_type() == PrimeType.UNPAIRED: + if not ui_state.prime_state.is_paired(): self._render_registration(rect) else: self._render_firehose_prompt(rect) @@ -46,8 +48,7 @@ class SetupWidget(Widget): y += 50 button_rect = rl.Rectangle(x, y + 50, w, 128) - if gui_button(button_rect, "Pair device", button_style=ButtonStyle.PRIMARY): - self._show_pairing() + self._pair_device_btn.render(button_rect) def _render_firehose_prompt(self, rect: rl.Rectangle): """Render firehose prompt widget.""" @@ -80,9 +81,7 @@ class SetupWidget(Widget): # Open button button_height = 48 + 64 # font size + padding button_rect = rl.Rectangle(x, y, w, button_height) - if gui_button(button_rect, "Open", button_style=ButtonStyle.PRIMARY): - if self._open_settings_callback: - self._open_settings_callback() + self._open_settings_btn.render(button_rect) def _show_pairing(self): if not self._pairing_dialog: diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 5b35f7ac9d..911891f10b 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -274,8 +274,9 @@ class GuiApplication: self._modal_overlay = ModalOverlay() if original_modal.callback is not None: original_modal.callback(result) + yield True else: - yield + yield False if self._render_texture: rl.end_texture_mode() diff --git a/system/ui/widgets/scroller.py b/system/ui/widgets/scroller.py index f7304bf6a6..757500a71c 100644 --- a/system/ui/widgets/scroller.py +++ b/system/ui/widgets/scroller.py @@ -27,7 +27,7 @@ class Scroller(Widget): super().__init__() self._items: list[Widget] = [] self._spacing = spacing - self._line_separator = line_separator + self._line_separator = LineSeparator() if line_separator else None self._pad_end = pad_end self.scroll_panel = GuiScrollPanel() @@ -36,14 +36,19 @@ class Scroller(Widget): self.add_widget(item) def add_widget(self, item: Widget) -> None: - if self._line_separator and len(self._items) > 0: - self._items.append(LineSeparator()) self._items.append(item) item.set_touch_valid_callback(self.scroll_panel.is_touch_valid) def _render(self, _): # TODO: don't draw items that are not in the viewport visible_items = [item for item in self._items if item.is_visible] + + # Add line separator between items + if self._line_separator is not None: + l = len(visible_items) + for i in range(1, len(visible_items)): + visible_items.insert(l - i, self._line_separator) + content_height = sum(item.rect.height for item in visible_items) + self._spacing * (len(visible_items)) if not self._pad_end: content_height -= self._spacing From 9297cd2f3e2b7586ebc56a463289735edcfde794 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 21:08:39 -0700 Subject: [PATCH 051/341] raylib: use filter for allow throttle (#36209) * use time here * use epic filter * rm * intermediary * tune --- selfdrive/ui/onroad/alert_renderer.py | 4 ++-- selfdrive/ui/onroad/model_renderer.py | 26 ++++++++------------------ selfdrive/ui/ui_state.py | 2 ++ 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index e8817f24b4..c529694df4 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from cereal import messaging, log from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.hardware import TICI -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_FPS +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.label import gui_text_box @@ -76,7 +76,7 @@ class AlertRenderer(Widget): # Check if selfdriveState messages have stopped arriving if not sm.updated['selfdriveState']: recv_frame = sm.recv_frame['selfdriveState'] - time_since_onroad = (sm.frame - ui_state.started_frame) / DEFAULT_FPS + time_since_onroad = time.monotonic() - ui_state.started_time # 1. Never received selfdriveState since going onroad waiting_for_startup = recv_frame < ui_state.started_frame diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 932773755d..89bdb48ac9 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -3,6 +3,7 @@ import numpy as np import pyray as rl from cereal import messaging, car from dataclasses import dataclass, field +from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.ui.ui_state import ui_state @@ -49,7 +50,7 @@ class ModelRenderer(Widget): super().__init__() self._longitudinal_control = False self._experimental_mode = False - self._blend_factor = 1.0 + self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / DEFAULT_FPS) self._prev_allow_throttle = True self._lane_line_probs = np.zeros(4, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32) @@ -277,6 +278,9 @@ class ModelRenderer(Widget): if not self._path.projected_points.size: return + allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control + self._blend_filter.update(int(allow_throttle)) + if self._experimental_mode: # Draw with acceleration coloring if len(self._exp_gradient['colors']) > 1: @@ -284,23 +288,9 @@ class ModelRenderer(Widget): else: draw_polygon(self._rect, self._path.projected_points, rl.Color(255, 255, 255, 30)) else: - # Draw with throttle/no throttle gradient - allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control - - # Start transition if throttle state changes - if allow_throttle != self._prev_allow_throttle: - self._prev_allow_throttle = allow_throttle - self._blend_factor = max(1.0 - self._blend_factor, 0.0) - - # Update blend factor - if self._blend_factor < 1.0: - self._blend_factor = min(self._blend_factor + PATH_BLEND_INCREMENT, 1.0) - - begin_colors = NO_THROTTLE_COLORS if allow_throttle else THROTTLE_COLORS - end_colors = THROTTLE_COLORS if allow_throttle else NO_THROTTLE_COLORS - - # Blend colors based on transition - blended_colors = self._blend_colors(begin_colors, end_colors, self._blend_factor) + # Blend throttle/no throttle colors based on transition + blend_factor = round(self._blend_filter.x * 100) / 100 + blended_colors = self._blend_colors(NO_THROTTLE_COLORS, THROTTLE_COLORS, blend_factor) gradient = { 'start': (0.0, 1.0), # Bottom of path 'end': (0.0, 0.0), # Top of path diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index c4a2c0ca11..13532620cb 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -59,6 +59,7 @@ class UIState: # UI Status tracking self.status: UIStatus = UIStatus.DISENGAGED self.started_frame: int = 0 + self.started_time: float = 0.0 self._engaged_prev: bool = False self._started_prev: bool = False @@ -131,6 +132,7 @@ class UIState: if self.started: self.status = UIStatus.DISENGAGED self.started_frame = self.sm.frame + self.started_time = time.monotonic() self._started_prev = self.started From 04365f12ffefc0978c83bb8d44917ebee08e601d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 21:10:09 -0700 Subject: [PATCH 052/341] raylib: remove unused globals --- selfdrive/ui/onroad/model_renderer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 89bdb48ac9..d84e16cb54 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -14,8 +14,6 @@ from openpilot.system.ui.widgets import Widget CLIP_MARGIN = 500 MIN_DRAW_DISTANCE = 10.0 MAX_DRAW_DISTANCE = 100.0 -PATH_COLOR_TRANSITION_DURATION = 0.5 # Seconds for color transition animation -PATH_BLEND_INCREMENT = 1.0 / (PATH_COLOR_TRANSITION_DURATION * DEFAULT_FPS) MAX_POINTS = 200 From 8de8c3eb00383cb5b8fa18c41ddc4899dccc1e61 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 21:38:59 -0700 Subject: [PATCH 053/341] raylib: 20 FPS onroad (#36208) * 20 * dynamic fps * flip * init to be safe * fix possible fps weirdness * gate on change * not now * rev --- common/filter_simple.py | 11 ++++++++--- selfdrive/ui/layouts/main.py | 10 ++++++++++ selfdrive/ui/onroad/model_renderer.py | 5 +++-- selfdrive/ui/ui_state.py | 4 ++-- system/ui/lib/application.py | 17 ++++++++++++----- system/ui/widgets/network.py | 8 +++++--- 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/common/filter_simple.py b/common/filter_simple.py index 9ea6fe3070..8a7105063d 100644 --- a/common/filter_simple.py +++ b/common/filter_simple.py @@ -1,16 +1,21 @@ class FirstOrderFilter: def __init__(self, x0, rc, dt, initialized=True): self.x = x0 - self.dt = dt + self._dt = dt self.update_alpha(rc) self.initialized = initialized + def update_dt(self, dt): + self._dt = dt + self.update_alpha(self._rc) + def update_alpha(self, rc): - self.alpha = self.dt / (rc + self.dt) + self._rc = rc + self._alpha = self._dt / (self._rc + self._dt) def update(self, x): if self.initialized: - self.x = (1. - self.alpha) * self.x + self.alpha * x + self.x = (1. - self._alpha) * self.x + self._alpha * x else: self.initialized = True self.x = x diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 777d2f4c3f..0a8fadea90 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -1,6 +1,7 @@ import pyray as rl from enum import IntEnum import cereal.messaging as messaging +from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH from openpilot.selfdrive.ui.layouts.home import HomeLayout from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType @@ -9,6 +10,10 @@ from openpilot.selfdrive.ui.ui_state import device, ui_state from openpilot.system.ui.widgets import Widget +ONROAD_FPS = 20 +OFFROAD_FPS = 60 + + class MainState(IntEnum): HOME = 0 SETTINGS = 1 @@ -25,6 +30,8 @@ class MainLayout(Widget): self._current_mode = MainState.HOME self._prev_onroad = False + gui_app.set_target_fps(OFFROAD_FPS) + # Initialize layouts self._layouts = {MainState.HOME: HomeLayout(), MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()} @@ -74,6 +81,9 @@ class MainLayout(Widget): self._current_mode = layout self._layouts[self._current_mode].show_event() + # No need to draw onroad faster than source (model at 20Hz) and prevents screen tearing + gui_app.set_target_fps(ONROAD_FPS if self._current_mode == MainState.ONROAD else OFFROAD_FPS) + def open_settings(self, panel_type: PanelType): self._layouts[MainState.SETTINGS].set_current_panel(panel_type) self._set_current_layout(MainState.SETTINGS) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index d84e16cb54..4c036873d0 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -7,7 +7,7 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import DEFAULT_FPS +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.shader_polygon import draw_polygon from openpilot.system.ui.widgets import Widget @@ -48,7 +48,7 @@ class ModelRenderer(Widget): super().__init__() self._longitudinal_control = False self._experimental_mode = False - self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / DEFAULT_FPS) + self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / gui_app.target_fps) self._prev_allow_throttle = True self._lane_line_probs = np.zeros(4, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32) @@ -277,6 +277,7 @@ class ModelRenderer(Widget): return allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control + self._blend_filter.update_dt(1 / gui_app.target_fps) self._blend_filter.update(int(allow_throttle)) if self._experimental_mode: diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 13532620cb..39a65a9191 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -9,7 +9,6 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params, UnknownKeyName from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.lib.prime_state import PrimeState -from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app @@ -153,7 +152,7 @@ class Device: self._offroad_brightness: int = BACKLIGHT_OFFROAD self._last_brightness: int = 0 - self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / DEFAULT_FPS) + self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / gui_app.target_fps) self._brightness_thread: threading.Thread | None = None def reset_interactive_timeout(self, timeout: int = -1) -> None: @@ -190,6 +189,7 @@ class Device: clipped_brightness = float(np.clip(100 * clipped_brightness, 10, 100)) + self._brightness_filter.update_dt(1 / gui_app.target_fps) brightness = round(self._brightness_filter.update(clipped_brightness)) if not self._awake: brightness = 0 diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 911891f10b..481fd98045 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -14,7 +14,7 @@ from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE, PC from openpilot.common.realtime import Ratekeeper -DEFAULT_FPS = int(os.getenv("FPS", "60")) +_DEFAULT_FPS = int(os.getenv("FPS", "60")) FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions @@ -130,7 +130,7 @@ class GuiApplication: self._scaled_height = int(self._height * self._scale) self._render_texture: rl.RenderTexture | None = None self._textures: dict[str, rl.Texture] = {} - self._target_fps: int = DEFAULT_FPS + self._target_fps: int = _DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() self._window_close_requested = False self._trace_log_callback = None @@ -145,7 +145,7 @@ class GuiApplication: def request_close(self): self._window_close_requested = True - def init_window(self, title: str, fps: int = DEFAULT_FPS): + def init_window(self, title: str, fps: int = _DEFAULT_FPS): atexit.register(self.close) # Automatically call close() on exit HARDWARE.set_display_power(True) @@ -164,15 +164,22 @@ class GuiApplication: rl.set_mouse_scale(1 / self._scale, 1 / self._scale) self._render_texture = rl.load_render_texture(self._width, self._height) rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) - rl.set_target_fps(fps) - self._target_fps = fps + self.set_target_fps(fps) self._set_styles() self._load_fonts() if not PC: self._mouse.start() + @property + def target_fps(self): + return self._target_fps + + def set_target_fps(self, fps: int): + self._target_fps = fps + rl.set_target_fps(fps) + def set_modal_overlay(self, overlay, callback: Callable | None = None): self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 119901da40..986d7158de 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -5,7 +5,7 @@ from typing import cast import pyray as rl from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, DEFAULT_FPS +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType from openpilot.system.ui.widgets import Widget @@ -50,10 +50,12 @@ class NavButton(Widget): super().__init__() self.text = text self.set_rect(rl.Rectangle(0, 0, 400, 100)) - self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) - self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) + self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) + self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) def set_position(self, x: float, y: float) -> None: + self._x_pos_filter.update_dt(1 / gui_app.target_fps) + self._y_pos_filter.update_dt(1 / gui_app.target_fps) x = self._x_pos_filter.update(x) y = self._y_pos_filter.update(y) changed = (self._rect.x != x or self._rect.y != y) From 5cbfc7705bbd46562f4425cd06ed4d337f0d9d7f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 21:41:12 -0700 Subject: [PATCH 054/341] Revert "raylib: 20 FPS onroad (#36208)" This reverts commit 8de8c3eb00383cb5b8fa18c41ddc4899dccc1e61. --- common/filter_simple.py | 11 +++-------- selfdrive/ui/layouts/main.py | 10 ---------- selfdrive/ui/onroad/model_renderer.py | 5 ++--- selfdrive/ui/ui_state.py | 4 ++-- system/ui/lib/application.py | 17 +++++------------ system/ui/widgets/network.py | 8 +++----- 6 files changed, 15 insertions(+), 40 deletions(-) diff --git a/common/filter_simple.py b/common/filter_simple.py index 8a7105063d..9ea6fe3070 100644 --- a/common/filter_simple.py +++ b/common/filter_simple.py @@ -1,21 +1,16 @@ class FirstOrderFilter: def __init__(self, x0, rc, dt, initialized=True): self.x = x0 - self._dt = dt + self.dt = dt self.update_alpha(rc) self.initialized = initialized - def update_dt(self, dt): - self._dt = dt - self.update_alpha(self._rc) - def update_alpha(self, rc): - self._rc = rc - self._alpha = self._dt / (self._rc + self._dt) + self.alpha = self.dt / (rc + self.dt) def update(self, x): if self.initialized: - self.x = (1. - self._alpha) * self.x + self._alpha * x + self.x = (1. - self.alpha) * self.x + self.alpha * x else: self.initialized = True self.x = x diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 0a8fadea90..777d2f4c3f 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -1,7 +1,6 @@ import pyray as rl from enum import IntEnum import cereal.messaging as messaging -from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH from openpilot.selfdrive.ui.layouts.home import HomeLayout from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType @@ -10,10 +9,6 @@ from openpilot.selfdrive.ui.ui_state import device, ui_state from openpilot.system.ui.widgets import Widget -ONROAD_FPS = 20 -OFFROAD_FPS = 60 - - class MainState(IntEnum): HOME = 0 SETTINGS = 1 @@ -30,8 +25,6 @@ class MainLayout(Widget): self._current_mode = MainState.HOME self._prev_onroad = False - gui_app.set_target_fps(OFFROAD_FPS) - # Initialize layouts self._layouts = {MainState.HOME: HomeLayout(), MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()} @@ -81,9 +74,6 @@ class MainLayout(Widget): self._current_mode = layout self._layouts[self._current_mode].show_event() - # No need to draw onroad faster than source (model at 20Hz) and prevents screen tearing - gui_app.set_target_fps(ONROAD_FPS if self._current_mode == MainState.ONROAD else OFFROAD_FPS) - def open_settings(self, panel_type: PanelType): self._layouts[MainState.SETTINGS].set_current_panel(panel_type) self._set_current_layout(MainState.SETTINGS) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 4c036873d0..d84e16cb54 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -7,7 +7,7 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.ui.lib.shader_polygon import draw_polygon from openpilot.system.ui.widgets import Widget @@ -48,7 +48,7 @@ class ModelRenderer(Widget): super().__init__() self._longitudinal_control = False self._experimental_mode = False - self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / gui_app.target_fps) + self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / DEFAULT_FPS) self._prev_allow_throttle = True self._lane_line_probs = np.zeros(4, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32) @@ -277,7 +277,6 @@ class ModelRenderer(Widget): return allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control - self._blend_filter.update_dt(1 / gui_app.target_fps) self._blend_filter.update(int(allow_throttle)) if self._experimental_mode: diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 39a65a9191..13532620cb 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -9,6 +9,7 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params, UnknownKeyName from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.lib.prime_state import PrimeState +from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app @@ -152,7 +153,7 @@ class Device: self._offroad_brightness: int = BACKLIGHT_OFFROAD self._last_brightness: int = 0 - self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / gui_app.target_fps) + self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / DEFAULT_FPS) self._brightness_thread: threading.Thread | None = None def reset_interactive_timeout(self, timeout: int = -1) -> None: @@ -189,7 +190,6 @@ class Device: clipped_brightness = float(np.clip(100 * clipped_brightness, 10, 100)) - self._brightness_filter.update_dt(1 / gui_app.target_fps) brightness = round(self._brightness_filter.update(clipped_brightness)) if not self._awake: brightness = 0 diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 481fd98045..911891f10b 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -14,7 +14,7 @@ from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE, PC from openpilot.common.realtime import Ratekeeper -_DEFAULT_FPS = int(os.getenv("FPS", "60")) +DEFAULT_FPS = int(os.getenv("FPS", "60")) FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions @@ -130,7 +130,7 @@ class GuiApplication: self._scaled_height = int(self._height * self._scale) self._render_texture: rl.RenderTexture | None = None self._textures: dict[str, rl.Texture] = {} - self._target_fps: int = _DEFAULT_FPS + self._target_fps: int = DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() self._window_close_requested = False self._trace_log_callback = None @@ -145,7 +145,7 @@ class GuiApplication: def request_close(self): self._window_close_requested = True - def init_window(self, title: str, fps: int = _DEFAULT_FPS): + def init_window(self, title: str, fps: int = DEFAULT_FPS): atexit.register(self.close) # Automatically call close() on exit HARDWARE.set_display_power(True) @@ -164,22 +164,15 @@ class GuiApplication: rl.set_mouse_scale(1 / self._scale, 1 / self._scale) self._render_texture = rl.load_render_texture(self._width, self._height) rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) + rl.set_target_fps(fps) - self.set_target_fps(fps) + self._target_fps = fps self._set_styles() self._load_fonts() if not PC: self._mouse.start() - @property - def target_fps(self): - return self._target_fps - - def set_target_fps(self, fps: int): - self._target_fps = fps - rl.set_target_fps(fps) - def set_modal_overlay(self, overlay, callback: Callable | None = None): self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 986d7158de..119901da40 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -5,7 +5,7 @@ from typing import cast import pyray as rl from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, DEFAULT_FPS from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType from openpilot.system.ui.widgets import Widget @@ -50,12 +50,10 @@ class NavButton(Widget): super().__init__() self.text = text self.set_rect(rl.Rectangle(0, 0, 400, 100)) - self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) - self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) + self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) + self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) def set_position(self, x: float, y: float) -> None: - self._x_pos_filter.update_dt(1 / gui_app.target_fps) - self._y_pos_filter.update_dt(1 / gui_app.target_fps) x = self._x_pos_filter.update(x) y = self._y_pos_filter.update(y) changed = (self._rect.x != x or self._rect.y != y) From 19fc66f88a690c8c78e4b79441bb9fc46fbec04e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 21:41:51 -0700 Subject: [PATCH 055/341] Fix tearing offroad --- system/ui/lib/application.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 911891f10b..132d78e5a2 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -11,10 +11,10 @@ from enum import StrEnum from typing import NamedTuple from importlib.resources import as_file, files from openpilot.common.swaglog import cloudlog -from openpilot.system.hardware import HARDWARE, PC +from openpilot.system.hardware import HARDWARE, PC, TICI from openpilot.common.realtime import Ratekeeper -DEFAULT_FPS = int(os.getenv("FPS", "60")) +DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions From ed185e90f680adc127111bbec29579619b5d753c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 21:43:15 -0700 Subject: [PATCH 056/341] Reapply "raylib: 20 FPS onroad (#36208)" This reverts commit 5cbfc7705bbd46562f4425cd06ed4d337f0d9d7f. --- common/filter_simple.py | 11 ++++++++--- selfdrive/ui/layouts/main.py | 10 ++++++++++ selfdrive/ui/onroad/model_renderer.py | 5 +++-- selfdrive/ui/ui_state.py | 4 ++-- system/ui/lib/application.py | 19 +++++++++++++------ system/ui/widgets/network.py | 8 +++++--- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/common/filter_simple.py b/common/filter_simple.py index 9ea6fe3070..8a7105063d 100644 --- a/common/filter_simple.py +++ b/common/filter_simple.py @@ -1,16 +1,21 @@ class FirstOrderFilter: def __init__(self, x0, rc, dt, initialized=True): self.x = x0 - self.dt = dt + self._dt = dt self.update_alpha(rc) self.initialized = initialized + def update_dt(self, dt): + self._dt = dt + self.update_alpha(self._rc) + def update_alpha(self, rc): - self.alpha = self.dt / (rc + self.dt) + self._rc = rc + self._alpha = self._dt / (self._rc + self._dt) def update(self, x): if self.initialized: - self.x = (1. - self.alpha) * self.x + self.alpha * x + self.x = (1. - self._alpha) * self.x + self._alpha * x else: self.initialized = True self.x = x diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 777d2f4c3f..0a8fadea90 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -1,6 +1,7 @@ import pyray as rl from enum import IntEnum import cereal.messaging as messaging +from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH from openpilot.selfdrive.ui.layouts.home import HomeLayout from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType @@ -9,6 +10,10 @@ from openpilot.selfdrive.ui.ui_state import device, ui_state from openpilot.system.ui.widgets import Widget +ONROAD_FPS = 20 +OFFROAD_FPS = 60 + + class MainState(IntEnum): HOME = 0 SETTINGS = 1 @@ -25,6 +30,8 @@ class MainLayout(Widget): self._current_mode = MainState.HOME self._prev_onroad = False + gui_app.set_target_fps(OFFROAD_FPS) + # Initialize layouts self._layouts = {MainState.HOME: HomeLayout(), MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()} @@ -74,6 +81,9 @@ class MainLayout(Widget): self._current_mode = layout self._layouts[self._current_mode].show_event() + # No need to draw onroad faster than source (model at 20Hz) and prevents screen tearing + gui_app.set_target_fps(ONROAD_FPS if self._current_mode == MainState.ONROAD else OFFROAD_FPS) + def open_settings(self, panel_type: PanelType): self._layouts[MainState.SETTINGS].set_current_panel(panel_type) self._set_current_layout(MainState.SETTINGS) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index d84e16cb54..4c036873d0 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -7,7 +7,7 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import DEFAULT_FPS +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.shader_polygon import draw_polygon from openpilot.system.ui.widgets import Widget @@ -48,7 +48,7 @@ class ModelRenderer(Widget): super().__init__() self._longitudinal_control = False self._experimental_mode = False - self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / DEFAULT_FPS) + self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / gui_app.target_fps) self._prev_allow_throttle = True self._lane_line_probs = np.zeros(4, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32) @@ -277,6 +277,7 @@ class ModelRenderer(Widget): return allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control + self._blend_filter.update_dt(1 / gui_app.target_fps) self._blend_filter.update(int(allow_throttle)) if self._experimental_mode: diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 13532620cb..39a65a9191 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -9,7 +9,6 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params, UnknownKeyName from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.lib.prime_state import PrimeState -from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app @@ -153,7 +152,7 @@ class Device: self._offroad_brightness: int = BACKLIGHT_OFFROAD self._last_brightness: int = 0 - self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / DEFAULT_FPS) + self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / gui_app.target_fps) self._brightness_thread: threading.Thread | None = None def reset_interactive_timeout(self, timeout: int = -1) -> None: @@ -190,6 +189,7 @@ class Device: clipped_brightness = float(np.clip(100 * clipped_brightness, 10, 100)) + self._brightness_filter.update_dt(1 / gui_app.target_fps) brightness = round(self._brightness_filter.update(clipped_brightness)) if not self._awake: brightness = 0 diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 132d78e5a2..481fd98045 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -11,10 +11,10 @@ from enum import StrEnum from typing import NamedTuple from importlib.resources import as_file, files from openpilot.common.swaglog import cloudlog -from openpilot.system.hardware import HARDWARE, PC, TICI +from openpilot.system.hardware import HARDWARE, PC from openpilot.common.realtime import Ratekeeper -DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) +_DEFAULT_FPS = int(os.getenv("FPS", "60")) FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions @@ -130,7 +130,7 @@ class GuiApplication: self._scaled_height = int(self._height * self._scale) self._render_texture: rl.RenderTexture | None = None self._textures: dict[str, rl.Texture] = {} - self._target_fps: int = DEFAULT_FPS + self._target_fps: int = _DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() self._window_close_requested = False self._trace_log_callback = None @@ -145,7 +145,7 @@ class GuiApplication: def request_close(self): self._window_close_requested = True - def init_window(self, title: str, fps: int = DEFAULT_FPS): + def init_window(self, title: str, fps: int = _DEFAULT_FPS): atexit.register(self.close) # Automatically call close() on exit HARDWARE.set_display_power(True) @@ -164,15 +164,22 @@ class GuiApplication: rl.set_mouse_scale(1 / self._scale, 1 / self._scale) self._render_texture = rl.load_render_texture(self._width, self._height) rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) - rl.set_target_fps(fps) - self._target_fps = fps + self.set_target_fps(fps) self._set_styles() self._load_fonts() if not PC: self._mouse.start() + @property + def target_fps(self): + return self._target_fps + + def set_target_fps(self, fps: int): + self._target_fps = fps + rl.set_target_fps(fps) + def set_modal_overlay(self, overlay, callback: Callable | None = None): self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 119901da40..986d7158de 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -5,7 +5,7 @@ from typing import cast import pyray as rl from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, DEFAULT_FPS +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType from openpilot.system.ui.widgets import Widget @@ -50,10 +50,12 @@ class NavButton(Widget): super().__init__() self.text = text self.set_rect(rl.Rectangle(0, 0, 400, 100)) - self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) - self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) + self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) + self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) def set_position(self, x: float, y: float) -> None: + self._x_pos_filter.update_dt(1 / gui_app.target_fps) + self._y_pos_filter.update_dt(1 / gui_app.target_fps) x = self._x_pos_filter.update(x) y = self._y_pos_filter.update(y) changed = (self._rect.x != x or self._rect.y != y) From 2feddf32b2c5510168d8b453f49999e8615a17bc Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 22:40:38 -0700 Subject: [PATCH 057/341] raylib: fix lost onroad tap events (#36211) * debug * see it's good to have abstraction * clean up * fine * wtf do you mean mypy? how can you not coerce this? --- selfdrive/ui/layouts/main.py | 2 +- selfdrive/ui/onroad/augmented_road_view.py | 16 ++++----------- selfdrive/ui/onroad/exp_button.py | 23 +++++++++------------- selfdrive/ui/onroad/hud_renderer.py | 6 +++--- system/ui/widgets/__init__.py | 5 +++++ 5 files changed, 22 insertions(+), 30 deletions(-) diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 0a8fadea90..ffb45f821d 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -50,7 +50,7 @@ class MainLayout(Widget): on_flag=self._on_bookmark_clicked) self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE)) self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state) - self._layouts[MainState.ONROAD].set_callbacks(on_click=self._on_onroad_clicked) + self._layouts[MainState.ONROAD].set_click_callback(self._on_onroad_clicked) device.add_interactive_timeout_callback(self._set_mode_for_state) def _update_layout_rects(self): diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index f012e8b06b..0a4c45163b 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -1,6 +1,5 @@ import numpy as np import pyray as rl -from collections.abc import Callable from cereal import log from msgq.visionipc import VisionStreamType from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus, UI_BORDER_SIZE @@ -49,12 +48,6 @@ class AugmentedRoadView(CameraView): self.alert_renderer = AlertRenderer() self.driver_state_renderer = DriverStateRenderer() - # Callbacks - self._click_callback: Callable | None = None - - def set_callbacks(self, on_click: Callable | None = None): - self._click_callback = on_click - def _render(self, rect): # Only render when system is started to avoid invalid data access if not ui_state.started: @@ -100,13 +93,12 @@ class AugmentedRoadView(CameraView): # End clipping region rl.end_scissor_mode() - # Handle click events if no HUD interaction occurred - if not self._hud_renderer.handle_mouse_event(): - if self._click_callback is not None and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - if rl.check_collision_point_rec(rl.get_mouse_position(), self._content_rect): - self._click_callback() + def _handle_mouse_press(self, _): + if not self._hud_renderer.user_interacting() and self._click_callback is not None: + self._click_callback() def _handle_mouse_release(self, _): + # We only call click callback on press if not interacting with HUD pass def _draw_border(self, rect: rl.Rectangle): diff --git a/selfdrive/ui/onroad/exp_button.py b/selfdrive/ui/onroad/exp_button.py index 27f5763077..175233c5ba 100644 --- a/selfdrive/ui/onroad/exp_button.py +++ b/selfdrive/ui/onroad/exp_button.py @@ -32,26 +32,21 @@ class ExpButton(Widget): self._experimental_mode = selfdrive_state.experimentalMode self._engageable = selfdrive_state.engageable or selfdrive_state.enabled - def handle_mouse_event(self) -> bool: - if rl.check_collision_point_rec(rl.get_mouse_position(), self._rect): - if (rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and - self._is_toggle_allowed()): - new_mode = not self._experimental_mode - self._params.put_bool("ExperimentalMode", new_mode) + def _handle_mouse_release(self, _): + super()._handle_mouse_release(_) + if self._is_toggle_allowed(): + new_mode = not self._experimental_mode + self._params.put_bool("ExperimentalMode", new_mode) - # Hold new state temporarily - self._held_mode = new_mode - self._hold_end_time = time.monotonic() + self._hold_duration - return True - return False + # Hold new state temporarily + self._held_mode = new_mode + self._hold_end_time = time.monotonic() + self._hold_duration def _render(self, rect: rl.Rectangle) -> None: center_x = int(self._rect.x + self._rect.width // 2) center_y = int(self._rect.y + self._rect.height // 2) - mouse_over = rl.check_collision_point_rec(rl.get_mouse_position(), self._rect) - mouse_down = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and self.is_pressed - self._white_color.a = 180 if (mouse_down and mouse_over) or not self._engageable else 255 + self._white_color.a = 180 if self.is_pressed or not self._engageable else 255 texture = self._txt_exp if self._held_or_actual_mode() else self._txt_wheel rl.draw_circle(center_x, center_y, self._rect.width / 2, self._black_bg) diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index 536d993389..c813d852bc 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -69,7 +69,7 @@ class HudRenderer(Widget): self._font_bold: rl.Font = gui_app.font(FontWeight.BOLD) self._font_medium: rl.Font = gui_app.font(FontWeight.MEDIUM) - self._exp_button = ExpButton(UI_CONFIG.button_size, UI_CONFIG.wheel_icon_size) + self._exp_button: ExpButton = ExpButton(UI_CONFIG.button_size, UI_CONFIG.wheel_icon_size) def _update_state(self) -> None: """Update HUD state based on car state and controls state.""" @@ -120,8 +120,8 @@ class HudRenderer(Widget): button_y = rect.y + UI_CONFIG.border_size self._exp_button.render(rl.Rectangle(button_x, button_y, UI_CONFIG.button_size, UI_CONFIG.button_size)) - def handle_mouse_event(self) -> bool: - return bool(self._exp_button.handle_mouse_event()) + def user_interacting(self) -> bool: + return self._exp_button.is_pressed def _draw_set_speed(self, rect: rl.Rectangle) -> None: """Draw the MAX speed indicator box.""" diff --git a/system/ui/widgets/__init__.py b/system/ui/widgets/__init__.py index 6372b1813d..0157dd3ef4 100644 --- a/system/ui/widgets/__init__.py +++ b/system/ui/widgets/__init__.py @@ -96,6 +96,7 @@ class Widget(abc.ABC): # Allows touch to leave the rect and come back in focus if mouse did not release if mouse_event.left_pressed and self._touch_valid(): if rl.check_collision_point_rec(mouse_event.pos, self._rect): + self._handle_mouse_press(mouse_event.pos) self.__is_pressed[mouse_event.slot] = True self.__tracking_is_pressed[mouse_event.slot] = True @@ -131,6 +132,10 @@ class Widget(abc.ABC): def _update_layout_rects(self) -> None: """Optionally update any layout rects on Widget rect change.""" + def _handle_mouse_press(self, mouse_pos: MousePos) -> bool: + """Optionally handle mouse press events.""" + return False + def _handle_mouse_release(self, mouse_pos: MousePos) -> bool: """Optionally handle mouse release events.""" if self._click_callback: From 35e2fc7dd91d7db2328185f045810236b0e37b1d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 26 Sep 2025 23:49:17 -0700 Subject: [PATCH 058/341] raylib: use touch thread in all places (#36212) * fix not opening alerts * whops * rm mouse pressed from offroad alerts * ah its a base class * one last place * fix * rm lines --- selfdrive/ui/layouts/home.py | 21 ++++------------- selfdrive/ui/onroad/driver_camera_dialog.py | 7 +++--- selfdrive/ui/widgets/offroad_alerts.py | 26 ++++++++++----------- 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index f102481c3a..9ee533d2a4 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -8,7 +8,7 @@ from openpilot.selfdrive.ui.widgets.exp_mode_button import ExperimentalModeButto from openpilot.selfdrive.ui.widgets.prime import PrimeWidget from openpilot.selfdrive.ui.widgets.setup import SetupWidget from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR +from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, DEFAULT_TEXT_COLOR from openpilot.system.ui.widgets import Widget HEADER_HEIGHT = 80 @@ -72,7 +72,6 @@ class HomeLayout(Widget): self._refresh() self.last_refresh = current_time - self._handle_input() self._render_header() # Render content based on current state @@ -110,25 +109,13 @@ class HomeLayout(Widget): self.alert_notif_rect.x = notif_x self.alert_notif_rect.y = self.header_rect.y + (self.header_rect.height - 60) // 2 - def _handle_input(self): - if not rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - return - - mouse_pos = rl.get_mouse_position() + def _handle_mouse_release(self, mouse_pos: MousePos): + super()._handle_mouse_release(mouse_pos) if self.update_available and rl.check_collision_point_rec(mouse_pos, self.update_notif_rect): self._set_state(HomeLayoutState.UPDATE) - return - - if self.alert_count > 0 and rl.check_collision_point_rec(mouse_pos, self.alert_notif_rect): + elif self.alert_count > 0 and rl.check_collision_point_rec(mouse_pos, self.alert_notif_rect): self._set_state(HomeLayoutState.ALERTS) - return - - # Content area input handling - if self.current_state == HomeLayoutState.UPDATE: - self.update_alert.handle_input(mouse_pos, True) - elif self.current_state == HomeLayoutState.ALERTS: - self.offroad_alert.handle_input(mouse_pos, True) def _render_header(self): font = gui_app.font(FontWeight.MEDIUM) diff --git a/selfdrive/ui/onroad/driver_camera_dialog.py b/selfdrive/ui/onroad/driver_camera_dialog.py index c8b4e62030..6c5508ce7d 100644 --- a/selfdrive/ui/onroad/driver_camera_dialog.py +++ b/selfdrive/ui/onroad/driver_camera_dialog.py @@ -13,12 +13,13 @@ class DriverCameraDialog(CameraView): super().__init__("camerad", VisionStreamType.VISION_STREAM_DRIVER) self.driver_state_renderer = DriverStateRenderer() + def _handle_mouse_release(self, _): + super()._handle_mouse_release(_) + gui_app.set_modal_overlay(None) + def _render(self, rect): super()._render(rect) - if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - return 1 - if not self.frame: gui_label( rect, diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index f54bd9116c..39fef3182c 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -1,11 +1,14 @@ +import os import json import pyray as rl from abc import ABC, abstractmethod from collections.abc import Callable from dataclasses import dataclass +from openpilot.common.swaglog import cloudlog +from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text @@ -69,26 +72,23 @@ class AbstractAlert(Widget, ABC): def get_content_height(self) -> float: pass - def handle_input(self, mouse_pos: rl.Vector2, mouse_clicked: bool) -> bool: - if not mouse_clicked or not self.scroll_panel.is_touch_valid(): - return False + def _handle_mouse_release(self, mouse_pos: MousePos): + super()._handle_mouse_release(mouse_pos) + + if not self.scroll_panel.is_touch_valid(): + return if rl.check_collision_point_rec(mouse_pos, self.dismiss_btn_rect): if self.dismiss_callback: self.dismiss_callback() - return True - if self.snooze_visible and rl.check_collision_point_rec(mouse_pos, self.snooze_btn_rect): + elif self.snooze_visible and rl.check_collision_point_rec(mouse_pos, self.snooze_btn_rect): self.params.put_bool("SnoozeUpdate", True) if self.dismiss_callback: self.dismiss_callback() - return True - if self.has_reboot_btn and rl.check_collision_point_rec(mouse_pos, self.reboot_btn_rect): + elif self.has_reboot_btn and rl.check_collision_point_rec(mouse_pos, self.reboot_btn_rect): HARDWARE.reboot() - return True - - return False def _render(self, rect: rl.Rectangle): rl.draw_rectangle_rounded(rect, AlertConstants.BORDER_RADIUS / rect.width, 10, AlertColors.BACKGROUND) @@ -233,14 +233,14 @@ class OffroadAlert(AbstractAlert): def _build_alerts(self): self.sorted_alerts = [] try: - with open("../selfdrived/alerts_offroad.json", "rb") as f: + with open(os.path.join(BASEDIR, "selfdrive/selfdrived/alerts_offroad.json"), "rb") as f: alerts_config = json.load(f) for key, config in sorted(alerts_config.items(), key=lambda x: x[1].get("severity", 0), reverse=True): severity = config.get("severity", 0) alert_data = AlertData(key=key, text="", severity=severity) self.sorted_alerts.append(alert_data) except (FileNotFoundError, json.JSONDecodeError): - pass + cloudlog.exception("Failed to load offroad alerts") def _render_content(self, content_rect: rl.Rectangle): y_offset = 20 From ef93981bfa3116442729ffe1b98a7c6f817c68ad Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 27 Sep 2025 02:37:35 -0700 Subject: [PATCH 059/341] raylib: ui diff test (#36213) * add raylib ui test * match qt * exe * vibing is epic * this is epic * format * add more settings * fix to actually use raylib * add kb * global * pair * rm cmts * show event * this is so stupid clean up * clean up * rename dir * clean up * no more vibe * rm * ugh it's always slightly different for no reason * nvm region is actually broken * 1l --- .github/workflows/raylib_ui_preview.yaml | 174 ++++++++++++++++++ .github/workflows/selfdrive_tests.yaml | 26 +++ selfdrive/ui/layouts/home.py | 4 + .../ui/tests/test_ui/raylib_screenshots.py | 146 +++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 .github/workflows/raylib_ui_preview.yaml create mode 100755 selfdrive/ui/tests/test_ui/raylib_screenshots.py diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml new file mode 100644 index 0000000000..11e745a96a --- /dev/null +++ b/.github/workflows/raylib_ui_preview.yaml @@ -0,0 +1,174 @@ +name: "raylib ui preview" +on: + push: + branches: + - master + pull_request_target: + types: [assigned, opened, synchronize, reopened, edited] + branches: + - 'master' + paths: + - 'selfdrive/ui/**' + - 'system/ui/**' + workflow_dispatch: + +env: + UI_JOB_NAME: "Create raylib UI Report" + REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} + SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }} + BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui" + +jobs: + preview: + if: github.repository == 'commaai/openpilot' + name: preview + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + pull-requests: write + actions: read + steps: + - name: Waiting for ui generation to start + run: sleep 30 + + - name: Waiting for ui generation to end + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ env.SHA }} + check-name: ${{ env.UI_JOB_NAME }} + repo-token: ${{ secrets.GITHUB_TOKEN }} + allowed-conclusions: success + wait-interval: 20 + + - name: Getting workflow run ID + id: get_run_id + run: | + echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?[0-9]+)") | .number')" >> $GITHUB_OUTPUT + + - name: Getting proposed ui + id: download-artifact + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + run_id: ${{ steps.get_run_id.outputs.run_id }} + search_artifacts: true + name: raylib-report-${{ env.REPORT_NAME }} + path: ${{ github.workspace }}/pr_ui + + - name: Getting master ui + uses: actions/checkout@v4 + with: + repository: commaai/ci-artifacts + ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} + path: ${{ github.workspace }}/master_ui_raylib + ref: openpilot_master_ui_raylib + + - name: Saving new master ui + if: github.ref == 'refs/heads/master' && github.event_name == 'push' + working-directory: ${{ github.workspace }}/master_ui_raylib + run: | + git checkout --orphan=new_master_ui_raylib + git rm -rf * + git branch -D openpilot_master_ui_raylib + git branch -m openpilot_master_ui_raylib + git config user.name "GitHub Actions Bot" + git config user.email "<>" + mv ${{ github.workspace }}/pr_ui/*.png . + git add . + git commit -m "raylib screenshots for commit ${{ env.SHA }}" + git push origin openpilot_master_ui_raylib --force + + - name: Finding diff + if: github.event_name == 'pull_request_target' + id: find_diff + run: >- + sudo apt-get update && sudo apt-get install -y imagemagick + + scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') + A=($scenes) + + DIFF="" + TABLE="
All Screenshots" + TABLE="${TABLE}" + + for ((i=0; i<${#A[*]}; i=i+1)); + do + # Check if the master file exists + if [ ! -f "${{ github.workspace }}/master_ui_raylib/${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{cyan}\\text{NEW}}\$\$" + DIFF="${DIFF}
" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
" + DIFF="${DIFF}
" + elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${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_raylib/${A[$i]}.png composite_diff.png + convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif + + mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png + + DIFF="${DIFF}
" + DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
master proposed
diff composite diff
" + DIFF="${DIFF}
" + else + rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png + fi + + INDEX=$(($i % 2)) + if [[ $INDEX -eq 0 ]]; then + TABLE="${TABLE}" + fi + TABLE="${TABLE} " + if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then + TABLE="${TABLE}" + fi + done + + TABLE="${TABLE}" + + echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" + + - name: Saving proposed ui + if: github.event_name == 'pull_request_target' + working-directory: ${{ github.workspace }}/master_ui_raylib + run: | + git config user.name "GitHub Actions Bot" + git config user.email "<>" + git checkout --orphan=${{ env.BRANCH_NAME }} + git rm -rf * + mv ${{ github.workspace }}/pr_ui/* . + git add . + git commit -m "raylib screenshots for PR #${{ github.event.number }}" + git push origin ${{ env.BRANCH_NAME }} --force + + - name: Comment Screenshots on PR + if: github.event_name == 'pull_request_target' + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + + ## raylib UI Preview + ${{ steps.find_diff.outputs.DIFF }} + comment_tag: run_id_screenshots_raylib + pr_number: ${{ github.event.number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index beb426c669..57b1158be2 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -277,3 +277,29 @@ jobs: with: name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} path: selfdrive/ui/tests/test_ui/report_1/screenshots + + create_raylib_ui_report: + name: Create raylib UI Report + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: ./.github/workflows/setup-with-retry + - name: Build openpilot + run: ${{ env.RUN }} "scons -j$(nproc)" + - name: Create raylib UI Report + run: > + ${{ env.RUN }} "PYTHONWARNINGS=ignore && + source selfdrive/test/setup_xvfb.sh && + python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py" + - name: Upload Raylib UI Report + uses: actions/upload-artifact@v4 + with: + name: raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} + path: selfdrive/ui/tests/test_ui/raylib_report/screenshots diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 9ee533d2a4..320e7477f1 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -56,6 +56,10 @@ class HomeLayout(Widget): self._exp_mode_button = ExperimentalModeButton() self._setup_callbacks() + def show_event(self): + self.last_refresh = time.monotonic() + self._refresh() + def _setup_callbacks(self): self.update_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME)) self.offroad_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME)) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py new file mode 100755 index 0000000000..7fb22e4484 --- /dev/null +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +import os +import sys +import shutil +import time +import pathlib +from collections import namedtuple + +import pyautogui +import pywinctl + +from cereal import log +from cereal import messaging +from cereal.messaging import PubMaster +from openpilot.common.params import Params +from openpilot.common.prefix import OpenpilotPrefix +from openpilot.selfdrive.test.helpers import with_processes +from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert + +TEST_DIR = pathlib.Path(__file__).parent +TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" +SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" +UI_DELAY = 0.1 + +# Offroad alerts to test +OFFROAD_ALERTS = ['Offroad_IsTakingSnapshot'] + + +def setup_homescreen(click, pm: PubMaster): + pass + + +def setup_settings_device(click, pm: PubMaster): + click(100, 100) + + +def setup_settings_network(click, pm: PubMaster): + setup_settings_device(click, pm) + click(278, 450) + + +def setup_settings_toggles(click, pm: PubMaster): + setup_settings_device(click, pm) + click(278, 600) + + +def setup_settings_software(click, pm: PubMaster): + setup_settings_device(click, pm) + click(278, 720) + + +def setup_settings_firehose(click, pm: PubMaster): + setup_settings_device(click, pm) + click(278, 845) + + +def setup_settings_developer(click, pm: PubMaster): + setup_settings_device(click, pm) + click(278, 950) + + +def setup_keyboard(click, pm: PubMaster): + setup_settings_developer(click, pm) + click(1930, 270) + + +def setup_pair_device(click, pm: PubMaster): + click(1950, 800) + + +def setup_offroad_alert(click, pm: PubMaster): + set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99') + for alert in OFFROAD_ALERTS: + set_offroad_alert(alert, True) + + setup_settings_device(click, pm) + click(240, 216) + + +CASES = { + "homescreen": setup_homescreen, + "settings_device": setup_settings_device, + "settings_network": setup_settings_network, + "settings_toggles": setup_settings_toggles, + "settings_software": setup_settings_software, + "settings_firehose": setup_settings_firehose, + "settings_developer": setup_settings_developer, + "keyboard": setup_keyboard, + "pair_device": setup_pair_device, + "offroad_alert": setup_offroad_alert, +} + + +class TestUI: + def __init__(self): + os.environ["SCALE"] = os.getenv("SCALE", "1") + sys.modules["mouseinfo"] = False + + def setup(self): + # Seed minimal offroad state + self.pm = PubMaster(["deviceState"]) + ds = messaging.new_message('deviceState') + ds.deviceState.networkType = log.DeviceState.NetworkType.wifi + for _ in range(5): + self.pm.send('deviceState', ds) + ds.clear_write_flag() + time.sleep(0.05) + time.sleep(0.5) + try: + self.ui = pywinctl.getWindowsWithTitle("UI")[0] + except Exception as e: + print(f"failed to find ui window, assuming that it's in the top left (for Xvfb) {e}") + self.ui = namedtuple("bb", ["left", "top", "width", "height"])(0, 0, 2160, 1080) + + def screenshot(self, name: str): + full_screenshot = pyautogui.screenshot() + cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) + cropped.save(SCREENSHOTS_DIR / f"{name}.png") + + def click(self, x: int, y: int, *args, **kwargs): + pyautogui.mouseDown(self.ui.left + x, self.ui.top + y, *args, **kwargs) + time.sleep(0.01) + pyautogui.mouseUp(self.ui.left + x, self.ui.top + y, *args, **kwargs) + + @with_processes(["raylib_ui"]) + def test_ui(self, name, setup_case): + self.setup() + setup_case(self.click, self.pm) + self.screenshot(name) + + +def create_screenshots(): + if TEST_OUTPUT_DIR.exists(): + shutil.rmtree(TEST_OUTPUT_DIR) + SCREENSHOTS_DIR.mkdir(parents=True) + + t = TestUI() + with OpenpilotPrefix(): + params = Params() + params.put("DongleId", "123456789012345") + for name, setup in CASES.items(): + t.test_ui(name, setup) + + +if __name__ == "__main__": + create_screenshots() From bc30b01eb7d0787f100a52313bcd59e3c3c4a855 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 27 Sep 2025 02:53:21 -0700 Subject: [PATCH 060/341] Fix raylib ui report (#36215) hmm --- .github/workflows/ui_preview.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml index 9ec7a59223..b5c3b21dc0 100644 --- a/.github/workflows/ui_preview.yaml +++ b/.github/workflows/ui_preview.yaml @@ -53,7 +53,7 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} run_id: ${{ steps.get_run_id.outputs.run_id }} search_artifacts: true - name: report-1-${{ env.REPORT_NAME }} + name: raylib-report-1-${{ env.REPORT_NAME }} path: ${{ github.workspace }}/pr_ui - name: Getting master ui From e6bd88371e30257f359c2a6126f5d39e3bbf8ba1 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 27 Sep 2025 02:55:40 -0700 Subject: [PATCH 061/341] fix! --- .github/workflows/raylib_ui_preview.yaml | 2 +- .github/workflows/ui_preview.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 11e745a96a..ff9655d155 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -53,7 +53,7 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} run_id: ${{ steps.get_run_id.outputs.run_id }} search_artifacts: true - name: raylib-report-${{ env.REPORT_NAME }} + name: raylib-report-1-${{ env.REPORT_NAME }} path: ${{ github.workspace }}/pr_ui - name: Getting master ui diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml index b5c3b21dc0..9ec7a59223 100644 --- a/.github/workflows/ui_preview.yaml +++ b/.github/workflows/ui_preview.yaml @@ -53,7 +53,7 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} run_id: ${{ steps.get_run_id.outputs.run_id }} search_artifacts: true - name: raylib-report-1-${{ env.REPORT_NAME }} + name: report-1-${{ env.REPORT_NAME }} path: ${{ github.workspace }}/pr_ui - name: Getting master ui From 56c77fd5fac2c9705630bb23d820ac086d67e6ac Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 27 Sep 2025 03:32:19 -0700 Subject: [PATCH 062/341] re-run From e9434befaa9a671b13fa191b173509e101e9b0fd Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 27 Sep 2025 03:37:23 -0700 Subject: [PATCH 063/341] Refactor offroad alerts loading to use OFFROAD_ALERTS (#36214) * Refactor offroad alerts loading to use OFFROAD_ALERTS * clean up --- selfdrive/ui/widgets/offroad_alerts.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 39fef3182c..da0ea287cf 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -1,11 +1,7 @@ -import os -import json import pyray as rl from abc import ABC, abstractmethod from collections.abc import Callable from dataclasses import dataclass -from openpilot.common.swaglog import cloudlog -from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos @@ -13,6 +9,7 @@ from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget +from openpilot.selfdrive.selfdrived.alertmanager import OFFROAD_ALERTS class AlertColors: @@ -232,15 +229,10 @@ class OffroadAlert(AbstractAlert): def _build_alerts(self): self.sorted_alerts = [] - try: - with open(os.path.join(BASEDIR, "selfdrive/selfdrived/alerts_offroad.json"), "rb") as f: - alerts_config = json.load(f) - for key, config in sorted(alerts_config.items(), key=lambda x: x[1].get("severity", 0), reverse=True): - severity = config.get("severity", 0) - alert_data = AlertData(key=key, text="", severity=severity) - self.sorted_alerts.append(alert_data) - except (FileNotFoundError, json.JSONDecodeError): - cloudlog.exception("Failed to load offroad alerts") + for key, config in sorted(OFFROAD_ALERTS.items(), key=lambda x: x[1].get("severity", 0), reverse=True): + severity = config.get("severity", 0) + alert_data = AlertData(key=key, text="", severity=severity) + self.sorted_alerts.append(alert_data) def _render_content(self, content_rect: rl.Rectangle): y_offset = 20 From 7ccab2bdb96414e1332d5f0b2403366d18a14f44 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Sun, 28 Sep 2025 13:50:13 -0700 Subject: [PATCH 064/341] [bot] Update Python packages (#36220) * Update Python packages * revert tg, model diff looks a bit fishy --------- Co-authored-by: Vehicle Researcher Co-authored-by: Adeeb Shihadeh --- opendbc_repo | 2 +- panda | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 2eec1af104..aa3d32a63b 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 2eec1af104972b7784644bf38c4c5afb52fc070a +Subproject commit aa3d32a63b7d05e69777fa90147192220abde8a3 diff --git a/panda b/panda index 1289337ceb..48c776a3df 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 1289337ceb6205ad985a5469baa950b319329327 +Subproject commit 48c776a3df87f68da00e099510f2904c2217b303 From 070a13096b4d6134cc3f029cbdc08f08fb616417 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 29 Sep 2025 13:03:03 -0700 Subject: [PATCH 065/341] raylib: add todo for niceness (#36210) * not nice * hmm * debug * todo * revert * yep --- selfdrive/ui/ui.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index 0230e16a4f..5fb6fd5737 100755 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -7,6 +7,9 @@ from openpilot.selfdrive.ui.ui_state import ui_state def main(): + # TODO: https://github.com/commaai/agnos-builder/pull/490 + # os.nice(-20) + gui_app.init_window("UI") main_layout = MainLayout() main_layout.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) From b5ec0e9744baaf5bae7989dd71b60006e49205ee Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 02:39:19 -0700 Subject: [PATCH 066/341] raylib: fix regulatory --- system/ui/lib/application.py | 2 +- system/ui/widgets/html_render.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 481fd98045..ed2a20dd11 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -334,7 +334,7 @@ class GuiApplication: for layout in KEYBOARD_LAYOUTS.values(): all_chars.update(key for row in layout for key in row) all_chars = "".join(all_chars) - all_chars += "–✓×°" + all_chars += "–✓×°§" codepoint_count = rl.ffi.new("int *", 1) codepoints = rl.load_codepoints(all_chars, codepoint_count) diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index 247b7a5492..d68ea58990 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -35,6 +35,7 @@ class HtmlElement: class HtmlRenderer(Widget): def __init__(self, file_path: str): + super().__init__() self.elements: list[HtmlElement] = [] self._normal_font = gui_app.font(FontWeight.NORMAL) self._bold_font = gui_app.font(FontWeight.BOLD) @@ -122,7 +123,7 @@ class HtmlRenderer(Widget): rl.end_scissor_mode() button_width = (rect.width - 3 * 50) // 3 - button_x = content_rect.x + (content_rect.width - button_width) / 2 + button_x = content_rect.x + content_rect.width - button_width button_y = content_rect.y + content_rect.height - button_height button_rect = rl.Rectangle(button_x, button_y, button_width, button_height) if gui_button(button_rect, "OK", button_style=ButtonStyle.PRIMARY) == 1: From aaf2aac050e1c0e7f33e018f415b07d9e6ce0c54 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 03:11:42 -0700 Subject: [PATCH 067/341] raylib: training guide (#36224) * fix regulatory * debug slow loading * easy gather step coords * gotcha * and fix * dm option * fix final * fixes * progress bar! * "vibe coding is great" * wtf gpt5 * jfc * hand crafted >> vibe * it's slow so only load images if we're doing any kind of training * tf * format * clean up * clean up * no float * cmt * more clean up * clean up * eww * rm * no debug * match y * clean that up * here too * windows --- selfdrive/ui/layouts/main.py | 6 + selfdrive/ui/layouts/onboarding.py | 197 ++++++++++++++++++++++++ selfdrive/ui/layouts/settings/device.py | 14 +- 3 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 selfdrive/ui/layouts/onboarding.py diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index ffb45f821d..97f9d6b222 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -8,6 +8,7 @@ from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, Pan from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView from openpilot.selfdrive.ui.ui_state import device, ui_state from openpilot.system.ui.widgets import Widget +from openpilot.selfdrive.ui.layouts.onboarding import OnboardingWindow ONROAD_FPS = 20 @@ -41,6 +42,11 @@ class MainLayout(Widget): # Set callbacks self._setup_callbacks() + # Start onboarding if terms or training not completed + self._onboarding_window = OnboardingWindow() + if not self._onboarding_window.completed: + gui_app.set_modal_overlay(self._onboarding_window) + def _render(self, _): self._handle_onroad_transition() self._render_main_content() diff --git a/selfdrive/ui/layouts/onboarding.py b/selfdrive/ui/layouts/onboarding.py new file mode 100644 index 0000000000..ea6fedebf7 --- /dev/null +++ b/selfdrive/ui/layouts/onboarding.py @@ -0,0 +1,197 @@ +import os +import re +from enum import IntEnum + +import pyray as rl +from openpilot.common.basedir import BASEDIR +from openpilot.system.ui.lib.application import FontWeight, gui_app +from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.widgets.button import Button, ButtonStyle +from openpilot.system.ui.widgets.label import Label, TextAlignment +from openpilot.selfdrive.ui.ui_state import ui_state + +DEBUG = False + +STEP_RECTS = [rl.Rectangle(104, 800, 633, 175), rl.Rectangle(1835, 0, 2159, 1080), rl.Rectangle(1835, 0, 2156, 1080), + rl.Rectangle(1526, 473, 427, 472), rl.Rectangle(1643, 441, 217, 223), rl.Rectangle(1835, 0, 2155, 1080), + rl.Rectangle(1786, 591, 267, 236), rl.Rectangle(1353, 0, 804, 1080), rl.Rectangle(1458, 485, 633, 211), + rl.Rectangle(95, 794, 1158, 187), rl.Rectangle(1560, 170, 392, 397), rl.Rectangle(1835, 0, 2159, 1080), + rl.Rectangle(1351, 0, 807, 1080), rl.Rectangle(1835, 0, 2158, 1080), rl.Rectangle(1531, 82, 441, 920), + rl.Rectangle(1336, 438, 490, 393), rl.Rectangle(1835, 0, 2159, 1080), rl.Rectangle(1835, 0, 2159, 1080), + rl.Rectangle(87, 795, 1187, 186)] + +DM_RECORD_STEP = 9 +DM_RECORD_YES_RECT = rl.Rectangle(695, 794, 558, 187) + +RESTART_TRAINING_RECT = rl.Rectangle(87, 795, 472, 186) + + +class OnboardingState(IntEnum): + TERMS = 0 + ONBOARDING = 1 + DECLINE = 2 + + +class TrainingGuide(Widget): + def __init__(self, completed_callback=None): + super().__init__() + self._completed_callback = completed_callback + + self._step = 0 + self._load_images() + + def _load_images(self): + self._images = [] + paths = [fn for fn in os.listdir(os.path.join(BASEDIR, "selfdrive/assets/training")) if re.match(r'^step\d*\.png$', fn)] + paths = sorted(paths, key=lambda x: int(re.search(r'\d+', x).group())) + for fn in paths: + path = os.path.join(BASEDIR, "selfdrive/assets/training", fn) + self._images.append(gui_app.texture(path, gui_app.width, gui_app.height)) + + def _handle_mouse_release(self, mouse_pos): + if rl.check_collision_point_rec(mouse_pos, STEP_RECTS[self._step]): + # Record DM camera? + if self._step == DM_RECORD_STEP: + yes = rl.check_collision_point_rec(mouse_pos, DM_RECORD_YES_RECT) + print(f"putting RecordFront to {yes}") + ui_state.params.put_bool("RecordFront", yes) + + # Restart training? + elif self._step == len(self._images) - 1: + if rl.check_collision_point_rec(mouse_pos, RESTART_TRAINING_RECT): + self._step = -1 + + self._step += 1 + + # Finished? + if self._step >= len(self._images): + self._step = 0 + if self._completed_callback: + self._completed_callback() + + def _render(self, _): + rl.draw_texture(self._images[self._step], 0, 0, rl.WHITE) + + # progress bar + if 0 < self._step < len(STEP_RECTS) - 1: + h = 20 + w = int((self._step / (len(STEP_RECTS) - 1)) * self._rect.width) + rl.draw_rectangle(int(self._rect.x), int(self._rect.y + self._rect.height - h), + w, h, rl.Color(70, 91, 234, 255)) + + if DEBUG: + rl.draw_rectangle_lines_ex(STEP_RECTS[self._step], 3, rl.RED) + + return -1 + + +class TermsPage(Widget): + def __init__(self, on_accept=None, on_decline=None): + super().__init__() + self._on_accept = on_accept + self._on_decline = on_decline + + self._title = Label("Welcome to openpilot", font_size=90, font_weight=FontWeight.BOLD, text_alignment=TextAlignment.LEFT) + self._desc = Label("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing.", + font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=TextAlignment.LEFT) + + self._decline_btn = Button("Decline", click_callback=on_decline) + self._accept_btn = Button("Agree", button_style=ButtonStyle.PRIMARY, click_callback=on_accept) + + def _render(self, _): + welcome_x = self._rect.x + 165 + welcome_y = self._rect.y + 165 + welcome_rect = rl.Rectangle(welcome_x, welcome_y, self._rect.width - welcome_x, 90) + self._title.render(welcome_rect) + + desc_x = welcome_x + # TODO: Label doesn't top align when wrapping + desc_y = welcome_y - 100 + desc_rect = rl.Rectangle(desc_x, desc_y, self._rect.width - desc_x, self._rect.height - desc_y - 250) + self._desc.render(desc_rect) + + btn_y = self._rect.y + self._rect.height - 160 - 45 + btn_width = (self._rect.width - 45 * 3) / 2 + self._decline_btn.render(rl.Rectangle(self._rect.x + 45, btn_y, btn_width, 160)) + self._accept_btn.render(rl.Rectangle(self._rect.x + 45 * 2 + btn_width, btn_y, btn_width, 160)) + + if DEBUG: + rl.draw_rectangle_lines_ex(welcome_rect, 3, rl.RED) + rl.draw_rectangle_lines_ex(desc_rect, 3, rl.RED) + + return -1 + + +class DeclinePage(Widget): + def __init__(self, back_callback=None): + super().__init__() + self._text = Label("You must accept the Terms and Conditions in order to use openpilot.", + font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=TextAlignment.LEFT) + self._back_btn = Button("Back", click_callback=back_callback) + self._uninstall_btn = Button("Decline, uninstall openpilot", button_style=ButtonStyle.DANGER, + click_callback=self._on_uninstall_clicked) + + def _on_uninstall_clicked(self): + ui_state.params.put_bool("DoUninstall", True) + gui_app.request_close() + + def _render(self, _): + btn_y = self._rect.y + self._rect.height - 160 - 45 + btn_width = (self._rect.width - 45 * 3) / 2 + self._back_btn.render(rl.Rectangle(self._rect.x + 45, btn_y, btn_width, 160)) + self._uninstall_btn.render(rl.Rectangle(self._rect.x + 45 * 2 + btn_width, btn_y, btn_width, 160)) + + # text rect in middle of top and button + text_height = btn_y - (200 + 45) + text_rect = rl.Rectangle(self._rect.x + 165, self._rect.y + (btn_y - text_height) / 2 + 10, self._rect.width - (165 * 2), text_height) + if DEBUG: + rl.draw_rectangle_lines_ex(text_rect, 3, rl.RED) + self._text.render(text_rect) + + +class OnboardingWindow(Widget): + def __init__(self): + super().__init__() + self._current_terms_version = ui_state.params.get("TermsVersion") + self._current_training_version = ui_state.params.get("TrainingVersion") + self._accepted_terms: bool = ui_state.params.get("HasAcceptedTerms") == self._current_terms_version + self._training_done: bool = ui_state.params.get("CompletedTrainingVersion") == self._current_training_version + + self._state = OnboardingState.TERMS if not self._accepted_terms else OnboardingState.ONBOARDING + + # Windows + self._terms = TermsPage(on_accept=self._on_terms_accepted, on_decline=self._on_terms_declined) + self._training_guide: TrainingGuide | None = None + self._decline_page = DeclinePage(back_callback=self._on_decline_back) + + @property + def completed(self) -> bool: + return self._accepted_terms and self._training_done + + def _on_terms_declined(self): + self._state = OnboardingState.DECLINE + + def _on_decline_back(self): + self._state = OnboardingState.TERMS + + def _on_terms_accepted(self): + ui_state.params.put("HasAcceptedTerms", self._current_terms_version) + self._state = OnboardingState.ONBOARDING + if self._training_done: + gui_app.set_modal_overlay(None) + + def _on_completed_training(self): + ui_state.params.put("CompletedTrainingVersion", self._current_training_version) + gui_app.set_modal_overlay(None) + + def _render(self, _): + if self._training_guide is None: + self._training_guide = TrainingGuide(completed_callback=self._on_completed_training) + + if self._state == OnboardingState.TERMS: + self._terms.render(self._rect) + if self._state == OnboardingState.ONBOARDING: + self._training_guide.render(self._rect) + elif self._state == OnboardingState.DECLINE: + self._decline_page.render(self._rect) + return -1 diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 14847df102..b34042d43e 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -5,6 +5,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app @@ -36,6 +37,7 @@ class DeviceLayout(Widget): self._driver_camera: DriverCameraDialog | None = None self._pair_device_dialog: PairingDialog | None = None self._fcc_dialog: HtmlRenderer | None = None + self._training_guide: TrainingGuide | None = None items = self._initialize_items() self._scroller = Scroller(items, line_separator=True, spacing=0) @@ -145,9 +147,11 @@ class DeviceLayout(Widget): def _on_regulatory(self): if not self._fcc_dialog: self._fcc_dialog = HtmlRenderer(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html")) + gui_app.set_modal_overlay(self._fcc_dialog, callback=lambda result: setattr(self, '_fcc_dialog', None)) - gui_app.set_modal_overlay(self._fcc_dialog, - callback=lambda result: setattr(self, '_fcc_dialog', None), - ) - - def _on_review_training_guide(self): pass + def _on_review_training_guide(self): + if not self._training_guide: + def completed_callback(): + gui_app.set_modal_overlay(None) + self._training_guide = TrainingGuide(completed_callback=completed_callback) + gui_app.set_modal_overlay(self._training_guide) From e4784d44f6d56efc6ba2663cceddb4aee5796297 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 30 Sep 2025 14:33:10 -0700 Subject: [PATCH 068/341] bump panda (#36226) bump --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 48c776a3df..615009cf0f 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 48c776a3df87f68da00e099510f2904c2217b303 +Subproject commit 615009cf0f8fb8f3feadac160fbb0a07e4de171b From 16a42067208006eb39f190bb23de502eb0adfd74 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 16:34:45 -0700 Subject: [PATCH 069/341] Revert "Reapply "raylib: 20 FPS onroad (#36208)"" This reverts commit ed185e90f680adc127111bbec29579619b5d753c. --- common/filter_simple.py | 11 +++-------- selfdrive/ui/layouts/main.py | 10 ---------- selfdrive/ui/onroad/model_renderer.py | 5 ++--- selfdrive/ui/ui_state.py | 4 ++-- system/ui/lib/application.py | 19 ++++++------------- system/ui/widgets/network.py | 8 +++----- 6 files changed, 16 insertions(+), 41 deletions(-) diff --git a/common/filter_simple.py b/common/filter_simple.py index 8a7105063d..9ea6fe3070 100644 --- a/common/filter_simple.py +++ b/common/filter_simple.py @@ -1,21 +1,16 @@ class FirstOrderFilter: def __init__(self, x0, rc, dt, initialized=True): self.x = x0 - self._dt = dt + self.dt = dt self.update_alpha(rc) self.initialized = initialized - def update_dt(self, dt): - self._dt = dt - self.update_alpha(self._rc) - def update_alpha(self, rc): - self._rc = rc - self._alpha = self._dt / (self._rc + self._dt) + self.alpha = self.dt / (rc + self.dt) def update(self, x): if self.initialized: - self.x = (1. - self._alpha) * self.x + self._alpha * x + self.x = (1. - self.alpha) * self.x + self.alpha * x else: self.initialized = True self.x = x diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 97f9d6b222..6628ab4d60 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -1,7 +1,6 @@ import pyray as rl from enum import IntEnum import cereal.messaging as messaging -from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH from openpilot.selfdrive.ui.layouts.home import HomeLayout from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType @@ -11,10 +10,6 @@ from openpilot.system.ui.widgets import Widget from openpilot.selfdrive.ui.layouts.onboarding import OnboardingWindow -ONROAD_FPS = 20 -OFFROAD_FPS = 60 - - class MainState(IntEnum): HOME = 0 SETTINGS = 1 @@ -31,8 +26,6 @@ class MainLayout(Widget): self._current_mode = MainState.HOME self._prev_onroad = False - gui_app.set_target_fps(OFFROAD_FPS) - # Initialize layouts self._layouts = {MainState.HOME: HomeLayout(), MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()} @@ -87,9 +80,6 @@ class MainLayout(Widget): self._current_mode = layout self._layouts[self._current_mode].show_event() - # No need to draw onroad faster than source (model at 20Hz) and prevents screen tearing - gui_app.set_target_fps(ONROAD_FPS if self._current_mode == MainState.ONROAD else OFFROAD_FPS) - def open_settings(self, panel_type: PanelType): self._layouts[MainState.SETTINGS].set_current_panel(panel_type) self._set_current_layout(MainState.SETTINGS) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 4c036873d0..d84e16cb54 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -7,7 +7,7 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.ui.lib.shader_polygon import draw_polygon from openpilot.system.ui.widgets import Widget @@ -48,7 +48,7 @@ class ModelRenderer(Widget): super().__init__() self._longitudinal_control = False self._experimental_mode = False - self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / gui_app.target_fps) + self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / DEFAULT_FPS) self._prev_allow_throttle = True self._lane_line_probs = np.zeros(4, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32) @@ -277,7 +277,6 @@ class ModelRenderer(Widget): return allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control - self._blend_filter.update_dt(1 / gui_app.target_fps) self._blend_filter.update(int(allow_throttle)) if self._experimental_mode: diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 39a65a9191..13532620cb 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -9,6 +9,7 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params, UnknownKeyName from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.lib.prime_state import PrimeState +from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app @@ -152,7 +153,7 @@ class Device: self._offroad_brightness: int = BACKLIGHT_OFFROAD self._last_brightness: int = 0 - self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / gui_app.target_fps) + self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / DEFAULT_FPS) self._brightness_thread: threading.Thread | None = None def reset_interactive_timeout(self, timeout: int = -1) -> None: @@ -189,7 +190,6 @@ class Device: clipped_brightness = float(np.clip(100 * clipped_brightness, 10, 100)) - self._brightness_filter.update_dt(1 / gui_app.target_fps) brightness = round(self._brightness_filter.update(clipped_brightness)) if not self._awake: brightness = 0 diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index ed2a20dd11..4973efa063 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -11,10 +11,10 @@ from enum import StrEnum from typing import NamedTuple from importlib.resources import as_file, files from openpilot.common.swaglog import cloudlog -from openpilot.system.hardware import HARDWARE, PC +from openpilot.system.hardware import HARDWARE, PC, TICI from openpilot.common.realtime import Ratekeeper -_DEFAULT_FPS = int(os.getenv("FPS", "60")) +DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions @@ -130,7 +130,7 @@ class GuiApplication: self._scaled_height = int(self._height * self._scale) self._render_texture: rl.RenderTexture | None = None self._textures: dict[str, rl.Texture] = {} - self._target_fps: int = _DEFAULT_FPS + self._target_fps: int = DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() self._window_close_requested = False self._trace_log_callback = None @@ -145,7 +145,7 @@ class GuiApplication: def request_close(self): self._window_close_requested = True - def init_window(self, title: str, fps: int = _DEFAULT_FPS): + def init_window(self, title: str, fps: int = DEFAULT_FPS): atexit.register(self.close) # Automatically call close() on exit HARDWARE.set_display_power(True) @@ -164,22 +164,15 @@ class GuiApplication: rl.set_mouse_scale(1 / self._scale, 1 / self._scale) self._render_texture = rl.load_render_texture(self._width, self._height) rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) + rl.set_target_fps(fps) - self.set_target_fps(fps) + self._target_fps = fps self._set_styles() self._load_fonts() if not PC: self._mouse.start() - @property - def target_fps(self): - return self._target_fps - - def set_target_fps(self, fps: int): - self._target_fps = fps - rl.set_target_fps(fps) - def set_modal_overlay(self, overlay, callback: Callable | None = None): self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 986d7158de..119901da40 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -5,7 +5,7 @@ from typing import cast import pyray as rl from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, DEFAULT_FPS from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType from openpilot.system.ui.widgets import Widget @@ -50,12 +50,10 @@ class NavButton(Widget): super().__init__() self.text = text self.set_rect(rl.Rectangle(0, 0, 400, 100)) - self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) - self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) + self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) + self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) def set_position(self, x: float, y: float) -> None: - self._x_pos_filter.update_dt(1 / gui_app.target_fps) - self._y_pos_filter.update_dt(1 / gui_app.target_fps) x = self._x_pos_filter.update(x) y = self._y_pos_filter.update(y) changed = (self._rect.x != x or self._rect.y != y) From 3efa52f53ba03368250044307bed19462655f603 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 20:05:40 -0700 Subject: [PATCH 070/341] fix missing import --- selfdrive/ui/layouts/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 6628ab4d60..1f56122241 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -1,6 +1,7 @@ import pyray as rl from enum import IntEnum import cereal.messaging as messaging +from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH from openpilot.selfdrive.ui.layouts.home import HomeLayout from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType From d24a14cb39d0d8b443fcf9f10c51e1a22e660af0 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Tue, 30 Sep 2025 20:32:19 -0700 Subject: [PATCH 071/341] =?UTF-8?q?DM:=20Large=20Donut=20model=20?= =?UTF-8?q?=F0=9F=8D=A9=20(#36198)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 59cfd731-6f80-4857-9271-10d952165079/225 * deprecate at the end --- cereal/log.capnp | 10 +++++----- selfdrive/modeld/dmonitoringmodeld.py | 12 ++++-------- selfdrive/modeld/models/README.md | 3 +-- selfdrive/modeld/models/dmonitoring_model.current | 2 -- selfdrive/modeld/models/dmonitoring_model.onnx | 4 ++-- selfdrive/monitoring/helpers.py | 10 +--------- selfdrive/monitoring/test_monitoring.py | 1 - 7 files changed, 13 insertions(+), 29 deletions(-) delete mode 100644 selfdrive/modeld/models/dmonitoring_model.current diff --git a/cereal/log.capnp b/cereal/log.capnp index b98c1f5242..019fbbe10b 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -2146,13 +2146,10 @@ struct Joystick { struct DriverStateV2 { frameId @0 :UInt32; modelExecutionTime @1 :Float32; - dspExecutionTimeDEPRECATED @2 :Float32; gpuExecutionTime @8 :Float32; rawPredictions @3 :Data; - poorVisionProb @4 :Float32; wheelOnRightProb @5 :Float32; - leftDriverData @6 :DriverData; rightDriverData @7 :DriverData; @@ -2167,10 +2164,13 @@ struct DriverStateV2 { leftBlinkProb @7 :Float32; rightBlinkProb @8 :Float32; sunglassesProb @9 :Float32; - occludedProb @10 :Float32; - readyProb @11 :List(Float32); notReadyProb @12 :List(Float32); + occludedProbDEPRECATED @10 :Float32; + readyProbDEPRECATED @11 :List(Float32); } + + dspExecutionTimeDEPRECATED @2 :Float32; + poorVisionProbDEPRECATED @4 :Float32; } struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 { diff --git a/selfdrive/modeld/dmonitoringmodeld.py b/selfdrive/modeld/dmonitoringmodeld.py index 5aeb035bdc..2851a3e7da 100755 --- a/selfdrive/modeld/dmonitoringmodeld.py +++ b/selfdrive/modeld/dmonitoringmodeld.py @@ -25,13 +25,13 @@ from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from MODEL_WIDTH, MODEL_HEIGHT = DM_INPUT_SIZE CALIB_LEN = 3 FEATURE_LEN = 512 -OUTPUT_SIZE = 84 + FEATURE_LEN +OUTPUT_SIZE = 83 + FEATURE_LEN PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl' - +# TODO: slice from meta class DriverStateResult(ctypes.Structure): _fields_ = [ ("face_orientation", ctypes.c_float*3), @@ -46,8 +46,8 @@ class DriverStateResult(ctypes.Structure): ("left_blink_prob", ctypes.c_float), ("right_blink_prob", ctypes.c_float), ("sunglasses_prob", ctypes.c_float), - ("occluded_prob", ctypes.c_float), - ("ready_prob", ctypes.c_float*4), + ("_unused_c", ctypes.c_float), + ("_unused_d", ctypes.c_float*4), ("not_ready_prob", ctypes.c_float*2)] @@ -55,7 +55,6 @@ class DMonitoringModelResult(ctypes.Structure): _fields_ = [ ("driver_state_lhd", DriverStateResult), ("driver_state_rhd", DriverStateResult), - ("poor_vision_prob", ctypes.c_float), ("wheel_on_right_prob", ctypes.c_float), ("features", ctypes.c_float*FEATURE_LEN)] @@ -107,8 +106,6 @@ def fill_driver_state(msg, ds_result: DriverStateResult): msg.leftBlinkProb = float(sigmoid(ds_result.left_blink_prob)) msg.rightBlinkProb = float(sigmoid(ds_result.right_blink_prob)) msg.sunglassesProb = float(sigmoid(ds_result.sunglasses_prob)) - msg.occludedProb = float(sigmoid(ds_result.occluded_prob)) - msg.readyProb = [float(sigmoid(x)) for x in ds_result.ready_prob] msg.notReadyProb = [float(sigmoid(x)) for x in ds_result.not_ready_prob] @@ -119,7 +116,6 @@ def get_driverstate_packet(model_output: np.ndarray, frame_id: int, location_ts: ds.frameId = frame_id ds.modelExecutionTime = execution_time ds.gpuExecutionTime = gpu_execution_time - ds.poorVisionProb = float(sigmoid(model_result.poor_vision_prob)) ds.wheelOnRightProb = float(sigmoid(model_result.wheel_on_right_prob)) ds.rawPredictions = model_output.tobytes() if SEND_RAW_PRED else b'' fill_driver_state(ds.leftDriverData, model_result.driver_state_lhd) diff --git a/selfdrive/modeld/models/README.md b/selfdrive/modeld/models/README.md index 255f28d80e..04b69c61c3 100644 --- a/selfdrive/modeld/models/README.md +++ b/selfdrive/modeld/models/README.md @@ -62,6 +62,5 @@ Refer to **slice_outputs** and **parse_vision_outputs/parse_policy_outputs** in * (deprecated) distracted probabilities: 2 * using phone probability: 1 * distracted probability: 1 - * common outputs 2 - * poor camera vision probability: 1 + * common outputs 1 * left hand drive probability: 1 diff --git a/selfdrive/modeld/models/dmonitoring_model.current b/selfdrive/modeld/models/dmonitoring_model.current deleted file mode 100644 index 121871ef2b..0000000000 --- a/selfdrive/modeld/models/dmonitoring_model.current +++ /dev/null @@ -1,2 +0,0 @@ -fa69be01-b430-4504-9d72-7dcb058eb6dd -d9fb22d1c4fa3ca3d201dbc8edf1d0f0918e53e6 diff --git a/selfdrive/modeld/models/dmonitoring_model.onnx b/selfdrive/modeld/models/dmonitoring_model.onnx index dcc727510e..5ae91f67a3 100644 --- a/selfdrive/modeld/models/dmonitoring_model.onnx +++ b/selfdrive/modeld/models/dmonitoring_model.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50efe6451a3fb3fa04b6bb0e846544533329bd46ecefe9e657e91214dee2aaeb -size 7196502 +oid sha256:9b2117ee4907add59e3fbe6829cda74e0ad71c0835b0ebb9373ba9425de0d336 +size 7191776 diff --git a/selfdrive/monitoring/helpers.py b/selfdrive/monitoring/helpers.py index a9cb21a3f1..c2e5bc3fe4 100644 --- a/selfdrive/monitoring/helpers.py +++ b/selfdrive/monitoring/helpers.py @@ -38,8 +38,6 @@ class DRIVER_MONITOR_SETTINGS: self._EE_THRESH12 = 15.0 self._EE_MAX_OFFSET1 = 0.06 self._EE_MIN_OFFSET1 = 0.025 - self._EE_THRESH21 = 0.01 - self._EE_THRESH22 = 0.35 self._POSE_PITCH_THRESHOLD = 0.3133 self._POSE_PITCH_THRESHOLD_SLACK = 0.3237 @@ -137,11 +135,8 @@ class DriverMonitoring: self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT) self.blink = DriverBlink() self.eev1 = 0. - self.eev2 = 1. self.ee1_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT) - self.ee2_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT) self.ee1_calibrated = False - self.ee2_calibrated = False self.always_on = always_on self.distracted_types = [] @@ -262,7 +257,7 @@ class DriverMonitoring: driver_data = driver_state.rightDriverData if self.wheel_on_right else driver_state.leftDriverData if not all(len(x) > 0 for x in (driver_data.faceOrientation, driver_data.facePosition, driver_data.faceOrientationStd, driver_data.facePositionStd, - driver_data.readyProb, driver_data.notReadyProb)): + driver_data.notReadyProb)): return self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD @@ -279,7 +274,6 @@ class DriverMonitoring: self.blink.right = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) \ * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD) self.eev1 = driver_data.notReadyProb[0] - self.eev2 = driver_data.readyProb[0] self.distracted_types = self._get_distracted_types() self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types @@ -293,12 +287,10 @@ class DriverMonitoring: self.pose.pitch_offseter.push_and_update(self.pose.pitch) self.pose.yaw_offseter.push_and_update(self.pose.yaw) self.ee1_offseter.push_and_update(self.eev1) - self.ee2_offseter.push_and_update(self.eev2) self.pose.calibrated = self.pose.pitch_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT and \ self.pose.yaw_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT self.ee1_calibrated = self.ee1_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT - self.ee2_calibrated = self.ee2_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT self.is_model_uncertain = self.hi_stds > self.settings._HI_STD_FALLBACK_TIME self._set_timers(self.face_detected and not self.is_model_uncertain) diff --git a/selfdrive/monitoring/test_monitoring.py b/selfdrive/monitoring/test_monitoring.py index 2a20b20dc1..1cc7101880 100644 --- a/selfdrive/monitoring/test_monitoring.py +++ b/selfdrive/monitoring/test_monitoring.py @@ -25,7 +25,6 @@ def make_msg(face_detected, distracted=False, model_uncertain=False): ds.leftDriverData.faceOrientationStd = [1.*model_uncertain, 1.*model_uncertain, 1.*model_uncertain] ds.leftDriverData.facePositionStd = [1.*model_uncertain, 1.*model_uncertain] # TODO: test both separately when e2e is used - ds.leftDriverData.readyProb = [0., 0., 0., 0.] ds.leftDriverData.notReadyProb = [0., 0.] return ds From 63e0e038fadfaed5f307a1813251929f0238acf3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 22:11:21 -0700 Subject: [PATCH 072/341] raylib: don't use DEFAULT_FPS (#36228) * dont use DEFAULT_FPS * replace --- selfdrive/ui/onroad/model_renderer.py | 4 ++-- selfdrive/ui/ui_state.py | 5 ++--- system/ui/lib/application.py | 10 +++++++--- system/ui/widgets/network.py | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index d84e16cb54..a770c0f92f 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -7,7 +7,7 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import DEFAULT_FPS +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.shader_polygon import draw_polygon from openpilot.system.ui.widgets import Widget @@ -48,7 +48,7 @@ class ModelRenderer(Widget): super().__init__() self._longitudinal_control = False self._experimental_mode = False - self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / DEFAULT_FPS) + self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / gui_app.target_fps) self._prev_allow_throttle = True self._lane_line_probs = np.zeros(4, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32) diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 13532620cb..bb3abcddb6 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -9,9 +9,8 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params, UnknownKeyName from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.lib.prime_state import PrimeState -from openpilot.system.ui.lib.application import DEFAULT_FPS -from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app +from openpilot.system.hardware import HARDWARE UI_BORDER_SIZE = 30 BACKLIGHT_OFFROAD = 50 @@ -153,7 +152,7 @@ class Device: self._offroad_brightness: int = BACKLIGHT_OFFROAD self._last_brightness: int = 0 - self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / DEFAULT_FPS) + self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / gui_app.target_fps) self._brightness_thread: threading.Thread | None = None def reset_interactive_timeout(self, timeout: int = -1) -> None: diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 4973efa063..a16f0b0bda 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -14,7 +14,7 @@ from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE, PC, TICI from openpilot.common.realtime import Ratekeeper -DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) +_DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions @@ -130,7 +130,7 @@ class GuiApplication: self._scaled_height = int(self._height * self._scale) self._render_texture: rl.RenderTexture | None = None self._textures: dict[str, rl.Texture] = {} - self._target_fps: int = DEFAULT_FPS + self._target_fps: int = _DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() self._window_close_requested = False self._trace_log_callback = None @@ -142,10 +142,14 @@ class GuiApplication: # Debug variables self._mouse_history: deque[MousePos] = deque(maxlen=MOUSE_THREAD_RATE) + @property + def target_fps(self): + return self._target_fps + def request_close(self): self._window_close_requested = True - def init_window(self, title: str, fps: int = DEFAULT_FPS): + def init_window(self, title: str, fps: int = _DEFAULT_FPS): atexit.register(self.close) # Automatically call close() on exit HARDWARE.set_display_power(True) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 119901da40..03385609d6 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -5,7 +5,7 @@ from typing import cast import pyray as rl from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, DEFAULT_FPS +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType from openpilot.system.ui.widgets import Widget @@ -50,8 +50,8 @@ class NavButton(Widget): super().__init__() self.text = text self.set_rect(rl.Rectangle(0, 0, 400, 100)) - self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) - self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) + self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) + self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) def set_position(self, x: float, y: float) -> None: x = self._x_pos_filter.update(x) From 5f33b2fb2dafb9957f539d9d94fc51cd4a82244a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 22:25:43 -0700 Subject: [PATCH 073/341] raylib: frame independent scroller (#36227) * rm that * almost * yess * some work * more * todo * okay viber is good once in a while * temp * chadder can't do this * revert * this was broken anyway * fixes * mouse wheel scroll * some clean up * kinda works * way better * can tap to stop * more clean up * more clean up * revert last mouse * fix * debug only * no print * ahh setup.py fps doesn't affect DEFAULT_FPS ofc * rest * fix text * fix touch valid for network --- selfdrive/ui/layouts/settings/firehose.py | 6 +- selfdrive/ui/widgets/offroad_alerts.py | 4 +- system/ui/lib/scroll_panel.py | 247 ++++++++-------------- system/ui/setup.py | 6 +- system/ui/text.py | 8 +- system/ui/widgets/html_render.py | 4 +- system/ui/widgets/network.py | 29 ++- system/ui/widgets/option_dialog.py | 4 +- system/ui/widgets/scroller.py | 5 +- 9 files changed, 123 insertions(+), 190 deletions(-) diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py index b3db1fa5f0..7e1d3b61ba 100644 --- a/selfdrive/ui/layouts/settings/firehose.py +++ b/selfdrive/ui/layouts/settings/firehose.py @@ -71,7 +71,7 @@ class FirehoseLayout(Widget): content_rect = rl.Rectangle(rect.x, rect.y, rect.width, content_height) # Handle scrolling and render with clipping - scroll_offset = self.scroll_panel.handle_scroll(rect, content_rect) + scroll_offset = self.scroll_panel.update(rect, content_rect) rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) self._render_content(rect, scroll_offset) rl.end_scissor_mode() @@ -106,9 +106,9 @@ class FirehoseLayout(Widget): return height - def _render_content(self, rect: rl.Rectangle, scroll_offset: rl.Vector2): + def _render_content(self, rect: rl.Rectangle, scroll_offset: float): x = int(rect.x + 40) - y = int(rect.y + 40 + scroll_offset.y) + y = int(rect.y + 40 + scroll_offset) w = int(rect.width - 80) # Title diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index da0ea287cf..6ca0ca2c40 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -109,7 +109,7 @@ class AbstractAlert(Widget, ABC): def _render_scrollable_content(self): content_total_height = self.get_content_height() content_bounds = rl.Rectangle(0, 0, self.scroll_panel_rect.width, content_total_height) - scroll_offset = self.scroll_panel.handle_scroll(self.scroll_panel_rect, content_bounds) + scroll_offset = self.scroll_panel.update(self.scroll_panel_rect, content_bounds) rl.begin_scissor_mode( int(self.scroll_panel_rect.x), @@ -120,7 +120,7 @@ class AbstractAlert(Widget, ABC): content_rect_with_scroll = rl.Rectangle( self.scroll_panel_rect.x, - self.scroll_panel_rect.y + scroll_offset.y, + self.scroll_panel_rect.y + scroll_offset, self.scroll_panel_rect.width, content_total_height, ) diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index e2296fd5ed..5dacae8210 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -1,189 +1,124 @@ -import time +import math import pyray as rl -from collections import deque from enum import IntEnum -from openpilot.system.ui.lib.application import gui_app, MouseEvent, MousePos +from openpilot.system.ui.lib.application import gui_app, MouseEvent +from openpilot.common.filter_simple import FirstOrderFilter # Scroll constants for smooth scrolling behavior -MOUSE_WHEEL_SCROLL_SPEED = 30 -INERTIA_FRICTION = 0.92 # The rate at which the inertia slows down -MIN_VELOCITY = 0.5 # Minimum velocity before stopping the inertia -DRAG_THRESHOLD = 12 # Pixels of movement to consider it a drag, not a click -BOUNCE_FACTOR = 0.2 # Elastic bounce when scrolling past boundaries -BOUNCE_RETURN_SPEED = 0.15 # How quickly it returns from the bounce -MAX_BOUNCE_DISTANCE = 150 # Maximum distance for bounce effect -FLICK_MULTIPLIER = 1.8 # Multiplier for flick gestures -VELOCITY_HISTORY_SIZE = 5 # Track velocity over multiple frames for smoother motion +MOUSE_WHEEL_SCROLL_SPEED = 50 +BOUNCE_RETURN_RATE = 5 # ~0.92 at 60fps +MIN_VELOCITY = 2 # px/s, changes from auto scroll to steady state +MIN_VELOCITY_FOR_CLICKING = 2 * 60 # px/s, accepts clicks while auto scrolling below this velocity +DRAG_THRESHOLD = 12 # pixels of movement to consider it a drag, not a click + +DEBUG = False class ScrollState(IntEnum): - IDLE = 0 - DRAGGING_CONTENT = 1 - DRAGGING_SCROLLBAR = 2 - BOUNCING = 3 + IDLE = 0 # Not dragging, content may be bouncing or scrolling with inertia + DRAGGING_CONTENT = 1 # User is actively dragging the content class GuiScrollPanel: - def __init__(self, show_vertical_scroll_bar: bool = False): + def __init__(self): self._scroll_state: ScrollState = ScrollState.IDLE self._last_mouse_y: float = 0.0 self._start_mouse_y: float = 0.0 # Track the initial mouse position for drag detection - self._offset = rl.Vector2(0, 0) - self._view = rl.Rectangle(0, 0, 0, 0) - self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar - self._velocity_y = 0.0 # Velocity for inertia - self._is_dragging: bool = False - self._bounce_offset: float = 0.0 - self._velocity_history: deque[float] = deque(maxlen=VELOCITY_HISTORY_SIZE) + self._offset_filter_y = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps) + self._velocity_filter_y = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps) self._last_drag_time: float = 0.0 - self._content_rect: rl.Rectangle | None = None - self._bounds_rect: rl.Rectangle | None = None - def handle_scroll(self, bounds: rl.Rectangle, content: rl.Rectangle) -> rl.Vector2: - # TODO: HACK: this class is driven by mouse events, so we need to ensure we have at least one event to process - for mouse_event in gui_app.mouse_events or [MouseEvent(MousePos(0, 0), 0, False, False, False, time.monotonic())]: + def update(self, bounds: rl.Rectangle, content: rl.Rectangle) -> float: + for mouse_event in gui_app.mouse_events: if mouse_event.slot == 0: self._handle_mouse_event(mouse_event, bounds, content) - return self._offset - def _handle_mouse_event(self, mouse_event: MouseEvent, bounds: rl.Rectangle, content: rl.Rectangle): - # Store rectangles for reference - self._content_rect = content - self._bounds_rect = bounds + self._update_state(bounds, content) - max_scroll_y = max(content.height - bounds.height, 0) + return float(self._offset_filter_y.x) - # Start dragging on mouse press - if rl.check_collision_point_rec(mouse_event.pos, bounds) and mouse_event.left_pressed: - if self._scroll_state == ScrollState.IDLE or self._scroll_state == ScrollState.BOUNCING: - self._scroll_state = ScrollState.DRAGGING_CONTENT - if self._show_vertical_scroll_bar: - scrollbar_width = rl.gui_get_style(rl.GuiControl.LISTVIEW, rl.GuiListViewProperty.SCROLLBAR_WIDTH) - scrollbar_x = bounds.x + bounds.width - scrollbar_width - if mouse_event.pos.x >= scrollbar_x: - self._scroll_state = ScrollState.DRAGGING_SCROLLBAR - - # TODO: hacky - # when clicking while moving, go straight into dragging - self._is_dragging = abs(self._velocity_y) > MIN_VELOCITY - self._last_mouse_y = mouse_event.pos.y - self._start_mouse_y = mouse_event.pos.y - self._last_drag_time = mouse_event.t - self._velocity_history.clear() - self._velocity_y = 0.0 - self._bounce_offset = 0.0 - - # Handle active dragging - if self._scroll_state == ScrollState.DRAGGING_CONTENT or self._scroll_state == ScrollState.DRAGGING_SCROLLBAR: - if mouse_event.left_down: - delta_y = mouse_event.pos.y - self._last_mouse_y - - # Track velocity for inertia - time_since_last_drag = mouse_event.t - self._last_drag_time - if time_since_last_drag > 0: - # TODO: HACK: /2 since we usually get two touch events per frame - drag_velocity = delta_y / time_since_last_drag / 60.0 / 2 # TODO: shouldn't be hardcoded - self._velocity_history.append(drag_velocity) - - self._last_drag_time = mouse_event.t - - # Detect actual dragging - total_drag = abs(mouse_event.pos.y - self._start_mouse_y) - if total_drag > DRAG_THRESHOLD: - self._is_dragging = True - - if self._scroll_state == ScrollState.DRAGGING_CONTENT: - # Add resistance at boundaries - if (self._offset.y > 0 and delta_y > 0) or (self._offset.y < -max_scroll_y and delta_y < 0): - delta_y *= BOUNCE_FACTOR - - self._offset.y += delta_y - elif self._scroll_state == ScrollState.DRAGGING_SCROLLBAR: - scroll_ratio = content.height / bounds.height - self._offset.y -= delta_y * scroll_ratio - - self._last_mouse_y = mouse_event.pos.y - - elif mouse_event.left_released: - # Calculate flick velocity - if self._velocity_history: - total_weight = 0 - weighted_velocity = 0.0 - - for i, v in enumerate(self._velocity_history): - weight = i + 1 - weighted_velocity += v * weight - total_weight += weight - - if total_weight > 0: - avg_velocity = weighted_velocity / total_weight - self._velocity_y = avg_velocity * FLICK_MULTIPLIER - - # Check bounds - if self._offset.y > 0 or self._offset.y < -max_scroll_y: - self._scroll_state = ScrollState.BOUNCING - else: - self._scroll_state = ScrollState.IDLE + def _update_state(self, bounds: rl.Rectangle, content: rl.Rectangle): + if DEBUG: + rl.draw_rectangle_lines(0, 0, abs(int(self._velocity_filter_y.x)), 10, rl.RED) # Handle mouse wheel - wheel_move = rl.get_mouse_wheel_move() - if wheel_move != 0: - self._velocity_y = 0.0 + self._offset_filter_y.x += rl.get_mouse_wheel_move() * MOUSE_WHEEL_SCROLL_SPEED - if self._show_vertical_scroll_bar: - self._offset.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) - rl.gui_scroll_panel(bounds, rl.ffi.NULL, content, self._offset, self._view) - else: - self._offset.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED - - if self._offset.y > 0 or self._offset.y < -max_scroll_y: - self._scroll_state = ScrollState.BOUNCING - - # Apply inertia (continue scrolling after mouse release) + max_scroll_distance = max(0, content.height - bounds.height) if self._scroll_state == ScrollState.IDLE: - if abs(self._velocity_y) > MIN_VELOCITY: - self._offset.y += self._velocity_y - self._velocity_y *= INERTIA_FRICTION + above_bounds, below_bounds = self._check_bounds(bounds, content) - if self._offset.y > 0 or self._offset.y < -max_scroll_y: - self._scroll_state = ScrollState.BOUNCING + # Decay velocity when idle + if abs(self._velocity_filter_y.x) > MIN_VELOCITY: + # Faster decay if bouncing back from out of bounds + friction = math.exp(-BOUNCE_RETURN_RATE * 1 / gui_app.target_fps) + self._velocity_filter_y.x *= friction ** 2 if (above_bounds or below_bounds) else friction else: - self._velocity_y = 0.0 + self._velocity_filter_y.x = 0.0 - # Handle bouncing effect - elif self._scroll_state == ScrollState.BOUNCING: - target_y = 0.0 - if self._offset.y < -max_scroll_y: - target_y = -max_scroll_y + if above_bounds or below_bounds: + if above_bounds: + self._offset_filter_y.update(0) + else: + self._offset_filter_y.update(-max_scroll_distance) - distance = target_y - self._offset.y - bounce_step = distance * BOUNCE_RETURN_SPEED - self._offset.y += bounce_step - self._velocity_y *= INERTIA_FRICTION * 0.8 + self._offset_filter_y.x += self._velocity_filter_y.x / gui_app.target_fps - if abs(distance) < 0.5 and abs(self._velocity_y) < MIN_VELOCITY: - self._offset.y = target_y - self._velocity_y = 0.0 + elif self._scroll_state == ScrollState.DRAGGING_CONTENT: + # Mouse not moving, decay velocity + if not len(gui_app.mouse_events): + self._velocity_filter_y.update(0.0) + + # Settle to exact bounds + if abs(self._offset_filter_y.x) < 1e-2: + self._offset_filter_y.x = 0.0 + elif abs(self._offset_filter_y.x + max_scroll_distance) < 1e-2: + self._offset_filter_y.x = -max_scroll_distance + + def _handle_mouse_event(self, mouse_event: MouseEvent, bounds: rl.Rectangle, content: rl.Rectangle): + if self._scroll_state == ScrollState.IDLE: + if mouse_event.left_pressed: + self._start_mouse_y = mouse_event.pos.y + # Interrupt scrolling with new drag + # TODO: stop scrolling with any tap, need to fix is_touch_valid + if abs(self._velocity_filter_y.x) > MIN_VELOCITY_FOR_CLICKING: + self._scroll_state = ScrollState.DRAGGING_CONTENT + # Start velocity at initial measurement for more immediate response + self._velocity_filter_y.initialized = False + + if mouse_event.left_down: + if abs(mouse_event.pos.y - self._start_mouse_y) > DRAG_THRESHOLD: + self._scroll_state = ScrollState.DRAGGING_CONTENT + # Start velocity at initial measurement for more immediate response + self._velocity_filter_y.initialized = False + + elif self._scroll_state == ScrollState.DRAGGING_CONTENT: + if mouse_event.left_released: self._scroll_state = ScrollState.IDLE + else: + delta_y = mouse_event.pos.y - self._last_mouse_y + above_bounds, below_bounds = self._check_bounds(bounds, content) + # Rubber banding effect when out of bands + if above_bounds or below_bounds: + delta_y /= 3 - # Limit bounce distance - if self._scroll_state != ScrollState.DRAGGING_CONTENT: - if self._offset.y > MAX_BOUNCE_DISTANCE: - self._offset.y = MAX_BOUNCE_DISTANCE - elif self._offset.y < -(max_scroll_y + MAX_BOUNCE_DISTANCE): - self._offset.y = -(max_scroll_y + MAX_BOUNCE_DISTANCE) + self._offset_filter_y.x += delta_y + + # Track velocity for inertia + dt = mouse_event.t - self._last_drag_time + if dt > 0: + drag_velocity = delta_y / dt + self._velocity_filter_y.update(drag_velocity) + + # TODO: just store last mouse event! + self._last_drag_time = mouse_event.t + self._last_mouse_y = mouse_event.pos.y + + def _check_bounds(self, bounds: rl.Rectangle, content: rl.Rectangle) -> tuple[bool, bool]: + max_scroll_distance = max(0, content.height - bounds.height) + above_bounds = self._offset_filter_y.x > 0 + below_bounds = self._offset_filter_y.x < -max_scroll_distance + return above_bounds, below_bounds def is_touch_valid(self): - return not self._is_dragging - - def get_normalized_scroll_position(self) -> float: - """Returns the current scroll position as a value from 0.0 to 1.0""" - if not self._content_rect or not self._bounds_rect: - return 0.0 - - max_scroll_y = max(self._content_rect.height - self._bounds_rect.height, 0) - if max_scroll_y == 0: - return 0.0 - - normalized = -self._offset.y / max_scroll_y - return max(0.0, min(1.0, normalized)) + return self._scroll_state == ScrollState.IDLE and abs(self._velocity_filter_y.x) < MIN_VELOCITY_FOR_CLICKING diff --git a/system/ui/setup.py b/system/ui/setup.py index a985e783be..e0d737cb1c 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -299,20 +299,20 @@ class Setup(Widget): def render_custom_software_warning(self, rect: rl.Rectangle): warn_rect = rl.Rectangle(rect.x, rect.y, rect.width, 1500) - offset = self._custom_software_warning_body_scroll_panel.handle_scroll(rect, warn_rect) + offset = self._custom_software_warning_body_scroll_panel.update(rect, warn_rect) button_width = (rect.width - MARGIN * 3) / 2 button_y = rect.height - MARGIN - BUTTON_HEIGHT rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(button_y - BODY_FONT_SIZE)) - y_offset = rect.y + offset.y + y_offset = rect.y + offset self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, y_offset + 150, rect.width - 265, TITLE_FONT_SIZE)) self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, y_offset + 200 , rect.width - 50, BODY_FONT_SIZE * 3)) rl.end_scissor_mode() self._custom_software_warning_back_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) self._custom_software_warning_continue_button.render(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT)) - if offset.y < (rect.height - warn_rect.height): + if offset < (rect.height - warn_rect.height): self._custom_software_warning_continue_button.set_enabled(True) self._custom_software_warning_continue_button.set_text("Continue") diff --git a/system/ui/text.py b/system/ui/text.py index 61ac043b72..3db930eb26 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -53,14 +53,14 @@ class TextWindow(Widget): self._textarea_rect = rl.Rectangle(MARGIN, MARGIN, gui_app.width - MARGIN * 2, gui_app.height - MARGIN * 2) self._wrapped_lines = wrap_text(text, FONT_SIZE, self._textarea_rect.width - 20) self._content_rect = rl.Rectangle(0, 0, self._textarea_rect.width - 20, len(self._wrapped_lines) * LINE_HEIGHT) - self._scroll_panel = GuiScrollPanel(show_vertical_scroll_bar=True) - self._scroll_panel._offset.y = -max(self._content_rect.height - self._textarea_rect.height, 0) + self._scroll_panel = GuiScrollPanel() + self._scroll_panel._offset_filter_y.x = -max(self._content_rect.height - self._textarea_rect.height, 0) def _render(self, rect: rl.Rectangle): - scroll = self._scroll_panel.handle_scroll(self._textarea_rect, self._content_rect) + scroll = self._scroll_panel.update(self._textarea_rect, self._content_rect) rl.begin_scissor_mode(int(self._textarea_rect.x), int(self._textarea_rect.y), int(self._textarea_rect.width), int(self._textarea_rect.height)) for i, line in enumerate(self._wrapped_lines): - position = rl.Vector2(self._textarea_rect.x + scroll.x, self._textarea_rect.y + scroll.y + i * LINE_HEIGHT) + position = rl.Vector2(self._textarea_rect.x, self._textarea_rect.y + scroll + i * LINE_HEIGHT) if position.y + LINE_HEIGHT < self._textarea_rect.y or position.y > self._textarea_rect.y + self._textarea_rect.height: continue rl.draw_text_ex(gui_app.font(), line, position, FONT_SIZE, 0, rl.WHITE) diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index d68ea58990..bb7eeb7c5c 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -116,10 +116,10 @@ class HtmlRenderer(Widget): total_height = self.get_total_height(int(scrollable_rect.width)) scroll_content_rect = rl.Rectangle(scrollable_rect.x, scrollable_rect.y, scrollable_rect.width, total_height) - scroll_offset = self._scroll_panel.handle_scroll(scrollable_rect, scroll_content_rect) + scroll_offset = self._scroll_panel.update(scrollable_rect, scroll_content_rect) rl.begin_scissor_mode(int(scrollable_rect.x), int(scrollable_rect.y), int(scrollable_rect.width), int(scrollable_rect.height)) - self._render_content(scrollable_rect, scroll_offset.y) + self._render_content(scrollable_rect, scroll_offset) rl.end_scissor_mode() button_width = (rect.width - 3 * 50) // 3 diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 03385609d6..3dd2a4d654 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -339,24 +339,23 @@ class WifiManagerUI(Widget): def _draw_network_list(self, rect: rl.Rectangle): content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self._networks) * ITEM_HEIGHT) - offset = self.scroll_panel.handle_scroll(rect, content_rect) - clicked = self.scroll_panel.is_touch_valid() and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) + offset = self.scroll_panel.update(rect, content_rect) rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) for i, network in enumerate(self._networks): - y_offset = rect.y + i * ITEM_HEIGHT + offset.y + y_offset = rect.y + i * ITEM_HEIGHT + offset item_rect = rl.Rectangle(rect.x, y_offset, rect.width, ITEM_HEIGHT) if not rl.check_collision_recs(item_rect, rect): continue - self._draw_network_item(item_rect, network, clicked) + self._draw_network_item(item_rect, network) if i < len(self._networks) - 1: line_y = int(item_rect.y + item_rect.height - 1) rl.draw_line(int(item_rect.x), int(line_y), int(item_rect.x + item_rect.width), line_y, rl.LIGHTGRAY) rl.end_scissor_mode() - def _draw_network_item(self, rect, network: Network, clicked: bool): + def _draw_network_item(self, rect, network: Network): spacing = 50 ssid_rect = rl.Rectangle(rect.x, rect.y, rect.width - self.btn_width * 2, ITEM_HEIGHT) signal_icon_rect = rl.Rectangle(rect.x + rect.width - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) @@ -396,18 +395,16 @@ class WifiManagerUI(Widget): self._draw_signal_strength_icon(signal_icon_rect, network) def _networks_buttons_callback(self, network): - if self.scroll_panel.is_touch_valid(): - if not network.is_saved and network.security_type != SecurityType.OPEN: - self.state = UIState.NEEDS_AUTH - self._state_network = network - self._password_retry = False - elif not network.is_connected: - self.connect_to_network(network) + if not network.is_saved and network.security_type != SecurityType.OPEN: + self.state = UIState.NEEDS_AUTH + self._state_network = network + self._password_retry = False + elif not network.is_connected: + self.connect_to_network(network) def _forget_networks_buttons_callback(self, network): - if self.scroll_panel.is_touch_valid(): - self.state = UIState.SHOW_FORGET_CONFIRM - self._state_network = network + self.state = UIState.SHOW_FORGET_CONFIRM + self._state_network = network def _draw_status_icon(self, rect, network: Network): """Draw the status icon based on network's connection state""" @@ -449,8 +446,10 @@ class WifiManagerUI(Widget): for n in self._networks: self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT, button_style=ButtonStyle.TRANSPARENT_WHITE) + self._networks_buttons[n.ssid].set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid()) self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI, font_size=45) + self._forget_networks_buttons[n.ssid].set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid()) def _on_need_auth(self, ssid): network = next((n for n in self._networks if n.ssid == ssid), None) diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index 8f33124b5c..cbab024f09 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -41,12 +41,12 @@ class MultiOptionDialog(Widget): list_content_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, content_h) # Scroll and render options - offset = self.scroll.handle_scroll(view_rect, list_content_rect) + offset = self.scroll.update(view_rect, list_content_rect) valid_click = self.scroll.is_touch_valid() and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) rl.begin_scissor_mode(int(view_rect.x), int(options_y), int(view_rect.width), int(options_h)) for i, option in enumerate(self.options): - item_y = options_y + i * (ITEM_HEIGHT + LIST_ITEM_SPACING) + offset.y + item_y = options_y + i * (ITEM_HEIGHT + LIST_ITEM_SPACING) + offset item_rect = rl.Rectangle(view_rect.x, item_y, view_rect.width, ITEM_HEIGHT) if rl.check_collision_recs(item_rect, view_rect): diff --git a/system/ui/widgets/scroller.py b/system/ui/widgets/scroller.py index 757500a71c..c76f30d196 100644 --- a/system/ui/widgets/scroller.py +++ b/system/ui/widgets/scroller.py @@ -52,7 +52,7 @@ class Scroller(Widget): content_height = sum(item.rect.height for item in visible_items) + self._spacing * (len(visible_items)) if not self._pad_end: content_height -= self._spacing - scroll = self.scroll_panel.handle_scroll(self._rect, rl.Rectangle(0, 0, self._rect.width, content_height)) + scroll = self.scroll_panel.update(self._rect, rl.Rectangle(0, 0, self._rect.width, content_height)) rl.begin_scissor_mode(int(self._rect.x), int(self._rect.y), int(self._rect.width), int(self._rect.height)) @@ -68,8 +68,7 @@ class Scroller(Widget): cur_height += item.rect.height + self._spacing * (idx != 0) # Consider scroll - x += scroll.x - y += scroll.y + y += scroll # Update item state item.set_position(x, y) From 5c0c2a17b0539aecfd7401ae96ca946a1142418e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 22:30:45 -0700 Subject: [PATCH 074/341] raylib: add mic indicator (#36207) * update lang * mic indicator * clean up * clean up * switch * fix * revert --- selfdrive/ui/layouts/main.py | 3 ++- selfdrive/ui/layouts/settings/toggles.py | 2 +- selfdrive/ui/layouts/sidebar.py | 23 ++++++++++++++++++++++- selfdrive/ui/ui_state.py | 1 + 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 1f56122241..a2401ef8be 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -47,7 +47,8 @@ class MainLayout(Widget): def _setup_callbacks(self): self._sidebar.set_callbacks(on_settings=self._on_settings_clicked, - on_flag=self._on_bookmark_clicked) + on_flag=self._on_bookmark_clicked, + open_settings=lambda: self.open_settings(PanelType.TOGGLES)) self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE)) self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state) self._layouts[MainState.ONROAD].set_click_callback(self._on_onroad_clicked) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 58afcec5ef..1e0e7bfd53 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -76,7 +76,7 @@ class TogglesLayout(Widget): icon="monitoring.png", ), toggle_item( - "Record Microphone Audio", + "Record and Upload Microphone Audio", DESCRIPTIONS["RecordAudio"], self._params.get_bool("RecordAudio"), icon="microphone.png", diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 4d47a9878c..5987d062ff 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -71,20 +71,26 @@ class Sidebar(Widget): self._temp_status = MetricData("TEMP", "GOOD", Colors.GOOD) self._panda_status = MetricData("VEHICLE", "ONLINE", Colors.GOOD) self._connect_status = MetricData("CONNECT", "OFFLINE", Colors.WARNING) + self._recording_audio = False self._home_img = gui_app.texture("images/button_home.png", HOME_BTN.width, HOME_BTN.height) self._flag_img = gui_app.texture("images/button_flag.png", HOME_BTN.width, HOME_BTN.height) self._settings_img = gui_app.texture("images/button_settings.png", SETTINGS_BTN.width, SETTINGS_BTN.height) + self._mic_img = gui_app.texture("icons/microphone.png", 30, 30) + self._mic_indicator_rect = rl.Rectangle(0, 0, 0, 0) self._font_regular = gui_app.font(FontWeight.NORMAL) self._font_bold = gui_app.font(FontWeight.SEMI_BOLD) # Callbacks self._on_settings_click: Callable | None = None self._on_flag_click: Callable | None = None + self._open_settings_callback: Callable | None = None - def set_callbacks(self, on_settings: Callable | None = None, on_flag: Callable | None = None): + def set_callbacks(self, on_settings: Callable | None = None, on_flag: Callable | None = None, + open_settings: Callable | None = None): self._on_settings_click = on_settings self._on_flag_click = on_flag + self._open_settings_callback = open_settings def _render(self, rect: rl.Rectangle): # Background @@ -101,6 +107,7 @@ class Sidebar(Widget): device_state = sm['deviceState'] + self._recording_audio = sm.alive['rawAudioData'] self._update_network_status(device_state) self._update_temperature_status(device_state) self._update_connection_status(device_state) @@ -143,6 +150,9 @@ class Sidebar(Widget): elif rl.check_collision_point_rec(mouse_pos, HOME_BTN) and ui_state.started: if self._on_flag_click: self._on_flag_click() + elif self._recording_audio and rl.check_collision_point_rec(mouse_pos, self._mic_indicator_rect): + if self._open_settings_callback: + self._open_settings_callback() def _draw_buttons(self, rect: rl.Rectangle): mouse_pos = rl.get_mouse_position() @@ -160,6 +170,17 @@ class Sidebar(Widget): tint = Colors.BUTTON_PRESSED if (ui_state.started and flag_pressed) else Colors.BUTTON_NORMAL rl.draw_texture(button_img, int(HOME_BTN.x), int(HOME_BTN.y), tint) + # Microphone button + if self._recording_audio: + self._mic_indicator_rect = rl.Rectangle(rect.x + rect.width - 138, rect.y + 245, 75, 40) + + mic_pressed = mouse_down and rl.check_collision_point_rec(mouse_pos, self._mic_indicator_rect) + bg_color = rl.Color(Colors.DANGER.r, Colors.DANGER.g, Colors.DANGER.b, int(255 * 0.65)) if mic_pressed else Colors.DANGER + + rl.draw_rectangle_rounded(self._mic_indicator_rect, 1, 10, bg_color) + rl.draw_texture(self._mic_img, int(self._mic_indicator_rect.x + (self._mic_indicator_rect.width - self._mic_img.width) / 2), + int(self._mic_indicator_rect.y + (self._mic_indicator_rect.height - self._mic_img.height) / 2), Colors.WHITE) + def _draw_network_indicator(self, rect: rl.Rectangle): # Signal strength dots x_start = rect.x + 58 diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index bb3abcddb6..9c45519b4b 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -50,6 +50,7 @@ class UIState: "managerState", "selfdriveState", "longitudinalPlan", + "rawAudioData", ] ) From b593b7cc43926fa6a5f20776319b4f91f86c4b05 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 23:43:23 -0700 Subject: [PATCH 075/341] raylib: SSH key text entry works more than once (#36230) * impossible * jarn * actually space * forgot --- selfdrive/ui/widgets/ssh_key.py | 4 ++-- system/ui/widgets/button.py | 12 ++++++------ system/ui/widgets/network.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/selfdrive/ui/widgets/ssh_key.py b/selfdrive/ui/widgets/ssh_key.py index 4f4a8dcdff..2fc13d4c96 100644 --- a/selfdrive/ui/widgets/ssh_key.py +++ b/selfdrive/ui/widgets/ssh_key.py @@ -34,7 +34,7 @@ class SshKeyAction(ItemAction): def __init__(self): super().__init__(self.MAX_WIDTH, True) - self._keyboard = Keyboard() + self._keyboard = Keyboard(min_text_size=1) self._params = Params() self._error_message: str = "" self._text_font = gui_app.font(FontWeight.MEDIUM) @@ -82,7 +82,7 @@ class SshKeyAction(ItemAction): def _handle_button_click(self): if self._state == SshKeyActionState.ADD: - self._keyboard.clear() + self._keyboard.reset() self._keyboard.set_title("Enter your GitHub username") gui_app.set_modal_overlay(self._keyboard, callback=self._on_username_submit) elif self._state == SshKeyActionState.REMOVE: diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 1e3f28eb8e..32d63952a2 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -14,7 +14,7 @@ class ButtonStyle(IntEnum): PRIMARY = 1 # For main actions DANGER = 2 # For critical actions, like reboot or delete TRANSPARENT = 3 # For buttons with transparent background and border - TRANSPARENT_WHITE = 3 # For buttons with transparent background and border + TRANSPARENT_WHITE_TEXT = 3 # For buttons with transparent background and border and white text ACTION = 4 LIST_ACTION = 5 # For list items with action buttons NO_EFFECT = 6 @@ -31,7 +31,7 @@ BUTTON_TEXT_COLOR = { ButtonStyle.PRIMARY: rl.Color(228, 228, 228, 255), ButtonStyle.DANGER: rl.Color(228, 228, 228, 255), ButtonStyle.TRANSPARENT: rl.BLACK, - ButtonStyle.TRANSPARENT_WHITE: rl.WHITE, + ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.WHITE, ButtonStyle.ACTION: rl.BLACK, ButtonStyle.LIST_ACTION: rl.Color(228, 228, 228, 255), ButtonStyle.NO_EFFECT: rl.Color(228, 228, 228, 255), @@ -40,7 +40,7 @@ BUTTON_TEXT_COLOR = { } BUTTON_DISABLED_TEXT_COLORS = { - ButtonStyle.TRANSPARENT_WHITE: rl.WHITE, + ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.WHITE, } BUTTON_BACKGROUND_COLORS = { @@ -48,7 +48,7 @@ BUTTON_BACKGROUND_COLORS = { ButtonStyle.PRIMARY: rl.Color(70, 91, 234, 255), ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, - ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, + ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, ButtonStyle.ACTION: rl.Color(189, 189, 189, 255), ButtonStyle.LIST_ACTION: rl.Color(57, 57, 57, 255), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), @@ -61,7 +61,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255), ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, - ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, + ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, ButtonStyle.ACTION: rl.Color(130, 130, 130, 255), ButtonStyle.LIST_ACTION: rl.Color(74, 74, 74, 74), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), @@ -70,7 +70,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { } BUTTON_DISABLED_BACKGROUND_COLORS = { - ButtonStyle.TRANSPARENT_WHITE: rl.BLANK, + ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, } _pressed_buttons: set[str] = set() # Track mouse press state globally diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 3dd2a4d654..9eaac6030b 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -445,7 +445,7 @@ class WifiManagerUI(Widget): self._networks = networks for n in self._networks: self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT, - button_style=ButtonStyle.TRANSPARENT_WHITE) + button_style=ButtonStyle.TRANSPARENT_WHITE_TEXT) self._networks_buttons[n.ssid].set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid()) self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI, font_size=45) From 9493f2a0eb889428d5e17967ca7ce919ab466848 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 30 Sep 2025 23:48:04 -0700 Subject: [PATCH 076/341] raylib: remove functional confirmation dialog (#36231) * rm * yess * clean up --- selfdrive/ui/layouts/settings/device.py | 26 ++++------ selfdrive/ui/layouts/settings/software.py | 8 ++- selfdrive/ui/widgets/ssh_key.py | 2 +- system/ui/widgets/confirm_dialog.py | 60 ++--------------------- 4 files changed, 19 insertions(+), 77 deletions(-) diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index b34042d43e..892853ca3b 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -10,7 +10,7 @@ from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import Widget, DialogResult -from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog, alert_dialog +from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog from openpilot.system.ui.widgets.html_render import HtmlRenderer from openpilot.system.ui.widgets.list_view import text_item, button_item, dual_button_item from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog @@ -92,13 +92,11 @@ class DeviceLayout(Widget): def _reset_calibration_prompt(self): if ui_state.engaged: - gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Reset Calibration")) + gui_app.set_modal_overlay(alert_dialog("Disengage to Reset Calibration")) return - gui_app.set_modal_overlay( - lambda: confirm_dialog("Are you sure you want to reset calibration?", "Reset"), - callback=self._reset_calibration, - ) + dialog = ConfirmDialog("Are you sure you want to reset calibration?", "Reset") + gui_app.set_modal_overlay(dialog, callback=self._reset_calibration) def _reset_calibration(self, result: int): if ui_state.engaged or result != DialogResult.CONFIRM: @@ -113,13 +111,11 @@ class DeviceLayout(Widget): def _reboot_prompt(self): if ui_state.engaged: - gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Reboot")) + gui_app.set_modal_overlay(alert_dialog("Disengage to Reboot")) return - gui_app.set_modal_overlay( - lambda: confirm_dialog("Are you sure you want to reboot?", "Reboot"), - callback=self._perform_reboot, - ) + dialog = ConfirmDialog("Are you sure you want to reboot?", "Reboot") + gui_app.set_modal_overlay(dialog, callback=self._perform_reboot) def _perform_reboot(self, result: int): if not ui_state.engaged and result == DialogResult.CONFIRM: @@ -127,13 +123,11 @@ class DeviceLayout(Widget): def _power_off_prompt(self): if ui_state.engaged: - gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Power Off")) + gui_app.set_modal_overlay(alert_dialog("Disengage to Power Off")) return - gui_app.set_modal_overlay( - lambda: confirm_dialog("Are you sure you want to power off?", "Power Off"), - callback=self._perform_power_off, - ) + dialog = ConfirmDialog("Are you sure you want to power off?", "Power Off") + gui_app.set_modal_overlay(dialog, callback=self._perform_power_off) def _perform_power_off(self, result: int): if not ui_state.engaged and result == DialogResult.CONFIRM: diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 4361725a1b..0349070010 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -1,7 +1,7 @@ from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import Widget, DialogResult -from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog +from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.list_view import button_item, text_item from openpilot.system.ui.widgets.scroller import Scroller @@ -36,7 +36,5 @@ class SoftwareLayout(Widget): if result == DialogResult.CONFIRM: self._params.put_bool("DoUninstall", True) - gui_app.set_modal_overlay( - lambda: confirm_dialog("Are you sure you want to uninstall?", "Uninstall"), - callback=handle_uninstall_confirmation, - ) + dialog = ConfirmDialog("Are you sure you want to uninstall?", "Uninstall") + gui_app.set_modal_overlay(dialog, callback=handle_uninstall_confirmation) diff --git a/selfdrive/ui/widgets/ssh_key.py b/selfdrive/ui/widgets/ssh_key.py index 2fc13d4c96..5611be8f70 100644 --- a/selfdrive/ui/widgets/ssh_key.py +++ b/selfdrive/ui/widgets/ssh_key.py @@ -49,7 +49,7 @@ class SshKeyAction(ItemAction): # Show error dialog if there's an error if self._error_message: message = copy.copy(self._error_message) - gui_app.set_modal_overlay(lambda: alert_dialog(message)) + gui_app.set_modal_overlay(alert_dialog(message)) self._username = "" self._error_message = "" diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index 1021b5452b..b1dc54bf7a 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -1,8 +1,8 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import DialogResult -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle, Button -from openpilot.system.ui.widgets.label import gui_text_box, Label +from openpilot.system.ui.widgets.button import ButtonStyle, Button +from openpilot.system.ui.widgets.label import Label from openpilot.system.ui.widgets import Widget DIALOG_WIDTH = 1520 @@ -12,6 +12,7 @@ MARGIN = 50 TEXT_AREA_HEIGHT_REDUCTION = 200 BACKGROUND_COLOR = rl.Color(27, 27, 27, 255) + class ConfirmDialog(Widget): def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel"): super().__init__() @@ -66,57 +67,6 @@ class ConfirmDialog(Widget): return self._dialog_result -def confirm_dialog(message: str, confirm_text: str, cancel_text: str = "Cancel") -> DialogResult: - dialog_x = (gui_app.width - DIALOG_WIDTH) / 2 - dialog_y = (gui_app.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_rec(dialog_rect, BACKGROUND_COLOR) - - # Draw the message in the dialog, centered - text_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y, dialog_rect.width - 2 * MARGIN, dialog_rect.height - TEXT_AREA_HEIGHT_REDUCTION) - gui_text_box( - text_rect, - message, - font_size=70, - alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, - alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, - font_weight=FontWeight.BOLD, - ) - - # Initialize result; -1 means no action taken yet - result = DialogResult.NO_ACTION - - # Check for keyboard input for accessibility - if rl.is_key_pressed(rl.KeyboardKey.KEY_ENTER): - result = DialogResult.CONFIRM - elif rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE): - result = DialogResult.CANCEL - - # Check for button clicks - if cancel_text: - if gui_button(yes_button, confirm_text, button_style=ButtonStyle.PRIMARY): - result = DialogResult.CONFIRM - if gui_button(no_button, cancel_text): - result = DialogResult.CANCEL - else: - centered_button_x = dialog_rect.x + (dialog_rect.width - button_width) / 2 - centered_yes_button = rl.Rectangle(centered_button_x, button_y, button_width, BUTTON_HEIGHT) - if gui_button(centered_yes_button, confirm_text, button_style=ButtonStyle.PRIMARY): - result = DialogResult.CONFIRM - - return result - - -def alert_dialog(message: str, button_text: str = "OK") -> DialogResult: - return confirm_dialog(message, button_text, cancel_text="") +def alert_dialog(message: str, button_text: str = "OK"): + return ConfirmDialog(message, button_text, cancel_text="") From eadab06f591889499d8ceb85da5f376e51b559cc Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 1 Oct 2025 00:32:09 -0700 Subject: [PATCH 077/341] raylib: remove gui_button (#36229) * vibing can be good * and listview * rm that * html render * text.py * ssh keys * updater w/ Auto * wow gpt5 actually is better * well this is better * huh wifi still doesn't work * lfg * lint * manager waits for exit * wait a minute this changes nothing * this will work * whoops * clean up html * actually useless * clean up option * typing * bump --- selfdrive/ui/layouts/settings/device.py | 2 +- selfdrive/ui/widgets/ssh_key.py | 26 ++--- system/ui/text.py | 19 ++-- system/ui/updater.py | 30 +++--- system/ui/widgets/button.py | 120 ++++-------------------- system/ui/widgets/html_render.py | 10 +- system/ui/widgets/list_view.py | 26 ++--- system/ui/widgets/option_dialog.py | 52 +++++----- 8 files changed, 101 insertions(+), 184 deletions(-) diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 892853ca3b..cb705d46f8 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -141,7 +141,7 @@ class DeviceLayout(Widget): def _on_regulatory(self): if not self._fcc_dialog: self._fcc_dialog = HtmlRenderer(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html")) - gui_app.set_modal_overlay(self._fcc_dialog, callback=lambda result: setattr(self, '_fcc_dialog', None)) + gui_app.set_modal_overlay(self._fcc_dialog) def _on_review_training_guide(self): if not self._training_guide: diff --git a/selfdrive/ui/widgets/ssh_key.py b/selfdrive/ui/widgets/ssh_key.py index 5611be8f70..370141bd64 100644 --- a/selfdrive/ui/widgets/ssh_key.py +++ b/selfdrive/ui/widgets/ssh_key.py @@ -2,13 +2,14 @@ import pyray as rl import requests import threading import copy +from collections.abc import Callable from enum import Enum from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import DialogResult -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle +from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.confirm_dialog import alert_dialog from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.list_view import ( @@ -38,9 +39,15 @@ class SshKeyAction(ItemAction): self._params = Params() self._error_message: str = "" self._text_font = gui_app.font(FontWeight.MEDIUM) + self._button = Button("", click_callback=self._handle_button_click, button_style=ButtonStyle.LIST_ACTION, + border_radius=BUTTON_BORDER_RADIUS, font_size=BUTTON_FONT_SIZE) self._refresh_state() + def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: + super().set_touch_valid_callback(touch_callback) + self._button.set_touch_valid_callback(touch_callback) + def _refresh_state(self): self._username = self._params.get("GithubUsername") self._state = SshKeyActionState.REMOVE if self._params.get("GithubSshKeys") else SshKeyActionState.ADD @@ -66,18 +73,11 @@ class SshKeyAction(ItemAction): ) # Draw button - if gui_button( - rl.Rectangle( - rect.x + rect.width - BUTTON_WIDTH, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT - ), - self._state.value, - is_enabled=self._state != SshKeyActionState.LOADING, - border_radius=BUTTON_BORDER_RADIUS, - font_size=BUTTON_FONT_SIZE, - button_style=ButtonStyle.LIST_ACTION, - ): - self._handle_button_click() - return True + button_rect = rl.Rectangle(rect.x + rect.width - BUTTON_WIDTH, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT) + self._button.set_rect(button_rect) + self._button.set_text(self._state.value) + self._button.set_enabled(self._state != SshKeyActionState.LOADING) + self._button.render(button_rect) return False def _handle_button_click(self): diff --git a/system/ui/text.py b/system/ui/text.py index 3db930eb26..707b30983b 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -7,7 +7,7 @@ from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle +from openpilot.system.ui.widgets.button import Button, ButtonStyle MARGIN = 50 SPACING = 40 @@ -56,6 +56,15 @@ class TextWindow(Widget): self._scroll_panel = GuiScrollPanel() self._scroll_panel._offset_filter_y.x = -max(self._content_rect.height - self._textarea_rect.height, 0) + button_text = "Exit" if PC else "Reboot" + self._button = Button(button_text, click_callback=self._on_button_clicked, button_style=ButtonStyle.TRANSPARENT_WHITE_BORDER) + + @staticmethod + def _on_button_clicked(): + gui_app.request_close() + if not PC: + HARDWARE.reboot() + def _render(self, rect: rl.Rectangle): scroll = self._scroll_panel.update(self._textarea_rect, self._content_rect) rl.begin_scissor_mode(int(self._textarea_rect.x), int(self._textarea_rect.y), int(self._textarea_rect.width), int(self._textarea_rect.height)) @@ -67,13 +76,7 @@ class TextWindow(Widget): rl.end_scissor_mode() button_bounds = rl.Rectangle(rect.width - MARGIN - BUTTON_SIZE.x - SPACING, rect.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) - ret = gui_button(button_bounds, "Exit" if PC else "Reboot", button_style=ButtonStyle.TRANSPARENT) - if ret: - if PC: - gui_app.request_close() - else: - HARDWARE.reboot() - return ret + self._button.render(button_bounds) if __name__ == "__main__": diff --git a/system/ui/updater.py b/system/ui/updater.py index b3cdc82cf5..48903fa5bd 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -9,7 +9,7 @@ from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.wifi_manager import WifiManager from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle +from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.label import gui_text_box, gui_label from openpilot.system.ui.widgets.network import WifiManagerUI @@ -45,8 +45,17 @@ class Updater(Widget): self.update_thread = None self.wifi_manager_ui = WifiManagerUI(WifiManager()) + # Buttons + self._wifi_button = Button("Connect to Wi-Fi", click_callback=lambda: self.set_current_screen(Screen.WIFI)) + self._install_button = Button("Install", click_callback=self.install_update, button_style=ButtonStyle.PRIMARY) + self._back_button = Button("Back", click_callback=lambda: self.set_current_screen(Screen.PROMPT)) + self._reboot_button = Button("Reboot", click_callback=lambda: HARDWARE.reboot()) + + def set_current_screen(self, screen: Screen): + self.current_screen = screen + def install_update(self): - self.current_screen = Screen.PROGRESS + self.set_current_screen(Screen.PROGRESS) self.progress_value = 0 self.progress_text = "Downloading..." self.show_reboot_button = False @@ -96,15 +105,11 @@ class Updater(Widget): # WiFi button wifi_button_rect = rl.Rectangle(MARGIN, button_y, button_width, BUTTON_HEIGHT) - if gui_button(wifi_button_rect, "Connect to Wi-Fi"): - self.current_screen = Screen.WIFI - return # Return to avoid processing other buttons after screen change + self._wifi_button.render(wifi_button_rect) # Install button install_button_rect = rl.Rectangle(MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT) - if gui_button(install_button_rect, "Install", button_style=ButtonStyle.PRIMARY): - self.install_update() - return # Return to avoid further processing after action + self._install_button.render(install_button_rect) def render_wifi_screen(self, rect: rl.Rectangle): # Draw the Wi-Fi manager UI @@ -112,9 +117,7 @@ class Updater(Widget): self.wifi_manager_ui.render(wifi_rect) back_button_rect = rl.Rectangle(MARGIN, rect.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) - if gui_button(back_button_rect, "Back"): - self.current_screen = Screen.PROMPT - return # Return to avoid processing other interactions after screen change + self._back_button.render(back_button_rect) def render_progress_screen(self, rect: rl.Rectangle): title_rect = rl.Rectangle(MARGIN + 100, 330, rect.width - MARGIN * 2 - 200, 100) @@ -133,10 +136,7 @@ class Updater(Widget): # Show reboot button if needed if self.show_reboot_button: reboot_rect = rl.Rectangle(MARGIN + 100, rect.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) - if gui_button(reboot_rect, "Reboot"): - # Return True to signal main loop to exit before rebooting - HARDWARE.reboot() - return + self._reboot_button.render(reboot_rect) def _render(self, rect: rl.Rectangle): if self.current_screen == Screen.PROMPT: diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 32d63952a2..141f682db0 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -3,8 +3,7 @@ from enum import IntEnum import pyray as rl -from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos -from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.application import FontWeight, MousePos from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.label import TextAlignment, Label @@ -14,7 +13,8 @@ class ButtonStyle(IntEnum): PRIMARY = 1 # For main actions DANGER = 2 # For critical actions, like reboot or delete TRANSPARENT = 3 # For buttons with transparent background and border - TRANSPARENT_WHITE_TEXT = 3 # For buttons with transparent background and border and white text + TRANSPARENT_WHITE_TEXT = 9 # For buttons with transparent background and border and white text + TRANSPARENT_WHITE_BORDER = 10 # For buttons with transparent background and white border and text ACTION = 4 LIST_ACTION = 5 # For list items with action buttons NO_EFFECT = 6 @@ -32,6 +32,7 @@ BUTTON_TEXT_COLOR = { ButtonStyle.DANGER: rl.Color(228, 228, 228, 255), ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.WHITE, + ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.Color(228, 228, 228, 255), ButtonStyle.ACTION: rl.BLACK, ButtonStyle.LIST_ACTION: rl.Color(228, 228, 228, 255), ButtonStyle.NO_EFFECT: rl.Color(228, 228, 228, 255), @@ -49,6 +50,7 @@ BUTTON_BACKGROUND_COLORS = { ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, + ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.BLACK, ButtonStyle.ACTION: rl.Color(189, 189, 189, 255), ButtonStyle.LIST_ACTION: rl.Color(57, 57, 57, 255), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), @@ -62,6 +64,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, + ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.BLANK, ButtonStyle.ACTION: rl.Color(130, 130, 130, 255), ButtonStyle.LIST_ACTION: rl.Color(74, 74, 74, 74), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), @@ -73,104 +76,6 @@ BUTTON_DISABLED_BACKGROUND_COLORS = { ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, } -_pressed_buttons: set[str] = set() # Track mouse press state globally - - -# TODO: This should be a Widget class - -def gui_button( - rect: rl.Rectangle, - text: str, - font_size: int = DEFAULT_BUTTON_FONT_SIZE, - font_weight: FontWeight = FontWeight.MEDIUM, - button_style: ButtonStyle = ButtonStyle.NORMAL, - is_enabled: bool = True, - border_radius: int = 10, # Corner rounding in pixels - text_alignment: TextAlignment = TextAlignment.CENTER, - text_padding: int = 20, # Padding for left/right alignment - icon=None, -) -> int: - button_id = f"{rect.x}_{rect.y}_{rect.width}_{rect.height}" - result = 0 - - if button_style in (ButtonStyle.PRIMARY, ButtonStyle.DANGER) and not is_enabled: - button_style = ButtonStyle.NORMAL - - if button_style == ButtonStyle.ACTION and font_size == DEFAULT_BUTTON_FONT_SIZE: - font_size = ACTION_BUTTON_FONT_SIZE - - # Set background color based on button type - bg_color = BUTTON_BACKGROUND_COLORS[button_style] - mouse_over = is_enabled and rl.check_collision_point_rec(rl.get_mouse_position(), rect) - is_pressed = button_id in _pressed_buttons - - if mouse_over: - if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - # Only this button enters pressed state - _pressed_buttons.add(button_id) - is_pressed = True - - # Use pressed color when mouse is down over this button - if is_pressed and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): - bg_color = BUTTON_PRESSED_BACKGROUND_COLORS[button_style] - - # Handle button click - if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and is_pressed: - result = 1 - _pressed_buttons.remove(button_id) - - # Clean up pressed state if mouse is released anywhere - if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and button_id in _pressed_buttons: - _pressed_buttons.remove(button_id) - - # Draw the button with rounded corners - roundness = border_radius / (min(rect.width, rect.height) / 2) - if button_style != ButtonStyle.TRANSPARENT: - rl.draw_rectangle_rounded(rect, roundness, 20, bg_color) - else: - rl.draw_rectangle_rounded(rect, roundness, 20, rl.BLACK) - rl.draw_rectangle_rounded_lines_ex(rect, roundness, 20, 2, rl.WHITE) - - # Handle icon and text positioning - font = gui_app.font(font_weight) - text_size = measure_text_cached(font, text, font_size) - text_pos = rl.Vector2(0, rect.y + (rect.height - text_size.y) // 2) # Vertical centering - - # Draw icon if provided - if icon: - icon_y = rect.y + (rect.height - icon.height) / 2 - if text: - if text_alignment == TextAlignment.LEFT: - icon_x = rect.x + text_padding - text_pos.x = icon_x + icon.width + ICON_PADDING - elif text_alignment == TextAlignment.CENTER: - total_width = icon.width + ICON_PADDING + text_size.x - icon_x = rect.x + (rect.width - total_width) / 2 - text_pos.x = icon_x + icon.width + ICON_PADDING - else: # RIGHT - text_pos.x = rect.x + rect.width - text_size.x - text_padding - icon_x = text_pos.x - ICON_PADDING - icon.width - else: - # Center icon when no text - icon_x = rect.x + (rect.width - icon.width) / 2 - - rl.draw_texture_v(icon, rl.Vector2(icon_x, icon_y), rl.WHITE if is_enabled else rl.Color(255, 255, 255, 100)) - else: - # No icon, position text normally - if text_alignment == TextAlignment.LEFT: - text_pos.x = rect.x + text_padding - elif text_alignment == TextAlignment.CENTER: - text_pos.x = rect.x + (rect.width - text_size.x) // 2 - elif text_alignment == TextAlignment.RIGHT: - text_pos.x = rect.x + rect.width - text_size.x - text_padding - - # Draw the button text if any - if text: - color = BUTTON_TEXT_COLOR[button_style] if is_enabled else BUTTON_DISABLED_TEXT_COLORS.get(button_style, rl.Color(228, 228, 228, 51)) - rl.draw_text_ex(font, text, text_pos, font_size, 0, color) - - return result - class Button(Widget): def __init__(self, @@ -182,7 +87,7 @@ class Button(Widget): border_radius: int = 10, text_alignment: TextAlignment = TextAlignment.CENTER, text_padding: int = 20, - icon = None, + icon=None, multi_touch: bool = False, ): @@ -200,6 +105,11 @@ class Button(Widget): def set_text(self, text): self._label.set_text(text) + def set_button_style(self, button_style: ButtonStyle): + self._button_style = button_style + self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] + self._label.set_text_color(BUTTON_TEXT_COLOR[self._button_style]) + def _update_state(self): if self.enabled: self._label.set_text_color(BUTTON_TEXT_COLOR[self._button_style]) @@ -213,7 +123,11 @@ class Button(Widget): def _render(self, _): roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2) - rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color) + if self._button_style == ButtonStyle.TRANSPARENT_WHITE_BORDER: + rl.draw_rectangle_rounded(self._rect, roundness, 10, rl.BLACK) + rl.draw_rectangle_rounded_lines_ex(self._rect, roundness, 10, 2, rl.WHITE) + else: + rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color) self._label.render(self._rect) diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index bb7eeb7c5c..b870227854 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -6,8 +6,8 @@ from typing import Any from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text -from openpilot.system.ui.widgets import Widget, DialogResult -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle +from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.widgets.button import Button, ButtonStyle class ElementType(Enum): @@ -40,6 +40,7 @@ class HtmlRenderer(Widget): self._normal_font = gui_app.font(FontWeight.NORMAL) self._bold_font = gui_app.font(FontWeight.BOLD) self._scroll_panel = GuiScrollPanel() + self._ok_button = Button("OK", click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY) self.styles: dict[ElementType, dict[str, Any]] = { ElementType.H1: {"size": 68, "weight": FontWeight.BOLD, "color": rl.BLACK, "margin_top": 20, "margin_bottom": 16}, @@ -126,10 +127,9 @@ class HtmlRenderer(Widget): button_x = content_rect.x + content_rect.width - button_width button_y = content_rect.y + content_rect.height - button_height button_rect = rl.Rectangle(button_x, button_y, button_width, button_height) - if gui_button(button_rect, "OK", button_style=ButtonStyle.PRIMARY) == 1: - return DialogResult.CONFIRM + self._ok_button.render(button_rect) - return DialogResult.NO_ACTION + return -1 def _render_content(self, rect: rl.Rectangle, scroll_offset: float = 0) -> float: current_y = rect.y + scroll_offset diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index 648ce47a6f..f9cdf7f2b4 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -6,7 +6,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import Button, gui_button, ButtonStyle +from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.toggle import Toggle, WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT ITEM_BASE_WIDTH = 600 @@ -149,9 +149,16 @@ class DualButtonAction(ItemAction): right_callback: Callable = None, enabled: bool | Callable[[], bool] = True): super().__init__(width=0, enabled=enabled) # Width 0 means use full width self.left_text, self.right_text = left_text, right_text - self.left_callback, self.right_callback = left_callback, right_callback - def _render(self, rect: rl.Rectangle) -> bool: + self.left_button = Button(left_text, click_callback=left_callback, button_style=ButtonStyle.LIST_ACTION) + self.right_button = Button(right_text, click_callback=right_callback, button_style=ButtonStyle.DANGER) + + def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: + super().set_touch_valid_callback(touch_callback) + self.left_button.set_touch_valid_callback(touch_callback) + self.right_button.set_touch_valid_callback(touch_callback) + + def _render(self, rect: rl.Rectangle): button_spacing = 30 button_height = 120 button_width = (rect.width - button_spacing) / 2 @@ -160,16 +167,9 @@ class DualButtonAction(ItemAction): left_rect = rl.Rectangle(rect.x, button_y, button_width, button_height) right_rect = rl.Rectangle(rect.x + button_width + button_spacing, button_y, button_width, button_height) - left_clicked = gui_button(left_rect, self.left_text, button_style=ButtonStyle.LIST_ACTION) == 1 - right_clicked = gui_button(right_rect, self.right_text, button_style=ButtonStyle.DANGER) == 1 - - if left_clicked and self.left_callback: - self.left_callback() - return True - if right_clicked and self.right_callback: - self.right_callback() - return True - return False + # Render buttons + self.left_button.render(left_rect) + self.right_button.render(right_rect) class MultipleButtonAction(ItemAction): diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index cbab024f09..604cd59fd0 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -1,9 +1,9 @@ import pyray as rl -from openpilot.system.ui.lib.application import FontWeight -from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel +from openpilot.system.ui.lib.application import FontWeight, gui_app from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle, TextAlignment +from openpilot.system.ui.widgets.button import Button, ButtonStyle, TextAlignment from openpilot.system.ui.widgets.label import gui_label +from openpilot.system.ui.widgets.scroller import Scroller # Constants MARGIN = 50 @@ -22,7 +22,17 @@ class MultiOptionDialog(Widget): self.options = options self.current = current self.selection = current - self.scroll = GuiScrollPanel() + + # Create scroller with option buttons + self.option_buttons = [Button(option, click_callback=lambda opt=option: self._on_option_clicked(opt), + text_alignment=TextAlignment.LEFT, button_style=ButtonStyle.NORMAL) for option in options] + self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING) + + self.cancel_button = Button("Cancel", click_callback=lambda: gui_app.set_modal_overlay(None)) + self.select_button = Button("Select", click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY) + + def _on_option_clicked(self, option): + self.selection = option def _render(self, rect): dialog_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - 2 * MARGIN, rect.height - 2 * MARGIN) @@ -36,36 +46,26 @@ class MultiOptionDialog(Widget): # Options area options_y = content_rect.y + TITLE_FONT_SIZE + ITEM_SPACING options_h = content_rect.height - TITLE_FONT_SIZE - BUTTON_HEIGHT - 2 * ITEM_SPACING - view_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, options_h) - content_h = len(self.options) * (ITEM_HEIGHT + LIST_ITEM_SPACING) - list_content_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, content_h) + options_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, options_h) - # Scroll and render options - offset = self.scroll.update(view_rect, list_content_rect) - valid_click = self.scroll.is_touch_valid() and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) - - rl.begin_scissor_mode(int(view_rect.x), int(options_y), int(view_rect.width), int(options_h)) + # Update button styles and set width based on selection for i, option in enumerate(self.options): - item_y = options_y + i * (ITEM_HEIGHT + LIST_ITEM_SPACING) + offset - item_rect = rl.Rectangle(view_rect.x, item_y, view_rect.width, ITEM_HEIGHT) + selected = option == self.selection + button = self.option_buttons[i] + button.set_button_style(ButtonStyle.PRIMARY if selected else ButtonStyle.NORMAL) + button.set_rect(rl.Rectangle(0, 0, options_rect.width, ITEM_HEIGHT)) - if rl.check_collision_recs(item_rect, view_rect): - selected = option == self.selection - style = ButtonStyle.PRIMARY if selected else ButtonStyle.NORMAL - - if gui_button(item_rect, option, button_style=style, text_alignment=TextAlignment.LEFT) and valid_click: - self.selection = option - rl.end_scissor_mode() + self.scroller.render(options_rect) # Buttons button_y = content_rect.y + content_rect.height - BUTTON_HEIGHT button_w = (content_rect.width - BUTTON_SPACING) / 2 - if gui_button(rl.Rectangle(content_rect.x, button_y, button_w, BUTTON_HEIGHT), "Cancel"): - return 0 + cancel_rect = rl.Rectangle(content_rect.x, button_y, button_w, BUTTON_HEIGHT) + self.cancel_button.render(cancel_rect) - if gui_button(rl.Rectangle(content_rect.x + button_w + BUTTON_SPACING, button_y, button_w, BUTTON_HEIGHT), - "Select", is_enabled=self.selection != self.current, button_style=ButtonStyle.PRIMARY): - return 1 + select_rect = rl.Rectangle(content_rect.x + button_w + BUTTON_SPACING, button_y, button_w, BUTTON_HEIGHT) + self.select_button.set_enabled(self.selection != self.current) + self.select_button.render(select_rect) return -1 From 29a6f0504a7afce2f039a51f3da49ed4cbfffb7f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 1 Oct 2025 00:46:51 -0700 Subject: [PATCH 078/341] raylib: fix WiFi in setup and updater (#36232) move back to more base class --- system/ui/widgets/network.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 9eaac6030b..5dfdff9b96 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -77,9 +77,6 @@ class NetworkUI(Widget): self._nav_button = NavButton("Advanced") self._nav_button.set_click_callback(self._cycle_panel) - def _update_state(self): - self._wifi_manager.process_callbacks() - def show_event(self): self._set_current_panel(PanelType.WIFI) self._wifi_panel.show_event() @@ -305,6 +302,9 @@ class WifiManagerUI(Widget): for icon in STRENGTH_ICONS + ["icons/checkmark.png", "icons/circled_slash.png", "icons/lock_closed.png"]: gui_app.texture(icon, ICON_SIZE, ICON_SIZE) + def _update_state(self): + self._wifi_manager.process_callbacks() + def _render(self, rect: rl.Rectangle): if not self._networks: gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) From b8ae62a0b1b93c134058421fe1ed347db2eff55d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 1 Oct 2025 01:02:35 -0700 Subject: [PATCH 079/341] raylib scroll panel: check bounds (#36233) check in bounds rect for scroll panel!! --- system/ui/lib/scroll_panel.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index 5dacae8210..d0d59df1ff 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -77,20 +77,21 @@ class GuiScrollPanel: def _handle_mouse_event(self, mouse_event: MouseEvent, bounds: rl.Rectangle, content: rl.Rectangle): if self._scroll_state == ScrollState.IDLE: - if mouse_event.left_pressed: - self._start_mouse_y = mouse_event.pos.y - # Interrupt scrolling with new drag - # TODO: stop scrolling with any tap, need to fix is_touch_valid - if abs(self._velocity_filter_y.x) > MIN_VELOCITY_FOR_CLICKING: - self._scroll_state = ScrollState.DRAGGING_CONTENT - # Start velocity at initial measurement for more immediate response - self._velocity_filter_y.initialized = False + if rl.check_collision_point_rec(mouse_event.pos, bounds): + if mouse_event.left_pressed: + self._start_mouse_y = mouse_event.pos.y + # Interrupt scrolling with new drag + # TODO: stop scrolling with any tap, need to fix is_touch_valid + if abs(self._velocity_filter_y.x) > MIN_VELOCITY_FOR_CLICKING: + self._scroll_state = ScrollState.DRAGGING_CONTENT + # Start velocity at initial measurement for more immediate response + self._velocity_filter_y.initialized = False - if mouse_event.left_down: - if abs(mouse_event.pos.y - self._start_mouse_y) > DRAG_THRESHOLD: - self._scroll_state = ScrollState.DRAGGING_CONTENT - # Start velocity at initial measurement for more immediate response - self._velocity_filter_y.initialized = False + if mouse_event.left_down: + if abs(mouse_event.pos.y - self._start_mouse_y) > DRAG_THRESHOLD: + self._scroll_state = ScrollState.DRAGGING_CONTENT + # Start velocity at initial measurement for more immediate response + self._velocity_filter_y.initialized = False elif self._scroll_state == ScrollState.DRAGGING_CONTENT: if mouse_event.left_released: From 49570c11c6fc565b0ca89552a07ed16543233a0d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 2 Oct 2025 02:04:09 -0700 Subject: [PATCH 080/341] Remove animation from networking --- system/ui/widgets/network.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 5dfdff9b96..35ceef1ff4 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -3,7 +3,6 @@ from functools import partial from typing import cast import pyray as rl -from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel @@ -50,16 +49,6 @@ class NavButton(Widget): super().__init__() self.text = text self.set_rect(rl.Rectangle(0, 0, 400, 100)) - self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) - self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False) - - def set_position(self, x: float, y: float) -> None: - x = self._x_pos_filter.update(x) - y = self._y_pos_filter.update(y) - changed = (self._rect.x != x or self._rect.y != y) - self._rect.x, self._rect.y = x, y - if changed: - self._update_layout_rects() def _render(self, _): color = rl.Color(74, 74, 74, 255) if self.is_pressed else rl.Color(57, 57, 57, 255) From 3fd352a7eff8b5b95e423da717f56987f0252e0f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 2 Oct 2025 03:57:10 -0700 Subject: [PATCH 081/341] raylib: updater UI (#36235) * auto attempt * gpt5 * Revert "gpt5" This reverts commit 556d6d9ee4d53aca0f4612023db6cfb2bed7ce29. * clean up * fixes * use raylib * fixes * debug * test update * more * rm * add value to button like qt * bump * bump * fixes * bump * fix * bump * clean up * time ago like qt rm * bump * clean up * updated can fail to respond on boot leading to stuck state * fix color fix * bump * bump * add back * test update * no unknown just '' * ffix --- selfdrive/ui/layouts/settings/software.py | 141 ++++++++++++++++++++-- system/manager/process_config.py | 4 +- system/ui/widgets/list_view.py | 26 +++- 3 files changed, 158 insertions(+), 13 deletions(-) diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 0349070010..7440512a62 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -1,24 +1,69 @@ -from openpilot.common.params import Params +import os +import time +import datetime +from openpilot.common.time_helpers import system_time_valid +from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog -from openpilot.system.ui.widgets.list_view import button_item, text_item +from openpilot.system.ui.widgets.list_view import button_item, text_item, ListItem from openpilot.system.ui.widgets.scroller import Scroller +# TODO: remove this. updater fails to respond on startup if time is not correct +UPDATED_TIMEOUT = 10 # seconds to wait for updated to respond + + +def time_ago(date: datetime.datetime | None) -> str: + if not date: + return "never" + + if not system_time_valid(): + return date.strftime("%a %b %d %Y") + + now = datetime.datetime.now(datetime.UTC) + if date.tzinfo is None: + date = date.replace(tzinfo=datetime.UTC) + + diff_seconds = int((now - date).total_seconds()) + if diff_seconds < 60: + return "now" + if diff_seconds < 3600: + m = diff_seconds // 60 + return f"{m} minute{'s' if m != 1 else ''} ago" + if diff_seconds < 86400: + h = diff_seconds // 3600 + return f"{h} hour{'s' if h != 1 else ''} ago" + if diff_seconds < 604800: + d = diff_seconds // 86400 + return f"{d} day{'s' if d != 1 else ''} ago" + return date.strftime("%a %b %d %Y") + class SoftwareLayout(Widget): def __init__(self): super().__init__() - self._params = Params() + self._onroad_label = ListItem(title="Updates are only downloaded while the car is off.") + self._version_item = text_item("Current Version", ui_state.params.get("UpdaterCurrentDescription") or "") + self._download_btn = button_item("Download", "CHECK", callback=self._on_download_update) + + # Install button is initially hidden + self._install_btn = button_item("Install Update", "INSTALL", callback=self._on_install_update) + self._install_btn.set_visible(False) + + # Track waiting-for-updater transition to avoid brief re-enable while still idle + self._waiting_for_updater = False + self._waiting_start_ts: float = 0.0 + items = self._init_items() self._scroller = Scroller(items, line_separator=True, spacing=0) def _init_items(self): items = [ - text_item("Current Version", ""), - button_item("Download", "CHECK", callback=self._on_download_update), - button_item("Install Update", "INSTALL", callback=self._on_install_update), + self._onroad_label, + self._version_item, + self._download_btn, + self._install_btn, button_item("Target Branch", "SELECT", callback=self._on_select_branch), button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall), ] @@ -27,14 +72,90 @@ class SoftwareLayout(Widget): def _render(self, rect): self._scroller.render(rect) - def _on_download_update(self): pass - def _on_install_update(self): pass - def _on_select_branch(self): pass + def _update_state(self): + # Show/hide onroad warning + self._onroad_label.set_visible(ui_state.is_onroad()) + + # Update current version and release notes + current_desc = ui_state.params.get("UpdaterCurrentDescription") or "" + current_release_notes = (ui_state.params.get("UpdaterCurrentReleaseNotes") or b"").decode("utf-8", "replace") + self._version_item.action_item.set_text(current_desc) + self._version_item.description = current_release_notes + + # Update download button visibility and state + self._download_btn.set_visible(ui_state.is_offroad()) + + updater_state = ui_state.params.get("UpdaterState") or "idle" + failed_count = ui_state.params.get("UpdateFailedCount") or 0 + fetch_available = ui_state.params.get_bool("UpdaterFetchAvailable") + update_available = ui_state.params.get_bool("UpdateAvailable") + + if updater_state != "idle": + # Updater responded + self._waiting_for_updater = False + self._download_btn.action_item.set_enabled(False) + self._download_btn.action_item.set_value(updater_state) + else: + if failed_count > 0: + self._download_btn.action_item.set_value("failed to check for update") + self._download_btn.action_item.set_text("CHECK") + elif fetch_available: + self._download_btn.action_item.set_value("update available") + self._download_btn.action_item.set_text("DOWNLOAD") + else: + last_update = ui_state.params.get("LastUpdateTime") + if last_update: + formatted = time_ago(last_update) + self._download_btn.action_item.set_value(f"up to date, last checked {formatted}") + else: + self._download_btn.action_item.set_value("up to date, last checked never") + self._download_btn.action_item.set_text("CHECK") + + # If we've been waiting too long without a state change, reset state + if self._waiting_for_updater and (time.monotonic() - self._waiting_start_ts > UPDATED_TIMEOUT): + self._waiting_for_updater = False + + # Only enable if we're not waiting for updater to flip out of idle + self._download_btn.action_item.set_enabled(not self._waiting_for_updater) + + # Update install button + self._install_btn.set_visible(ui_state.is_offroad() and update_available) + if update_available: + new_desc = ui_state.params.get("UpdaterNewDescription") or "" + new_release_notes = (ui_state.params.get("UpdaterNewReleaseNotes") or b"").decode("utf-8", "replace") + self._install_btn.action_item.set_text("INSTALL") + self._install_btn.action_item.set_value(new_desc) + self._install_btn.description = new_release_notes + # Enable install button for testing (like Qt showEvent) + self._install_btn.action_item.set_enabled(True) + else: + self._install_btn.set_visible(False) + + def _on_download_update(self): + # Check if we should start checking or start downloading + self._download_btn.action_item.set_enabled(False) + if self._download_btn.action_item.text == "CHECK": + # Start checking for updates + self._waiting_for_updater = True + self._waiting_start_ts = time.monotonic() + os.system("pkill -SIGUSR1 -f system.updated.updated") + else: + # Start downloading + self._waiting_for_updater = True + self._waiting_start_ts = time.monotonic() + os.system("pkill -SIGHUP -f system.updated.updated") def _on_uninstall(self): def handle_uninstall_confirmation(result): if result == DialogResult.CONFIRM: - self._params.put_bool("DoUninstall", True) + ui_state.params.put_bool("DoUninstall", True) dialog = ConfirmDialog("Are you sure you want to uninstall?", "Uninstall") gui_app.set_modal_overlay(dialog, callback=handle_uninstall_confirmation) + + def _on_install_update(self): + # Trigger reboot to install update + self._install_btn.action_item.set_enabled(False) + ui_state.params.put_bool("DoReboot", True) + + def _on_select_branch(self): pass diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 22f159e891..55e2812efd 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -80,8 +80,8 @@ procs = [ PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), PythonProcess("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)), - PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), + NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), + PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False), diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index f9cdf7f2b4..c9ccff8210 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -14,6 +14,7 @@ ITEM_BASE_HEIGHT = 170 ITEM_PADDING = 20 ITEM_TEXT_FONT_SIZE = 50 ITEM_TEXT_COLOR = rl.WHITE +ITEM_TEXT_VALUE_COLOR = rl.Color(170, 170, 170, 255) ITEM_DESC_TEXT_COLOR = rl.Color(128, 128, 128, 255) ITEM_DESC_FONT_SIZE = 40 ITEM_DESC_V_OFFSET = 140 @@ -77,7 +78,9 @@ class ButtonAction(ItemAction): def __init__(self, text: str | Callable[[], str], width: int = BUTTON_WIDTH, enabled: bool | Callable[[], bool] = True): super().__init__(width, enabled) self._text_source = text + self._value_source: str | Callable[[], str] | None = None self._pressed = False + self._font = gui_app.font(FontWeight.NORMAL) def pressed(): self._pressed = True @@ -96,16 +99,34 @@ class ButtonAction(ItemAction): super().set_touch_valid_callback(touch_callback) self._button.set_touch_valid_callback(touch_callback) + def set_text(self, text: str | Callable[[], str]): + self._text_source = text + + def set_value(self, value: str | Callable[[], str]): + self._value_source = value + @property def text(self): return _resolve_value(self._text_source, "Error") + @property + def value(self): + return _resolve_value(self._value_source, "") + def _render(self, rect: rl.Rectangle) -> bool: self._button.set_text(self.text) self._button.set_enabled(_resolve_value(self.enabled)) button_rect = rl.Rectangle(rect.x, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT) self._button.render(button_rect) + value_text = self.value + if value_text: + spacing = 20 + text_size = measure_text_cached(self._font, value_text, ITEM_TEXT_FONT_SIZE) + text_x = button_rect.x - spacing - text_size.x + text_y = rect.y + (rect.height - text_size.y) / 2 + rl.draw_text_ex(self._font, value_text, rl.Vector2(text_x, text_y), ITEM_TEXT_FONT_SIZE, 0, ITEM_TEXT_VALUE_COLOR) + # TODO: just use the generic Widget click callbacks everywhere, no returning from render pressed = self._pressed self._pressed = False @@ -139,6 +160,9 @@ class TextAction(ItemAction): rl.draw_text_ex(self._font, current_text, rl.Vector2(text_x, text_y), ITEM_TEXT_FONT_SIZE, 0, self.color) return False + def set_text(self, text: str | Callable[[], str]): + self._text_source = text + def get_width(self) -> int: text_width = measure_text_cached(self._font, self.text, ITEM_TEXT_FONT_SIZE).x return int(text_width + TEXT_PADDING) @@ -382,7 +406,7 @@ def button_item(title: str, button_text: str | Callable[[], str], description: s def text_item(title: str, value: str | Callable[[], str], description: str | Callable[[], str] | None = None, callback: Callable | None = None, enabled: bool | Callable[[], bool] = True) -> ListItem: - action = TextAction(text=value, color=rl.Color(170, 170, 170, 255), enabled=enabled) + action = TextAction(text=value, color=ITEM_TEXT_VALUE_COLOR, enabled=enabled) return ListItem(title=title, description=description, action_item=action, callback=callback) From ec7e3192bbc3e8766ed1775ed224acb3684e109c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 2 Oct 2025 04:00:09 -0700 Subject: [PATCH 082/341] revert that --- system/manager/process_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 55e2812efd..22f159e891 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -80,8 +80,8 @@ procs = [ PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), PythonProcess("sensord", "system.sensord.sensord", only_onroad, enabled=not PC), - NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), - PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, watchdog_max_dt=(5 if not PC else None)), + NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)), + PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False), From cc52f980b35131b2eec95bad3937ca5711853433 Mon Sep 17 00:00:00 2001 From: Armand du Parc Locmaria Date: Thu, 2 Oct 2025 15:31:39 -0700 Subject: [PATCH 083/341] Dockerfile.openpilot uv run scons (#36236) * Dockerfile.openpilot_base use UV_PROJECT_ENVIRONMENT * Revert "Dockerfile.openpilot_base use UV_PROJECT_ENVIRONMENT" This reverts commit 3725e54ce0727077ca4347d24ca38e25d5864d47. * Reapply "Dockerfile.openpilot_base use UV_PROJECT_ENVIRONMENT" This reverts commit 11b04f57acb9c81fcc5a22a6a6d78d666c59ca6c. * use uv run to pick up correct ppath --- Dockerfile.openpilot | 4 +++- Dockerfile.openpilot_base | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile.openpilot b/Dockerfile.openpilot index d85be77121..14c1e64774 100644 --- a/Dockerfile.openpilot +++ b/Dockerfile.openpilot @@ -9,4 +9,6 @@ WORKDIR ${OPENPILOT_PATH} COPY . ${OPENPILOT_PATH}/ -RUN scons --cache-readonly -j$(nproc) +ENV UV_BIN="/home/batman/.local/bin/" +ENV PATH="$UV_BIN:$PATH" +RUN uv run scons --cache-readonly -j$(nproc) diff --git a/Dockerfile.openpilot_base b/Dockerfile.openpilot_base index 44d8d95e95..e3bb03de07 100644 --- a/Dockerfile.openpilot_base +++ b/Dockerfile.openpilot_base @@ -71,8 +71,8 @@ USER $USER COPY --chown=$USER pyproject.toml uv.lock /home/$USER COPY --chown=$USER tools/install_python_dependencies.sh /home/$USER/tools/ -ENV VIRTUAL_ENV=/home/$USER/.venv -ENV PATH="$VIRTUAL_ENV/bin:$PATH" +ENV UV_PROJECT_ENVIRONMENT=/home/$USER/.venv +ENV PATH="$UV_PROJECT_ENVIRONMENT/bin:$PATH" RUN cd /home/$USER && \ tools/install_python_dependencies.sh && \ rm -rf tools/ pyproject.toml uv.lock .cache From 1ee798439ae650e832f1224a17cdfb657cecca23 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 2 Oct 2025 21:09:17 -0700 Subject: [PATCH 084/341] raylib: WiFi fixes (#36239) * proces in AN and WM * clean * ban api * fix * fiix * fix pairing dialog * cleanup * fix multi action button hard to click * fix * fix right margin of multi action * clean up --- pyproject.toml | 6 +++++- selfdrive/ui/widgets/pairing_dialog.py | 30 +++++++++++++++----------- system/ui/lib/application.py | 4 ++-- system/ui/widgets/list_view.py | 30 ++++++++++++-------------- system/ui/widgets/network.py | 2 ++ 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9c8a6148a2..a0d135db65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -262,8 +262,12 @@ lint.flake8-implicit-str-concat.allow-multiline = false "tools".msg = "Use openpilot.tools" "pytest.main".msg = "pytest.main requires special handling that is easy to mess up!" "unittest".msg = "Use pytest" -"pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure" "time.time".msg = "Use time.monotonic" +# raylib banned APIs +"pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure" +"pyray.is_mouse_button_pressed".msg = "This can miss events. Use Widget._handle_mouse_press" +"pyray.is_mouse_button_released".msg = "This can miss events. Use Widget._handle_mouse_release" + [tool.ruff.format] quote-style = "preserve" diff --git a/selfdrive/ui/widgets/pairing_dialog.py b/selfdrive/ui/widgets/pairing_dialog.py index 55d53125d8..7676635e18 100644 --- a/selfdrive/ui/widgets/pairing_dialog.py +++ b/selfdrive/ui/widgets/pairing_dialog.py @@ -13,6 +13,18 @@ from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.selfdrive.ui.ui_state import ui_state +class IconButton(Widget): + def __init__(self, texture: rl.Texture): + super().__init__() + self._texture = texture + + def _render(self, rect: rl.Rectangle): + color = rl.Color(180, 180, 180, 150) if self.is_pressed else rl.WHITE + draw_x = rect.x + (rect.width - self._texture.width) / 2 + draw_y = rect.y + (rect.height - self._texture.height) / 2 + rl.draw_texture(self._texture, int(draw_x), int(draw_y), color) + + class PairingDialog(Widget): """Dialog for device pairing with QR code.""" @@ -23,6 +35,8 @@ class PairingDialog(Widget): self.params = Params() self.qr_texture: rl.Texture | None = None self.last_qr_generation = 0 + self._close_btn = IconButton(gui_app.texture("icons/close.png", 80, 80)) + self._close_btn.set_click_callback(lambda: gui_app.set_modal_overlay(None)) def _get_pairing_url(self) -> str: try: @@ -78,19 +92,9 @@ class PairingDialog(Widget): # Close button close_size = 80 - close_icon = gui_app.texture("icons/close.png", close_size, close_size) - close_rect = rl.Rectangle(content_rect.x, y, close_size, close_size) - - mouse_pos = rl.get_mouse_position() - is_hover = rl.check_collision_point_rec(mouse_pos, close_rect) - is_pressed = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) - is_released = rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) - - color = rl.Color(180, 180, 180, 150) if (is_hover and is_pressed) else rl.WHITE - rl.draw_texture(close_icon, int(content_rect.x), int(y), color) - - if (is_hover and is_released) or rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE): - return 1 + pad = 20 + close_rect = rl.Rectangle(content_rect.x - pad, y - pad, close_size + pad * 2, close_size + pad * 2) + self._close_btn.render(close_rect) y += close_size + 40 diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index a16f0b0bda..6c703a516e 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -108,8 +108,8 @@ class MouseState: ev = MouseEvent( MousePos(x, y), slot, - rl.is_mouse_button_pressed(slot), - rl.is_mouse_button_released(slot), + rl.is_mouse_button_pressed(slot), # noqa: TID251 + rl.is_mouse_button_released(slot), # noqa: TID251 rl.is_mouse_button_down(slot), time.monotonic(), ) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index c9ccff8210..7877325492 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -198,17 +198,16 @@ class DualButtonAction(ItemAction): class MultipleButtonAction(ItemAction): def __init__(self, buttons: list[str], button_width: int, selected_index: int = 0, callback: Callable = None): - super().__init__(width=len(buttons) * (button_width + 20), enabled=True) + super().__init__(width=len(buttons) * button_width + (len(buttons) - 1) * RIGHT_ITEM_PADDING, enabled=True) self.buttons = buttons self.button_width = button_width self.selected_button = selected_index self.callback = callback self._font = gui_app.font(FontWeight.MEDIUM) - def _render(self, rect: rl.Rectangle) -> bool: - spacing = 20 + def _render(self, rect: rl.Rectangle): + spacing = RIGHT_ITEM_PADDING button_y = rect.y + (rect.height - BUTTON_HEIGHT) / 2 - clicked = -1 for i, text in enumerate(self.buttons): button_x = rect.x + i * (self.button_width + spacing) @@ -216,8 +215,7 @@ class MultipleButtonAction(ItemAction): # Check button state mouse_pos = rl.get_mouse_position() - is_hovered = rl.check_collision_point_rec(mouse_pos, button_rect) and self.enabled - is_pressed = is_hovered and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and self.is_pressed + is_pressed = rl.check_collision_point_rec(mouse_pos, button_rect) and self.enabled and self.is_pressed is_selected = i == self.selected_button # Button colors @@ -241,16 +239,16 @@ class MultipleButtonAction(ItemAction): text_color = rl.Color(228, 228, 228, 255) if self.enabled else rl.Color(150, 150, 150, 255) rl.draw_text_ex(self._font, text, rl.Vector2(text_x, text_y), 40, 0, text_color) - # Handle click - if is_hovered and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and self.is_pressed: - clicked = i - - if clicked >= 0: - self.selected_button = clicked - if self.callback: - self.callback(clicked) - return True - return False + def _handle_mouse_release(self, mouse_pos: MousePos): + spacing = RIGHT_ITEM_PADDING + button_y = self._rect.y + (self._rect.height - BUTTON_HEIGHT) / 2 + for i, _text in enumerate(self.buttons): + button_x = self._rect.x + i * (self.button_width + spacing) + button_rect = rl.Rectangle(button_x, button_y, self.button_width, BUTTON_HEIGHT) + if rl.check_collision_point_rec(mouse_pos, button_rect): + self.selected_button = i + if self.callback: + self.callback(i) class ListItem(Widget): diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 35ceef1ff4..8a7bd9f512 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -246,6 +246,8 @@ class AdvancedNetworkSettings(Widget): gui_app.set_modal_overlay(self._keyboard, update_password) def _update_state(self): + self._wifi_manager.process_callbacks() + # If not using prime SIM, show GSM settings and enable IPv4 forwarding show_cell_settings = ui_state.prime_state.get_type() in (PrimeType.NONE, PrimeType.LITE) self._wifi_manager.set_ipv4_forward(show_cell_settings) From 21fd3d03201fc7d9cb857fe068b3e51fb855c80d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 2 Oct 2025 21:21:50 -0700 Subject: [PATCH 085/341] raylib: use extra text in offroad alerts (#36241) * replace properly * test --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- selfdrive/ui/widgets/offroad_alerts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 7fb22e4484..1ac5083fc6 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -69,7 +69,7 @@ def setup_pair_device(click, pm: PubMaster): def setup_offroad_alert(click, pm: PubMaster): - set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99') + set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C') for alert in OFFROAD_ALERTS: set_offroad_alert(alert, True) diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 6ca0ca2c40..7ad96bc9a5 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -190,7 +190,7 @@ class OffroadAlert(AbstractAlert): alert_json = self.params.get(alert_data.key) if alert_json: - text = alert_json.get("text", "").replace("{}", alert_json.get("extra", "")) + text = alert_json.get("text", "").replace("%1", alert_json.get("extra", "")) alert_data.text = text alert_data.visible = bool(text) From 75e52427d1da872f17002cdec3b5e7ce27b7acfc Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 2 Oct 2025 22:53:02 -0700 Subject: [PATCH 086/341] raylib: fix when we show offroad alerts and styles (#36240) * fix how we show alerts * test this too * match border radius * simplifty * keep * back * fix alert spacing * fix alert text padding * cmt * cmt --- selfdrive/ui/layouts/home.py | 21 +++++++----- .../ui/tests/test_ui/raylib_screenshots.py | 1 + selfdrive/ui/widgets/offroad_alerts.py | 33 +++++++++++-------- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 320e7477f1..c33b16fac4 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -41,6 +41,8 @@ class HomeLayout(Widget): self.update_available = False self.alert_count = 0 + self._prev_update_available = False + self._prev_alerts_present = False self.header_rect = rl.Rectangle(0, 0, 0, 0) self.content_rect = rl.Rectangle(0, 0, 0, 0) @@ -185,20 +187,23 @@ class HomeLayout(Widget): def _refresh(self): # TODO: implement _update_state with a timer - self.update_available = self.update_alert.refresh() - self.alert_count = self.offroad_alert.refresh() - self._update_state_priority(self.update_available, self.alert_count > 0) - - def _update_state_priority(self, update_available: bool, alerts_present: bool): - current_state = self.current_state + update_available = self.update_alert.refresh() + alert_count = self.offroad_alert.refresh() + alerts_present = alert_count > 0 + # Show panels on transition from no alert/update to any alerts/update if not update_available and not alerts_present: self.current_state = HomeLayoutState.HOME - elif update_available and (current_state == HomeLayoutState.HOME or (not alerts_present and current_state == HomeLayoutState.ALERTS)): + elif update_available and ((not self._prev_update_available) or (not alerts_present and self.current_state == HomeLayoutState.ALERTS)): self.current_state = HomeLayoutState.UPDATE - elif alerts_present and (current_state == HomeLayoutState.HOME or (not update_available and current_state == HomeLayoutState.UPDATE)): + elif alerts_present and ((not self._prev_alerts_present) or (not update_available and self.current_state == HomeLayoutState.UPDATE)): self.current_state = HomeLayoutState.ALERTS + self.update_available = update_available + self.alert_count = alert_count + self._prev_update_available = update_available + self._prev_alerts_present = alerts_present + def _get_version_text(self) -> str: brand = "openpilot" description = self.params.get("UpdaterCurrentDescription") diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 1ac5083fc6..81eef60d69 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -70,6 +70,7 @@ def setup_pair_device(click, pm: PubMaster): def setup_offroad_alert(click, pm: PubMaster): set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C') + set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal') for alert in OFFROAD_ALERTS: set_offroad_alert(alert, True) diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 7ad96bc9a5..586d90dc4b 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -29,9 +29,10 @@ class AlertConstants: MARGIN = 50 SPACING = 30 FONT_SIZE = 48 - BORDER_RADIUS = 30 + BORDER_RADIUS = 30 * 2 # matches Qt's 30px ALERT_HEIGHT = 120 - ALERT_SPACING = 20 + ALERT_SPACING = 10 + ALERT_INSET = 60 @dataclass @@ -88,7 +89,7 @@ class AbstractAlert(Widget, ABC): HARDWARE.reboot() def _render(self, rect: rl.Rectangle): - rl.draw_rectangle_rounded(rect, AlertConstants.BORDER_RADIUS / rect.width, 10, AlertColors.BACKGROUND) + rl.draw_rectangle_rounded(rect, AlertConstants.BORDER_RADIUS / rect.height, 10, AlertColors.BACKGROUND) footer_height = AlertConstants.BUTTON_SIZE[1] + AlertConstants.SPACING content_height = rect.height - 2 * AlertConstants.MARGIN - footer_height @@ -138,7 +139,8 @@ class AbstractAlert(Widget, ABC): self.dismiss_btn_rect.x = rect.x + AlertConstants.MARGIN self.dismiss_btn_rect.y = footer_y - rl.draw_rectangle_rounded(self.dismiss_btn_rect, 0.3, 10, AlertColors.BUTTON) + roundness = AlertConstants.BORDER_RADIUS / self.dismiss_btn_rect.height + rl.draw_rectangle_rounded(self.dismiss_btn_rect, roundness, 10, AlertColors.BUTTON) text = "Close" text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x @@ -151,7 +153,8 @@ class AbstractAlert(Widget, ABC): if self.snooze_visible: self.snooze_btn_rect.x = rect.x + rect.width - AlertConstants.MARGIN - AlertConstants.SNOOZE_BUTTON_SIZE[0] self.snooze_btn_rect.y = footer_y - rl.draw_rectangle_rounded(self.snooze_btn_rect, 0.3, 10, AlertColors.SNOOZE_BG) + roundness = AlertConstants.BORDER_RADIUS / self.snooze_btn_rect.height + rl.draw_rectangle_rounded(self.snooze_btn_rect, roundness, 10, AlertColors.SNOOZE_BG) text = "Snooze Update" text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x @@ -162,7 +165,8 @@ class AbstractAlert(Widget, ABC): elif self.has_reboot_btn: self.reboot_btn_rect.x = rect.x + rect.width - AlertConstants.MARGIN - AlertConstants.REBOOT_BUTTON_SIZE[0] self.reboot_btn_rect.y = footer_y - rl.draw_rectangle_rounded(self.reboot_btn_rect, 0.3, 10, AlertColors.BUTTON) + roundness = AlertConstants.BORDER_RADIUS / self.reboot_btn_rect.height + rl.draw_rectangle_rounded(self.reboot_btn_rect, roundness, 10, AlertColors.BUTTON) text = "Reboot and Update" text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x @@ -215,11 +219,11 @@ class OffroadAlert(AbstractAlert): if not alert_data.visible: continue - text_width = int(self.content_rect.width - 90) + text_width = int(self.content_rect.width - (AlertConstants.ALERT_INSET * 2)) wrapped_lines = wrap_text(font, alert_data.text, AlertConstants.FONT_SIZE, text_width) line_count = len(wrapped_lines) text_height = line_count * (AlertConstants.FONT_SIZE + 5) - alert_item_height = max(text_height + 40, AlertConstants.ALERT_HEIGHT) + alert_item_height = max(text_height + (AlertConstants.ALERT_INSET * 2), AlertConstants.ALERT_HEIGHT) total_height += alert_item_height + AlertConstants.ALERT_SPACING if total_height > 20: @@ -235,7 +239,7 @@ class OffroadAlert(AbstractAlert): self.sorted_alerts.append(alert_data) def _render_content(self, content_rect: rl.Rectangle): - y_offset = 20 + y_offset = AlertConstants.ALERT_SPACING font = gui_app.font(FontWeight.NORMAL) for alert_data in self.sorted_alerts: @@ -243,11 +247,11 @@ class OffroadAlert(AbstractAlert): continue bg_color = AlertColors.HIGH_SEVERITY if alert_data.severity > 0 else AlertColors.LOW_SEVERITY - text_width = int(content_rect.width - 90) + text_width = int(content_rect.width - (AlertConstants.ALERT_INSET * 2)) wrapped_lines = wrap_text(font, alert_data.text, AlertConstants.FONT_SIZE, text_width) line_count = len(wrapped_lines) text_height = line_count * (AlertConstants.FONT_SIZE + 5) - alert_item_height = max(text_height + 40, AlertConstants.ALERT_HEIGHT) + alert_item_height = max(text_height + (AlertConstants.ALERT_INSET * 2), AlertConstants.ALERT_HEIGHT) alert_rect = rl.Rectangle( content_rect.x + 10, @@ -256,10 +260,11 @@ class OffroadAlert(AbstractAlert): alert_item_height, ) - rl.draw_rectangle_rounded(alert_rect, 0.2, 10, bg_color) + roundness = AlertConstants.BORDER_RADIUS / min(alert_rect.height, alert_rect.width) + rl.draw_rectangle_rounded(alert_rect, roundness, 10, bg_color) - text_x = alert_rect.x + 30 - text_y = alert_rect.y + 20 + text_x = alert_rect.x + AlertConstants.ALERT_INSET + text_y = alert_rect.y + AlertConstants.ALERT_INSET for i, line in enumerate(wrapped_lines): rl.draw_text_ex( From 21273c921e99a40f85b65e51b5b650327e18781b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 2 Oct 2025 23:54:31 -0700 Subject: [PATCH 087/341] raylib: excessive actuation offroad alert (#36242) * excessive actuation check * from gpt * back * use buttons * use widgets for ultimate clean up - no ai slop * feature parity * revert * clean up --- selfdrive/ui/widgets/offroad_alerts.py | 151 ++++++++++++++----------- 1 file changed, 86 insertions(+), 65 deletions(-) diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 586d90dc4b..8ff8f27bed 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -1,10 +1,11 @@ import pyray as rl +from enum import IntEnum from abc import ABC, abstractmethod from collections.abc import Callable from dataclasses import dataclass from openpilot.common.params import Params from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text @@ -17,15 +18,16 @@ class AlertColors: LOW_SEVERITY = rl.Color(41, 41, 41, 255) BACKGROUND = rl.Color(57, 57, 57, 255) BUTTON = rl.WHITE + BUTTON_PRESSED = rl.Color(200, 200, 200, 255) BUTTON_TEXT = rl.BLACK SNOOZE_BG = rl.Color(79, 79, 79, 255) + SNOOZE_BG_PRESSED = rl.Color(100, 100, 100, 255) TEXT = rl.WHITE class AlertConstants: - BUTTON_SIZE = (400, 125) - SNOOZE_BUTTON_SIZE = (550, 125) - REBOOT_BUTTON_SIZE = (600, 125) + MIN_BUTTON_WIDTH = 400 + BUTTON_HEIGHT = 125 MARGIN = 50 SPACING = 30 FONT_SIZE = 48 @@ -43,6 +45,41 @@ class AlertData: visible: bool = False +class ButtonStyle(IntEnum): + LIGHT = 0 + DARK = 1 + + +class ActionButton(Widget): + def __init__(self, text: str, style: ButtonStyle = ButtonStyle.LIGHT, + min_width: int = AlertConstants.MIN_BUTTON_WIDTH): + super().__init__() + self._style = style + self._min_width = min_width + self._font = gui_app.font(FontWeight.MEDIUM) + self.set_text(text) + + def set_text(self, text: str): + self._text = text + self._text_width = measure_text_cached(gui_app.font(FontWeight.MEDIUM), self._text, AlertConstants.FONT_SIZE).x + self._rect.width = max(self._text_width + 60 * 2, self._min_width) + self._rect.height = AlertConstants.BUTTON_HEIGHT + + def _render(self, _): + roundness = AlertConstants.BORDER_RADIUS / self._rect.height + bg_color = AlertColors.BUTTON if self._style == ButtonStyle.LIGHT else AlertColors.SNOOZE_BG + if self.is_pressed: + bg_color = AlertColors.BUTTON_PRESSED if self._style == ButtonStyle.LIGHT else AlertColors.SNOOZE_BG_PRESSED + + rl.draw_rectangle_rounded(self._rect, roundness, 10, bg_color) + + # center text + color = rl.WHITE if self._style == ButtonStyle.DARK else rl.BLACK + text_x = int(self._rect.x + (self._rect.width - self._text_width) // 2) + text_y = int(self._rect.y + (self._rect.height - AlertConstants.FONT_SIZE) // 2) + rl.draw_text_ex(self._font, self._text, rl.Vector2(text_x, text_y), AlertConstants.FONT_SIZE, 0, color) + + class AbstractAlert(Widget, ABC): def __init__(self, has_reboot_btn: bool = False): super().__init__() @@ -50,17 +87,35 @@ class AbstractAlert(Widget, ABC): self.has_reboot_btn = has_reboot_btn self.dismiss_callback: Callable | None = None - self.dismiss_btn_rect = rl.Rectangle(0, 0, *AlertConstants.BUTTON_SIZE) - self.snooze_btn_rect = rl.Rectangle(0, 0, *AlertConstants.SNOOZE_BUTTON_SIZE) - self.reboot_btn_rect = rl.Rectangle(0, 0, *AlertConstants.REBOOT_BUTTON_SIZE) + def snooze_callback(): + self.params.put_bool("SnoozeUpdate", True) + if self.dismiss_callback: + self.dismiss_callback() - self.snooze_visible = False + def excessive_actuation_callback(): + self.params.remove("Offroad_ExcessiveActuation") + if self.dismiss_callback: + self.dismiss_callback() + + self.dismiss_btn = ActionButton("Close") + + self.snooze_btn = ActionButton("Snooze Update", style=ButtonStyle.DARK) + self.snooze_btn.set_click_callback(snooze_callback) + + self.excessive_actuation_btn = ActionButton("Acknowledge Excessive Actuation", style=ButtonStyle.DARK, min_width=800) + self.excessive_actuation_btn.set_click_callback(excessive_actuation_callback) + + self.reboot_btn = ActionButton("Reboot and Update", min_width=600) + self.reboot_btn.set_click_callback(lambda: HARDWARE.reboot()) + + # TODO: just use a Scroller? self.content_rect = rl.Rectangle(0, 0, 0, 0) self.scroll_panel_rect = rl.Rectangle(0, 0, 0, 0) self.scroll_panel = GuiScrollPanel() def set_dismiss_callback(self, callback: Callable): self.dismiss_callback = callback + self.dismiss_btn.set_click_callback(self.dismiss_callback) @abstractmethod def refresh(self) -> bool: @@ -70,28 +125,10 @@ class AbstractAlert(Widget, ABC): def get_content_height(self) -> float: pass - def _handle_mouse_release(self, mouse_pos: MousePos): - super()._handle_mouse_release(mouse_pos) - - if not self.scroll_panel.is_touch_valid(): - return - - if rl.check_collision_point_rec(mouse_pos, self.dismiss_btn_rect): - if self.dismiss_callback: - self.dismiss_callback() - - elif self.snooze_visible and rl.check_collision_point_rec(mouse_pos, self.snooze_btn_rect): - self.params.put_bool("SnoozeUpdate", True) - if self.dismiss_callback: - self.dismiss_callback() - - elif self.has_reboot_btn and rl.check_collision_point_rec(mouse_pos, self.reboot_btn_rect): - HARDWARE.reboot() - def _render(self, rect: rl.Rectangle): rl.draw_rectangle_rounded(rect, AlertConstants.BORDER_RADIUS / rect.height, 10, AlertColors.BACKGROUND) - footer_height = AlertConstants.BUTTON_SIZE[1] + AlertConstants.SPACING + footer_height = AlertConstants.BUTTON_HEIGHT + AlertConstants.SPACING content_height = rect.height - 2 * AlertConstants.MARGIN - footer_height self.content_rect = rl.Rectangle( @@ -134,47 +171,26 @@ class AbstractAlert(Widget, ABC): pass def _render_footer(self, rect: rl.Rectangle): - footer_y = rect.y + rect.height - AlertConstants.MARGIN - AlertConstants.BUTTON_SIZE[1] - font = gui_app.font(FontWeight.MEDIUM) + footer_y = rect.y + rect.height - AlertConstants.MARGIN - AlertConstants.BUTTON_HEIGHT - self.dismiss_btn_rect.x = rect.x + AlertConstants.MARGIN - self.dismiss_btn_rect.y = footer_y - roundness = AlertConstants.BORDER_RADIUS / self.dismiss_btn_rect.height - rl.draw_rectangle_rounded(self.dismiss_btn_rect, roundness, 10, AlertColors.BUTTON) + dismiss_x = rect.x + AlertConstants.MARGIN + self.dismiss_btn.set_position(dismiss_x, footer_y) + self.dismiss_btn.render() - text = "Close" - text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x - text_x = self.dismiss_btn_rect.x + (AlertConstants.BUTTON_SIZE[0] - text_width) // 2 - text_y = self.dismiss_btn_rect.y + (AlertConstants.BUTTON_SIZE[1] - AlertConstants.FONT_SIZE) // 2 - rl.draw_text_ex( - font, text, rl.Vector2(int(text_x), int(text_y)), AlertConstants.FONT_SIZE, 0, AlertColors.BUTTON_TEXT - ) + if self.has_reboot_btn: + reboot_x = rect.x + rect.width - AlertConstants.MARGIN - self.reboot_btn.rect.width + self.reboot_btn.set_position(reboot_x, footer_y) + self.reboot_btn.render() - if self.snooze_visible: - self.snooze_btn_rect.x = rect.x + rect.width - AlertConstants.MARGIN - AlertConstants.SNOOZE_BUTTON_SIZE[0] - self.snooze_btn_rect.y = footer_y - roundness = AlertConstants.BORDER_RADIUS / self.snooze_btn_rect.height - rl.draw_rectangle_rounded(self.snooze_btn_rect, roundness, 10, AlertColors.SNOOZE_BG) + elif self.excessive_actuation_btn.is_visible: + actuation_x = rect.x + rect.width - AlertConstants.MARGIN - self.excessive_actuation_btn.rect.width + self.excessive_actuation_btn.set_position(actuation_x, footer_y) + self.excessive_actuation_btn.render() - text = "Snooze Update" - text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x - text_x = self.snooze_btn_rect.x + (AlertConstants.SNOOZE_BUTTON_SIZE[0] - text_width) // 2 - text_y = self.snooze_btn_rect.y + (AlertConstants.SNOOZE_BUTTON_SIZE[1] - AlertConstants.FONT_SIZE) // 2 - rl.draw_text_ex(font, text, rl.Vector2(int(text_x), int(text_y)), AlertConstants.FONT_SIZE, 0, AlertColors.TEXT) - - elif self.has_reboot_btn: - self.reboot_btn_rect.x = rect.x + rect.width - AlertConstants.MARGIN - AlertConstants.REBOOT_BUTTON_SIZE[0] - self.reboot_btn_rect.y = footer_y - roundness = AlertConstants.BORDER_RADIUS / self.reboot_btn_rect.height - rl.draw_rectangle_rounded(self.reboot_btn_rect, roundness, 10, AlertColors.BUTTON) - - text = "Reboot and Update" - text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x - text_x = self.reboot_btn_rect.x + (AlertConstants.REBOOT_BUTTON_SIZE[0] - text_width) // 2 - text_y = self.reboot_btn_rect.y + (AlertConstants.REBOOT_BUTTON_SIZE[1] - AlertConstants.FONT_SIZE) // 2 - rl.draw_text_ex( - font, text, rl.Vector2(int(text_x), int(text_y)), AlertConstants.FONT_SIZE, 0, AlertColors.BUTTON_TEXT - ) + elif self.snooze_btn.is_visible: + snooze_x = rect.x + rect.width - AlertConstants.MARGIN - self.snooze_btn.rect.width + self.snooze_btn.set_position(snooze_x, footer_y) + self.snooze_btn.render() class OffroadAlert(AbstractAlert): @@ -188,6 +204,7 @@ class OffroadAlert(AbstractAlert): active_count = 0 connectivity_needed = False + excessive_actuation = False for alert_data in self.sorted_alerts: text = "" @@ -205,7 +222,11 @@ class OffroadAlert(AbstractAlert): if alert_data.key == "Offroad_ConnectivityNeeded" and alert_data.visible: connectivity_needed = True - self.snooze_visible = connectivity_needed + if alert_data.key == "Offroad_ExcessiveActuation" and alert_data.visible: + excessive_actuation = True + + self.excessive_actuation_btn.set_visible(excessive_actuation) + self.snooze_btn.set_visible(connectivity_needed and not excessive_actuation) return active_count def get_content_height(self) -> float: From 540fff52262477f62c4ee846ed537f0a81bafc08 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 00:20:30 -0700 Subject: [PATCH 088/341] raylib: draw update button and fix incorrect font (#36243) * always update layout rects * don't ever use raylib font * use it * such as --- pyproject.toml | 1 + selfdrive/ui/layouts/home.py | 4 ++-- selfdrive/ui/widgets/offroad_alerts.py | 2 +- selfdrive/ui/widgets/pairing_dialog.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a0d135db65..9a8de2b746 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -268,6 +268,7 @@ lint.flake8-implicit-str-concat.allow-multiline = false "pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure" "pyray.is_mouse_button_pressed".msg = "This can miss events. Use Widget._handle_mouse_press" "pyray.is_mouse_button_released".msg = "This can miss events. Use Widget._handle_mouse_release" +"pyray.draw_text".msg = "Use a function (such as rl.draw_font_ex) that takes font as an argument" [tool.ruff.format] quote-style = "preserve" diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index c33b16fac4..39b39d33d9 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -88,7 +88,7 @@ class HomeLayout(Widget): elif self.current_state == HomeLayoutState.ALERTS: self._render_alerts_view() - def _update_layout_rects(self): + def _update_state(self): self.header_rect = rl.Rectangle( self._rect.x + CONTENT_MARGIN, self._rect.y + CONTENT_MARGIN, self._rect.width - 2 * CONTENT_MARGIN, HEADER_HEIGHT ) @@ -129,7 +129,7 @@ class HomeLayout(Widget): # Update notification button if self.update_available: # Highlight if currently viewing updates - highlight_color = rl.Color(255, 140, 40, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(255, 102, 0, 255) + highlight_color = rl.Color(75, 95, 255, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(54, 77, 239, 255) rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color) text = "UPDATE" diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 8ff8f27bed..e26b5d313e 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -341,4 +341,4 @@ class UpdateAlert(AbstractAlert): text_width = rl.measure_text(no_notes_text, AlertConstants.FONT_SIZE) text_x = content_rect.x + (content_rect.width - text_width) // 2 text_y = content_rect.y + 50 - rl.draw_text(no_notes_text, int(text_x), int(text_y), AlertConstants.FONT_SIZE, AlertColors.TEXT) + rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), no_notes_text, (int(text_x), int(text_y)), AlertConstants.FONT_SIZE, 0, AlertColors.TEXT) diff --git a/selfdrive/ui/widgets/pairing_dialog.py b/selfdrive/ui/widgets/pairing_dialog.py index 7676635e18..64e1b701f1 100644 --- a/selfdrive/ui/widgets/pairing_dialog.py +++ b/selfdrive/ui/widgets/pairing_dialog.py @@ -146,7 +146,7 @@ class PairingDialog(Widget): rl.draw_circle(int(circle_x), int(circle_y), circle_radius, rl.Color(70, 70, 70, 255)) number = str(i + 1) number_width = measure_text_cached(font, number, 30).x - rl.draw_text(number, int(circle_x - number_width // 2), int(circle_y - 15), 30, rl.WHITE) + rl.draw_text_ex(font, number, (int(circle_x - number_width // 2), int(circle_y - 15)), 30, 0, rl.WHITE) # Text rl.draw_text_ex(font, "\n".join(wrapped), rl.Vector2(text_x, y), 47, 0.0, rl.BLACK) From d567442136d06897bf8b0f46fd729226266ae9e0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 00:37:47 -0700 Subject: [PATCH 089/341] raylib: split out HTML renderer (#36244) * stash * ok chatter is useful for once * why doesn't it understand?! * rm that * clean up --- selfdrive/ui/layouts/settings/device.py | 6 +- selfdrive/ui/widgets/offroad_alerts.py | 2 + system/ui/widgets/html_render.py | 76 ++++++++++++++----------- 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index cb705d46f8..c41c8b0a26 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -11,7 +11,7 @@ from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog -from openpilot.system.ui.widgets.html_render import HtmlRenderer +from openpilot.system.ui.widgets.html_render import HtmlModal from openpilot.system.ui.widgets.list_view import text_item, button_item, dual_button_item from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog from openpilot.system.ui.widgets.scroller import Scroller @@ -36,7 +36,7 @@ class DeviceLayout(Widget): self._select_language_dialog: MultiOptionDialog | None = None self._driver_camera: DriverCameraDialog | None = None self._pair_device_dialog: PairingDialog | None = None - self._fcc_dialog: HtmlRenderer | None = None + self._fcc_dialog: HtmlModal | None = None self._training_guide: TrainingGuide | None = None items = self._initialize_items() @@ -140,7 +140,7 @@ class DeviceLayout(Widget): def _on_regulatory(self): if not self._fcc_dialog: - self._fcc_dialog = HtmlRenderer(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html")) + self._fcc_dialog = HtmlModal(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html")) gui_app.set_modal_overlay(self._fcc_dialog) def _on_review_training_guide(self): diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index e26b5d313e..444688b7a0 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -10,6 +10,7 @@ from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.widgets.html_render import HtmlRenderer from openpilot.selfdrive.selfdrived.alertmanager import OFFROAD_ALERTS @@ -306,6 +307,7 @@ class UpdateAlert(AbstractAlert): self.release_notes = "" self._wrapped_release_notes = "" self._cached_content_height: float = 0.0 + self._html_renderer: HtmlRenderer | None = None def refresh(self) -> bool: update_available: bool = self.params.get_bool("UpdateAvailable") diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index b870227854..db30833f19 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -34,13 +34,11 @@ class HtmlElement: class HtmlRenderer(Widget): - def __init__(self, file_path: str): + def __init__(self, file_path: str | None = None, text: str | None = None): super().__init__() self.elements: list[HtmlElement] = [] self._normal_font = gui_app.font(FontWeight.NORMAL) self._bold_font = gui_app.font(FontWeight.BOLD) - self._scroll_panel = GuiScrollPanel() - self._ok_button = Button("OK", click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY) self.styles: dict[ElementType, dict[str, Any]] = { ElementType.H1: {"size": 68, "weight": FontWeight.BOLD, "color": rl.BLACK, "margin_top": 20, "margin_bottom": 16}, @@ -53,7 +51,12 @@ class HtmlRenderer(Widget): ElementType.BR: {"size": 0, "weight": FontWeight.NORMAL, "color": rl.BLACK, "margin_top": 0, "margin_bottom": 12}, } - self.parse_html_file(file_path) + if file_path is not None: + self.parse_html_file(file_path) + elif text is not None: + self.parse_html_content(text) + else: + raise ValueError("Either file_path or text must be provided") def parse_html_file(self, file_path: str) -> None: with open(file_path, encoding='utf-8') as file: @@ -106,33 +109,7 @@ class HtmlRenderer(Widget): self.elements.append(element) def _render(self, rect: rl.Rectangle): - margin = 50 - content_rect = rl.Rectangle(rect.x + margin, rect.y + margin, rect.width - (margin * 2), rect.height - (margin * 2)) - - button_height = 160 - button_spacing = 20 - scrollable_height = content_rect.height - button_height - button_spacing - - scrollable_rect = rl.Rectangle(content_rect.x, content_rect.y, content_rect.width, scrollable_height) - - total_height = self.get_total_height(int(scrollable_rect.width)) - scroll_content_rect = rl.Rectangle(scrollable_rect.x, scrollable_rect.y, scrollable_rect.width, total_height) - scroll_offset = self._scroll_panel.update(scrollable_rect, scroll_content_rect) - - rl.begin_scissor_mode(int(scrollable_rect.x), int(scrollable_rect.y), int(scrollable_rect.width), int(scrollable_rect.height)) - self._render_content(scrollable_rect, scroll_offset) - rl.end_scissor_mode() - - button_width = (rect.width - 3 * 50) // 3 - button_x = content_rect.x + content_rect.width - button_width - button_y = content_rect.y + content_rect.height - button_height - button_rect = rl.Rectangle(button_x, button_y, button_width, button_height) - self._ok_button.render(button_rect) - - return -1 - - def _render_content(self, rect: rl.Rectangle, scroll_offset: float = 0) -> float: - current_y = rect.y + scroll_offset + current_y = rect.y padding = 20 content_width = rect.width - (padding * 2) @@ -164,7 +141,7 @@ class HtmlRenderer(Widget): # Apply bottom margin current_y += element.margin_bottom - return current_y - rect.y - scroll_offset # Return total content height + return current_y - rect.y def get_total_height(self, content_width: int) -> float: total_height = 0.0 @@ -193,3 +170,38 @@ class HtmlRenderer(Widget): if weight == FontWeight.BOLD: return self._bold_font return self._normal_font + + +class HtmlModal(Widget): + def __init__(self, file_path: str | None = None, text: str | None = None): + super().__init__() + self._content = HtmlRenderer(file_path=file_path, text=text) + self._scroll_panel = GuiScrollPanel() + self._ok_button = Button("OK", click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY) + + def _render(self, rect: rl.Rectangle): + margin = 50 + content_rect = rl.Rectangle(rect.x + margin, rect.y + margin, rect.width - (margin * 2), rect.height - (margin * 2)) + + button_height = 160 + button_spacing = 20 + scrollable_height = content_rect.height - button_height - button_spacing + + scrollable_rect = rl.Rectangle(content_rect.x, content_rect.y, content_rect.width, scrollable_height) + + total_height = self._content.get_total_height(int(scrollable_rect.width)) + scroll_content_rect = rl.Rectangle(scrollable_rect.x, scrollable_rect.y, scrollable_rect.width, total_height) + scroll_offset = self._scroll_panel.update(scrollable_rect, scroll_content_rect) + scroll_content_rect.y += scroll_offset + + rl.begin_scissor_mode(int(scrollable_rect.x), int(scrollable_rect.y), int(scrollable_rect.width), int(scrollable_rect.height)) + self._content.render(scroll_content_rect) + rl.end_scissor_mode() + + button_width = (rect.width - 3 * 50) // 3 + button_x = content_rect.x + content_rect.width - button_width + button_y = content_rect.y + content_rect.height - button_height + button_rect = rl.Rectangle(button_x, button_y, button_width, button_height) + self._ok_button.render(button_rect) + + return -1 From 150ff72646e7d7f9056246e2898f639fc0e2345f Mon Sep 17 00:00:00 2001 From: Armand du Parc Locmaria Date: Fri, 3 Oct 2025 14:36:12 -0700 Subject: [PATCH 090/341] Dockerfile.openpilot: don't set UV_PROJECT_ENVIRONMENT (#36246) * Dockerfile.openpilot: don't uv sync with root * Revert "Dockerfile.openpilot: don't uv sync with root" This reverts commit 2c271d0b5b55d6ae2ece6b28dc90a96e6e891ded. * don't set UV_PROJECT_ENVIRONMENT --- Dockerfile.openpilot | 2 +- Dockerfile.openpilot_base | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile.openpilot b/Dockerfile.openpilot index 14c1e64774..106a06e3a2 100644 --- a/Dockerfile.openpilot +++ b/Dockerfile.openpilot @@ -11,4 +11,4 @@ COPY . ${OPENPILOT_PATH}/ ENV UV_BIN="/home/batman/.local/bin/" ENV PATH="$UV_BIN:$PATH" -RUN uv run scons --cache-readonly -j$(nproc) +RUN UV_PROJECT_ENVIRONMENT=$VIRTUAL_ENV uv run scons --cache-readonly -j$(nproc) diff --git a/Dockerfile.openpilot_base b/Dockerfile.openpilot_base index e3bb03de07..44d8d95e95 100644 --- a/Dockerfile.openpilot_base +++ b/Dockerfile.openpilot_base @@ -71,8 +71,8 @@ USER $USER COPY --chown=$USER pyproject.toml uv.lock /home/$USER COPY --chown=$USER tools/install_python_dependencies.sh /home/$USER/tools/ -ENV UV_PROJECT_ENVIRONMENT=/home/$USER/.venv -ENV PATH="$UV_PROJECT_ENVIRONMENT/bin:$PATH" +ENV VIRTUAL_ENV=/home/$USER/.venv +ENV PATH="$VIRTUAL_ENV/bin:$PATH" RUN cd /home/$USER && \ tools/install_python_dependencies.sh && \ rm -rf tools/ pyproject.toml uv.lock .cache From 670b6011da567d2db2a30d5621eba314eda447ee Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 16:13:40 -0700 Subject: [PATCH 091/341] raylib: match QT confirmation dialog size (#36248) * closer to qt * this too * eval --- system/ui/widgets/confirm_dialog.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index b1dc54bf7a..606b04b6e9 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -5,8 +5,8 @@ from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.label import Label from openpilot.system.ui.widgets import Widget -DIALOG_WIDTH = 1520 -DIALOG_HEIGHT = 600 +DIALOG_WIDTH = 1748 +DIALOG_HEIGHT = 690 BUTTON_HEIGHT = 160 MARGIN = 50 TEXT_AREA_HEIGHT_REDUCTION = 200 @@ -16,7 +16,7 @@ BACKGROUND_COLOR = rl.Color(27, 27, 27, 255) class ConfirmDialog(Widget): def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel"): super().__init__() - self._label = Label(text, 70, FontWeight.BOLD) + self._label = Label(text, 70, FontWeight.BOLD, text_color=rl.Color(201, 201, 201, 255)) self._cancel_button = Button(cancel_text, self._cancel_button_callback) self._confirm_button = Button(confirm_text, self._confirm_button_callback, button_style=ButtonStyle.PRIMARY) self._dialog_result = DialogResult.NO_ACTION From bd357adb8b7deda194a8f25e00ba58134e099fd3 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Fri, 3 Oct 2025 17:01:20 -0700 Subject: [PATCH 092/341] update release notes for 0.10.1 --- RELEASES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 966d5d3809..568be6c353 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,10 +1,12 @@ Version 0.10.1 (2025-09-08) ======================== -* New driving model #36087 +* New driving model #36114 * World Model: removed global localization inputs * World Model: 2x the number of parameters * World Model: trained on 4x the number of segments + * VAE Compression Model: new architecture and training objective * Driving Vision Model: trained on 4x the number of segments +* New Driver Monitoring model #36198 * Acura TLX 2021 support thanks to MVL! * Honda City 2023 support thanks to vanillagorillaa and drFritz! * Honda N-Box 2018 support thanks to miettal! From 7b2b10bc9e28fb1080b1415e5173b0b29683b66f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 17:34:22 -0700 Subject: [PATCH 093/341] raylib screenshots: raise ui delay --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 81eef60d69..acb317cbaa 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -20,7 +20,7 @@ from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert TEST_DIR = pathlib.Path(__file__).parent TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" -UI_DELAY = 0.1 +UI_DELAY = 0.2 # Offroad alerts to test OFFROAD_ALERTS = ['Offroad_IsTakingSnapshot'] From 9670e3a5eb4add3a26a7875d455b6101d4bfcebe Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 17:34:53 -0700 Subject: [PATCH 094/341] raylib: add confirmation dialog (#36252) * conf * update case * fix * fix * rm * back * alread setup * avail --- .../ui/tests/test_ui/raylib_screenshots.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index acb317cbaa..d7e08112b2 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -12,10 +12,12 @@ import pywinctl from cereal import log from cereal import messaging from cereal.messaging import PubMaster +from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params from openpilot.common.prefix import OpenpilotPrefix from openpilot.selfdrive.test.helpers import with_processes from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert +from openpilot.system.updated.updated import parse_release_notes TEST_DIR = pathlib.Path(__file__).parent TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" @@ -34,6 +36,10 @@ def setup_settings_device(click, pm: PubMaster): click(100, 100) +def close_settings(click, pm: PubMaster): + click(240, 216) + + def setup_settings_network(click, pm: PubMaster): setup_settings_device(click, pm) click(278, 450) @@ -75,7 +81,22 @@ def setup_offroad_alert(click, pm: PubMaster): set_offroad_alert(alert, True) setup_settings_device(click, pm) - click(240, 216) + close_settings(click, pm) + + +def setup_confirmation_dialog(click, pm: PubMaster): + setup_settings_device(click, pm) + click(1985, 791) # reset calibration + + +def setup_update_available(click, pm: PubMaster): + params = Params() + params.put_bool("UpdateAvailable", True) + params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR)) + description = "0.10.1 / html-release-notes-2 / 7864838 / Oct 03" + params.put("UpdaterCurrentDescription", description) + setup_settings_device(click, pm) + close_settings(click, pm) CASES = { @@ -89,6 +110,8 @@ CASES = { "keyboard": setup_keyboard, "pair_device": setup_pair_device, "offroad_alert": setup_offroad_alert, + "update_available": setup_update_available, + "confirmation_dialog": setup_confirmation_dialog, } From edc5a0412c3b1ef3b831ec93d9454905c11037bb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 17:35:05 -0700 Subject: [PATCH 095/341] rename to setup_settings --- .../ui/tests/test_ui/raylib_screenshots.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index d7e08112b2..f570a701ba 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -32,7 +32,7 @@ def setup_homescreen(click, pm: PubMaster): pass -def setup_settings_device(click, pm: PubMaster): +def setup_settings(click, pm: PubMaster): click(100, 100) @@ -41,27 +41,27 @@ def close_settings(click, pm: PubMaster): def setup_settings_network(click, pm: PubMaster): - setup_settings_device(click, pm) + setup_settings(click, pm) click(278, 450) def setup_settings_toggles(click, pm: PubMaster): - setup_settings_device(click, pm) + setup_settings(click, pm) click(278, 600) def setup_settings_software(click, pm: PubMaster): - setup_settings_device(click, pm) + setup_settings(click, pm) click(278, 720) def setup_settings_firehose(click, pm: PubMaster): - setup_settings_device(click, pm) + setup_settings(click, pm) click(278, 845) def setup_settings_developer(click, pm: PubMaster): - setup_settings_device(click, pm) + setup_settings(click, pm) click(278, 950) @@ -80,12 +80,12 @@ def setup_offroad_alert(click, pm: PubMaster): for alert in OFFROAD_ALERTS: set_offroad_alert(alert, True) - setup_settings_device(click, pm) + setup_settings(click, pm) close_settings(click, pm) def setup_confirmation_dialog(click, pm: PubMaster): - setup_settings_device(click, pm) + setup_settings(click, pm) click(1985, 791) # reset calibration @@ -95,13 +95,13 @@ def setup_update_available(click, pm: PubMaster): params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR)) description = "0.10.1 / html-release-notes-2 / 7864838 / Oct 03" params.put("UpdaterCurrentDescription", description) - setup_settings_device(click, pm) + setup_settings(click, pm) close_settings(click, pm) CASES = { "homescreen": setup_homescreen, - "settings_device": setup_settings_device, + "settings_device": setup_settings, "settings_network": setup_settings_network, "settings_toggles": setup_settings_toggles, "settings_software": setup_settings_software, From 45f497e8f68e06f1256b0b26dcf640d2191c32e7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 17:46:39 -0700 Subject: [PATCH 096/341] raylib screenshots: add mouse click helper (#36253) * add helper * rm * name * fix --- .../ui/tests/test_ui/print_mouse_coords.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100755 selfdrive/ui/tests/test_ui/print_mouse_coords.py diff --git a/selfdrive/ui/tests/test_ui/print_mouse_coords.py b/selfdrive/ui/tests/test_ui/print_mouse_coords.py new file mode 100755 index 0000000000..1e88ce57d3 --- /dev/null +++ b/selfdrive/ui/tests/test_ui/print_mouse_coords.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +""" +Simple script to print mouse coordinates on Ubuntu. +Run with: python print_mouse_coords.py +Press Ctrl+C to exit. +""" + +from pynput import mouse + +print("Mouse coordinate printer - Press Ctrl+C to exit") +print("Click to set the top left origin") + +origin: tuple[int, int] | None = None +clicks: list[tuple[int, int]] = [] + + +def on_click(x, y, button, pressed): + global origin, clicks + if pressed: # Only on mouse down, not up + if origin is None: + origin = (x, y) + print(f"Origin set to: {x},{y}") + else: + rel_x = x - origin[0] + rel_y = y - origin[1] + clicks.append((rel_x, rel_y)) + print(f"Clicks: {clicks}") + + +if __name__ == "__main__": + try: + # Start mouse listener + with mouse.Listener(on_click=on_click) as listener: + listener.join() + except KeyboardInterrupt: + print("\nExiting...") From 39b97d4e18b99712344f898ee03975889d64fb6f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 17:50:02 -0700 Subject: [PATCH 097/341] raylib screenshots: use long branch name (#36254) * stress test * everything --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index f570a701ba..36d4e1f504 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -28,6 +28,14 @@ UI_DELAY = 0.2 OFFROAD_ALERTS = ['Offroad_IsTakingSnapshot'] +def put_update_params(params: Params): + params.put("UpdaterCurrentReleaseNotes", parse_release_notes(BASEDIR)) + params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR)) + description = "0.10.1 / this-is-a-really-super-mega-long-branch-name / 7864838 / Oct 03" + params.put("UpdaterCurrentDescription", description) + params.put("UpdaterNewDescription", description) + + def setup_homescreen(click, pm: PubMaster): pass @@ -51,6 +59,7 @@ def setup_settings_toggles(click, pm: PubMaster): def setup_settings_software(click, pm: PubMaster): + put_update_params(Params()) setup_settings(click, pm) click(278, 720) @@ -92,9 +101,7 @@ def setup_confirmation_dialog(click, pm: PubMaster): def setup_update_available(click, pm: PubMaster): params = Params() params.put_bool("UpdateAvailable", True) - params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR)) - description = "0.10.1 / html-release-notes-2 / 7864838 / Oct 03" - params.put("UpdaterCurrentDescription", description) + put_update_params(params) setup_settings(click, pm) close_settings(click, pm) From 844c3286254ab36490e129f19175f3c0cd06fa2f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 17:59:19 -0700 Subject: [PATCH 098/341] raylib screenshots: prevent saving black frame raylib screenshots: prevent saving black frame --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 36d4e1f504..ecd1302589 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -156,6 +156,7 @@ class TestUI: @with_processes(["raylib_ui"]) def test_ui(self, name, setup_case): self.setup() + time.sleep(UI_DELAY) # wait for UI to start setup_case(self.click, self.pm) self.screenshot(name) From a8328cb5ffd4df13a02a7e925aa673ee07397924 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 17:59:42 -0700 Subject: [PATCH 099/341] raylib screenshots: find diff faster (#36255) * ? * run it * wrong * here too * revert --- .github/workflows/raylib_ui_preview.yaml | 2 +- .github/workflows/ui_preview.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index ff9655d155..3986c8592f 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -83,7 +83,7 @@ jobs: if: github.event_name == 'pull_request_target' id: find_diff run: >- - sudo apt-get update && sudo apt-get install -y imagemagick + sudo apt-get install -y imagemagick scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') A=($scenes) diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml index 9ec7a59223..79ab373c2e 100644 --- a/.github/workflows/ui_preview.yaml +++ b/.github/workflows/ui_preview.yaml @@ -83,7 +83,7 @@ jobs: if: github.event_name == 'pull_request_target' id: find_diff run: >- - sudo apt-get update && sudo apt-get install -y imagemagick + sudo apt-get install -y imagemagick scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') A=($scenes) From 4d53a26a06973a7d6fa4fef1e238a3bb9e64e67c Mon Sep 17 00:00:00 2001 From: Armand du Parc Locmaria Date: Fri, 3 Oct 2025 19:43:03 -0700 Subject: [PATCH 100/341] relock after inplace metadrive update (#36256) * relock after inplace metadrive update * Revert "relock after inplace metadrive update" This reverts commit 18193ffe34b66085e18605e6c9289ddcd658844d. * just the hash --- uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index 9c791b8110..3246221299 100644 --- a/uv.lock +++ b/uv.lock @@ -949,7 +949,7 @@ dependencies = [ { name = "yapf" }, ] 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" }, + { 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:d0afaf3b005e35e14b929d5491d2d5b64562d0c1cd5093ba969fb63908670dd4" }, ] [package.metadata] From 99a83e5522da426f8eeeff85838d3e0d0b70b0fc Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 20:35:32 -0700 Subject: [PATCH 101/341] Revert "raylib screenshots: find diff faster (#36255)" This reverts commit a8328cb5ffd4df13a02a7e925aa673ee07397924. --- .github/workflows/raylib_ui_preview.yaml | 2 +- .github/workflows/ui_preview.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 3986c8592f..ff9655d155 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -83,7 +83,7 @@ jobs: if: github.event_name == 'pull_request_target' id: find_diff run: >- - sudo apt-get install -y imagemagick + sudo apt-get update && sudo apt-get install -y imagemagick scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') A=($scenes) diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml index 79ab373c2e..9ec7a59223 100644 --- a/.github/workflows/ui_preview.yaml +++ b/.github/workflows/ui_preview.yaml @@ -83,7 +83,7 @@ jobs: if: github.event_name == 'pull_request_target' id: find_diff run: >- - sudo apt-get install -y imagemagick + sudo apt-get update && sudo apt-get install -y imagemagick scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') A=($scenes) From 89d350a7912919dd570abd51f7cbc41bd8c19b07 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 20:42:42 -0700 Subject: [PATCH 102/341] raylib html renderer: fixups (#36257) * this wasn't used * override text size and color * render untagged text as paragraph * and indent * cache expensive height calc * fmt * fix that * unclear if this is even needed * and that * huh * debug * Revert "debug" This reverts commit 7d446d2a37a96e6bd1001c566d4f8e8f417f8fb7. --- system/ui/widgets/html_render.py | 105 ++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index db30833f19..b032df4d9f 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -9,6 +9,8 @@ from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle +LIST_INDENT_PX = 40 + class ElementType(Enum): H1 = "h1" @@ -18,39 +20,60 @@ class ElementType(Enum): H5 = "h5" H6 = "h6" P = "p" + UL = "ul" + LI = "li" BR = "br" +TAG_NAMES = '|'.join([t.value for t in ElementType]) +START_TAG_RE = re.compile(f'<({TAG_NAMES})>') +END_TAG_RE = re.compile(f'') + + +def is_tag(token: str) -> tuple[bool, bool, ElementType | None]: + supported_tag = bool(START_TAG_RE.fullmatch(token)) + supported_end_tag = bool(END_TAG_RE.fullmatch(token)) + tag = ElementType(token[1:-1].strip('/')) if supported_tag or supported_end_tag else None + return supported_tag, supported_end_tag, tag + + @dataclass class HtmlElement: type: ElementType content: str font_size: int font_weight: FontWeight - color: rl.Color margin_top: int margin_bottom: int line_height: float = 1.2 + indent_level: int = 0 class HtmlRenderer(Widget): - def __init__(self, file_path: str | None = None, text: str | None = None): + def __init__(self, file_path: str | None = None, text: str | None = None, + text_size: dict | None = None, text_color: rl.Color = rl.WHITE): super().__init__() - self.elements: list[HtmlElement] = [] + self._text_color = text_color self._normal_font = gui_app.font(FontWeight.NORMAL) self._bold_font = gui_app.font(FontWeight.BOLD) + self._indent_level = 0 + if text_size is None: + text_size = {} + + # Untagged text defaults to

self.styles: dict[ElementType, dict[str, Any]] = { - ElementType.H1: {"size": 68, "weight": FontWeight.BOLD, "color": rl.BLACK, "margin_top": 20, "margin_bottom": 16}, - ElementType.H2: {"size": 60, "weight": FontWeight.BOLD, "color": rl.BLACK, "margin_top": 24, "margin_bottom": 12}, - ElementType.H3: {"size": 52, "weight": FontWeight.BOLD, "color": rl.BLACK, "margin_top": 20, "margin_bottom": 10}, - ElementType.H4: {"size": 48, "weight": FontWeight.BOLD, "color": rl.BLACK, "margin_top": 16, "margin_bottom": 8}, - ElementType.H5: {"size": 44, "weight": FontWeight.BOLD, "color": rl.BLACK, "margin_top": 12, "margin_bottom": 6}, - ElementType.H6: {"size": 40, "weight": FontWeight.BOLD, "color": rl.BLACK, "margin_top": 10, "margin_bottom": 4}, - ElementType.P: {"size": 38, "weight": FontWeight.NORMAL, "color": rl.Color(40, 40, 40, 255), "margin_top": 8, "margin_bottom": 12}, - ElementType.BR: {"size": 0, "weight": FontWeight.NORMAL, "color": rl.BLACK, "margin_top": 0, "margin_bottom": 12}, + ElementType.H1: {"size": 68, "weight": FontWeight.BOLD, "margin_top": 20, "margin_bottom": 16}, + ElementType.H2: {"size": 60, "weight": FontWeight.BOLD, "margin_top": 24, "margin_bottom": 12}, + ElementType.H3: {"size": 52, "weight": FontWeight.BOLD, "margin_top": 20, "margin_bottom": 10}, + ElementType.H4: {"size": 48, "weight": FontWeight.BOLD, "margin_top": 16, "margin_bottom": 8}, + ElementType.H5: {"size": 44, "weight": FontWeight.BOLD, "margin_top": 12, "margin_bottom": 6}, + ElementType.H6: {"size": 40, "weight": FontWeight.BOLD, "margin_top": 10, "margin_bottom": 4}, + ElementType.P: {"size": text_size.get(ElementType.P, 38), "weight": FontWeight.NORMAL, "margin_top": 8, "margin_bottom": 12}, + ElementType.BR: {"size": 0, "weight": FontWeight.NORMAL, "margin_top": 0, "margin_bottom": 12}, } + self.elements: list[HtmlElement] = [] if file_path is not None: self.parse_html_file(file_path) elif text is not None: @@ -73,25 +96,48 @@ class HtmlRenderer(Widget): html_content = re.sub(r']*>', '', html_content) html_content = re.sub(r']*>', '', html_content) - # Find all HTML elements - pattern = r'<(h[1-6]|p)(?:[^>]*)>(.*?)|' - matches = re.finditer(pattern, html_content, re.DOTALL | re.IGNORECASE) + # Parse HTML + tokens = re.findall(r']+>|<[^>]+>|[^<\s]+', html_content) + + def close_tag(): + nonlocal current_content + nonlocal current_tag + + # If no tag is set, default to paragraph so we don't lose text + if current_tag is None: + current_tag = ElementType.P + + text = ' '.join(current_content).strip() + current_content = [] + if text: + if current_tag == ElementType.LI: + text = '• ' + text + self._add_element(current_tag, text) + + current_content: list[str] = [] + current_tag: ElementType | None = None + for token in tokens: + is_start_tag, is_end_tag, tag = is_tag(token) + if tag is not None: + if tag == ElementType.BR: + self._add_element(ElementType.BR, "") + + elif tag == ElementType.UL: + self._indent_level = self._indent_level + 1 if is_start_tag else max(0, self._indent_level - 1) + + elif is_start_tag or is_end_tag: + # Always add content regardless of opening or closing tag + close_tag() + + # TODO: reset to None if end tag? + if is_start_tag: + current_tag = tag - for match in matches: - if match.group(0).lower().startswith(' tags - self._add_element(ElementType.BR, "") else: - tag = match.group(1).lower() - content = match.group(2).strip() + current_content.append(token) - # Clean up content - remove extra whitespace - content = re.sub(r'\s+', ' ', content) - content = content.strip() - - if content: # Only add non-empty elements - element_type = ElementType(tag) - self._add_element(element_type, content) + if current_content: + close_tag() def _add_element(self, element_type: ElementType, content: str) -> None: style = self.styles[element_type] @@ -101,9 +147,9 @@ class HtmlRenderer(Widget): content=content, font_size=style["size"], font_weight=style["weight"], - color=style["color"], margin_top=style["margin_top"], margin_bottom=style["margin_bottom"], + indent_level=self._indent_level, ) self.elements.append(element) @@ -134,7 +180,8 @@ class HtmlRenderer(Widget): if current_y > rect.y + rect.height: break - rl.draw_text_ex(font, line, rl.Vector2(rect.x + padding, current_y), element.font_size, 0, rl.WHITE) + text_x = rect.x + (max(element.indent_level - 1, 0) * LIST_INDENT_PX) + rl.draw_text_ex(font, line, rl.Vector2(text_x + padding, current_y), element.font_size, 0, self._text_color) current_y += element.font_size * element.line_height From 12b3d0e08dbb10318186cd37b833c40dbb98749f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 20:52:50 -0700 Subject: [PATCH 103/341] raylib: cache wrap text (#36258) * cache html height * clean up * todo --- system/ui/lib/wrap_text.py | 8 ++++++++ system/ui/widgets/html_render.py | 1 + 2 files changed, 9 insertions(+) diff --git a/system/ui/lib/wrap_text.py b/system/ui/lib/wrap_text.py index 35dc5ac401..f6caa3c5f0 100644 --- a/system/ui/lib/wrap_text.py +++ b/system/ui/lib/wrap_text.py @@ -36,7 +36,14 @@ def _break_long_word(font: rl.Font, word: str, font_size: int, max_width: int) - return parts +_cache: dict[int, list[str]] = {} + + def wrap_text(font: rl.Font, text: str, font_size: int, max_width: int) -> list[str]: + key = hash((font.texture.id, text, font_size, max_width)) + if key in _cache: + return _cache[key] + if not text or max_width <= 0: return [] @@ -100,4 +107,5 @@ def wrap_text(font: rl.Font, text: str, font_size: int, max_width: int) -> list[ # Add all lines from this paragraph all_lines.extend(lines) + _cache[key] = all_lines return all_lines diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index b032df4d9f..f7dc9e9344 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -155,6 +155,7 @@ class HtmlRenderer(Widget): self.elements.append(element) def _render(self, rect: rl.Rectangle): + # TODO: speed up by removing duplicate calculations across renders current_y = rect.y padding = 20 content_width = rect.width - (padding * 2) From bd9888a439712f951f682bea38fb070ab1a240c0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 21:29:20 -0700 Subject: [PATCH 104/341] raylib screenshots: add software release notes (#36259) add software --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index ecd1302589..e77a2d4d7e 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -98,7 +98,7 @@ def setup_confirmation_dialog(click, pm: PubMaster): click(1985, 791) # reset calibration -def setup_update_available(click, pm: PubMaster): +def setup_homescreen_update_available(click, pm: PubMaster): params = Params() params.put_bool("UpdateAvailable", True) put_update_params(params) @@ -106,6 +106,12 @@ def setup_update_available(click, pm: PubMaster): close_settings(click, pm) +def setup_software_release_notes(click, pm: PubMaster): + setup_settings(click, pm) + setup_settings_software(click, pm) + click(588, 110) # expand description for current version + + CASES = { "homescreen": setup_homescreen, "settings_device": setup_settings, @@ -117,8 +123,9 @@ CASES = { "keyboard": setup_keyboard, "pair_device": setup_pair_device, "offroad_alert": setup_offroad_alert, - "update_available": setup_update_available, + "homescreen_update_available": setup_homescreen_update_available, "confirmation_dialog": setup_confirmation_dialog, + "software_release_notes": setup_software_release_notes, } From 2337704602fe492741afbe856f19118ae562f196 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 21:47:53 -0700 Subject: [PATCH 105/341] raylib: release notes are drawn with HTML renderer (#36245) * stash * ok chatter is useful for once * draw text outside tags * hmm * undo that shit * i don't like this chatgpt * Revert "i don't like this chatgpt" This reverts commit 5b511911d81242457bfb5fc808a9b9f35fe9f7a2. * more robust parsing (works with missing tags, markdown.py actually had bug) + add indent level * the html looks weird but is correct - the old parser didn't handle it * clean up * some * move out * clean up * oh this was wrong * draft * rm that * fix * fix indentation for new driving model * clean up * some clean up * more clean up * more clean up * and this * cmt * ok this is egregious mypy --- selfdrive/ui/layouts/settings/software.py | 4 +- selfdrive/ui/widgets/offroad_alerts.py | 27 +++----- system/ui/lib/application.py | 2 +- system/ui/widgets/html_render.py | 8 ++- system/ui/widgets/list_view.py | 75 +++++++++++------------ 5 files changed, 55 insertions(+), 61 deletions(-) diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 7440512a62..7aebc609f8 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -80,7 +80,7 @@ class SoftwareLayout(Widget): current_desc = ui_state.params.get("UpdaterCurrentDescription") or "" current_release_notes = (ui_state.params.get("UpdaterCurrentReleaseNotes") or b"").decode("utf-8", "replace") self._version_item.action_item.set_text(current_desc) - self._version_item.description = current_release_notes + self._version_item.set_description(current_release_notes) # Update download button visibility and state self._download_btn.set_visible(ui_state.is_offroad()) @@ -125,7 +125,7 @@ class SoftwareLayout(Widget): new_release_notes = (ui_state.params.get("UpdaterNewReleaseNotes") or b"").decode("utf-8", "replace") self._install_btn.action_item.set_text("INSTALL") self._install_btn.action_item.set_value(new_desc) - self._install_btn.description = new_release_notes + self._install_btn.set_description(new_release_notes) # Enable install button for testing (like Qt showEvent) self._install_btn.action_item.set_enabled(True) else: diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 444688b7a0..1045971636 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -14,6 +14,9 @@ from openpilot.system.ui.widgets.html_render import HtmlRenderer from openpilot.selfdrive.selfdrived.alertmanager import OFFROAD_ALERTS +NO_RELEASE_NOTES = "

No release notes available.

" + + class AlertColors: HIGH_SEVERITY = rl.Color(226, 44, 44, 255) LOW_SEVERITY = rl.Color(41, 41, 41, 255) @@ -307,13 +310,16 @@ class UpdateAlert(AbstractAlert): self.release_notes = "" self._wrapped_release_notes = "" self._cached_content_height: float = 0.0 - self._html_renderer: HtmlRenderer | None = None + self._html_renderer = HtmlRenderer(text="") def refresh(self) -> bool: update_available: bool = self.params.get_bool("UpdateAvailable") if update_available: - self.release_notes = self.params.get("UpdaterNewReleaseNotes") + self.release_notes = (self.params.get("UpdaterNewReleaseNotes") or b"").decode("utf8").strip() + self._html_renderer.parse_html_content(self.release_notes or NO_RELEASE_NOTES) self._cached_content_height = 0 + else: + self._html_renderer.parse_html_content(NO_RELEASE_NOTES) return update_available @@ -329,18 +335,5 @@ class UpdateAlert(AbstractAlert): return self._cached_content_height def _render_content(self, content_rect: rl.Rectangle): - if self.release_notes: - rl.draw_text_ex( - gui_app.font(FontWeight.NORMAL), - self._wrapped_release_notes, - rl.Vector2(content_rect.x + 30, content_rect.y + 30), - AlertConstants.FONT_SIZE, - 0.0, - AlertColors.TEXT, - ) - else: - no_notes_text = "No release notes available." - text_width = rl.measure_text(no_notes_text, AlertConstants.FONT_SIZE) - text_x = content_rect.x + (content_rect.width - text_width) // 2 - text_y = content_rect.y + 50 - rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), no_notes_text, (int(text_x), int(text_y)), AlertConstants.FONT_SIZE, 0, AlertColors.TEXT) + notes_rect = rl.Rectangle(content_rect.x + 30, content_rect.y + 30, content_rect.width - 60, content_rect.height - 60) + self._html_renderer.render(notes_rect) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 6c703a516e..a42b1d1129 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -331,7 +331,7 @@ class GuiApplication: for layout in KEYBOARD_LAYOUTS.values(): all_chars.update(key for row in layout for key in row) all_chars = "".join(all_chars) - all_chars += "–✓×°§" + all_chars += "–✓×°§•" codepoint_count = rl.ffi.new("int *", 1) codepoints = rl.load_codepoints(all_chars, codepoint_count) diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index f7dc9e9344..2c3eeb3793 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -70,6 +70,7 @@ class HtmlRenderer(Widget): ElementType.H5: {"size": 44, "weight": FontWeight.BOLD, "margin_top": 12, "margin_bottom": 6}, ElementType.H6: {"size": 40, "weight": FontWeight.BOLD, "margin_top": 10, "margin_bottom": 4}, ElementType.P: {"size": text_size.get(ElementType.P, 38), "weight": FontWeight.NORMAL, "margin_top": 8, "margin_bottom": 12}, + ElementType.LI: {"size": 38, "weight": FontWeight.NORMAL, "color": rl.Color(40, 40, 40, 255), "margin_top": 6, "margin_bottom": 6}, ElementType.BR: {"size": 0, "weight": FontWeight.NORMAL, "margin_top": 0, "margin_bottom": 12}, } @@ -122,9 +123,6 @@ class HtmlRenderer(Widget): if tag == ElementType.BR: self._add_element(ElementType.BR, "") - elif tag == ElementType.UL: - self._indent_level = self._indent_level + 1 if is_start_tag else max(0, self._indent_level - 1) - elif is_start_tag or is_end_tag: # Always add content regardless of opening or closing tag close_tag() @@ -133,6 +131,10 @@ class HtmlRenderer(Widget): if is_start_tag: current_tag = tag + # increment after we add the content for the current tag + if tag == ElementType.UL: + self._indent_level = self._indent_level + 1 if is_start_tag else max(0, self._indent_level - 1) + else: current_content.append(token) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index 7877325492..056aa2d90b 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -4,10 +4,10 @@ from collections.abc import Callable from abc import ABC from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.toggle import Toggle, WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT +from openpilot.system.ui.widgets.html_render import HtmlRenderer, ElementType ITEM_BASE_WIDTH = 600 ITEM_BASE_HEIGHT = 170 @@ -258,7 +258,7 @@ class ListItem(Widget): super().__init__() self.title = title self.icon = icon - self.description = description + self._description = description self.description_visible = description_visible self.callback = callback self.action_item = action_item @@ -267,11 +267,12 @@ class ListItem(Widget): self._font = gui_app.font(FontWeight.NORMAL) self._icon_texture = gui_app.texture(os.path.join("icons", self.icon), ICON_SIZE, ICON_SIZE) if self.icon else None + self._html_renderer = HtmlRenderer(text="", text_size={ElementType.P: ITEM_DESC_FONT_SIZE}, + text_color=ITEM_DESC_TEXT_COLOR) + self.set_description(self.description) + # Cached properties for performance - self._prev_max_width: int = 0 - self._wrapped_description: str | None = None - self._prev_description: str | None = None - self._description_height: float = 0 + self._prev_description: str | None = self.description def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: super().set_touch_valid_callback(touch_callback) @@ -295,9 +296,15 @@ class ListItem(Widget): if self.description: self.description_visible = not self.description_visible - content_width = self.get_content_width(int(self._rect.width - ITEM_PADDING * 2)) + content_width = int(self._rect.width - ITEM_PADDING * 2) self._rect.height = self.get_item_height(self._font, content_width) + def _update_state(self): + # Detect changes if description is callback + new_description = self.description + if new_description != self._prev_description: + self.set_description(new_description) + def _render(self, _): if not self.is_visible: return @@ -323,16 +330,16 @@ class ListItem(Widget): rl.draw_text_ex(self._font, self.title, rl.Vector2(text_x, item_y), ITEM_TEXT_FONT_SIZE, 0, ITEM_TEXT_COLOR) # Draw description if visible - current_description = self.get_description() - if self.description_visible and current_description and self._wrapped_description: - rl.draw_text_ex( - self._font, - self._wrapped_description, - rl.Vector2(text_x, self._rect.y + ITEM_DESC_V_OFFSET), - ITEM_DESC_FONT_SIZE, - 0, - ITEM_DESC_TEXT_COLOR, + if self.description_visible: + content_width = int(self._rect.width - ITEM_PADDING * 2) + description_height = self._html_renderer.get_total_height(content_width) + description_rect = rl.Rectangle( + self._rect.x + ITEM_PADDING, + self._rect.y + ITEM_DESC_V_OFFSET, + content_width, + description_height ) + self._html_renderer.render(description_rect) # Draw right item if present if self.action_item: @@ -343,33 +350,25 @@ class ListItem(Widget): if self.callback: self.callback() - def get_description(self): - return _resolve_value(self.description, None) + def set_description(self, description: str | Callable[[], str] | None): + self._description = description + new_desc = self.description + self._html_renderer.parse_html_content(new_desc) + self._prev_description = new_desc + + @property + def description(self): + return _resolve_value(self._description, "") def get_item_height(self, font: rl.Font, max_width: int) -> float: if not self.is_visible: return 0 - current_description = self.get_description() - if self.description_visible and current_description: - if ( - not self._wrapped_description - or current_description != self._prev_description - or max_width != self._prev_max_width - ): - self._prev_max_width = max_width - self._prev_description = current_description - - wrapped_lines = wrap_text(font, current_description, ITEM_DESC_FONT_SIZE, max_width) - self._wrapped_description = "\n".join(wrapped_lines) - self._description_height = len(wrapped_lines) * ITEM_DESC_FONT_SIZE + 10 - return ITEM_BASE_HEIGHT + self._description_height - (ITEM_BASE_HEIGHT - ITEM_DESC_V_OFFSET) + ITEM_PADDING - return ITEM_BASE_HEIGHT - - def get_content_width(self, total_width: int) -> int: - if self.action_item and self.action_item.rect.width > 0: - return total_width - int(self.action_item.rect.width) - RIGHT_ITEM_PADDING - return total_width + height = float(ITEM_BASE_HEIGHT) + if self.description_visible: + description_height = self._html_renderer.get_total_height(max_width) + height += description_height - (ITEM_BASE_HEIGHT - ITEM_DESC_V_OFFSET) + ITEM_PADDING + return height def get_right_item_rect(self, item_rect: rl.Rectangle) -> rl.Rectangle: if not self.action_item: From 703f3d0573edac611881ddeeb0263bf3ff458d37 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 3 Oct 2025 22:09:00 -0700 Subject: [PATCH 106/341] disable sim test for now --- .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 57b1158be2..35ced1e38b 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -225,7 +225,7 @@ jobs: (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') || fromJSON('["ubuntu-24.04"]') }} - if: (github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + if: false # FIXME: Started to timeout recently steps: - uses: actions/checkout@v4 with: From 0eb90ecb3e4e26f72ccc62eb21e16deb989f27eb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 23:04:55 -0700 Subject: [PATCH 107/341] raylib: elide list item actions (#36262) fix --- system/ui/widgets/list_view.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index 056aa2d90b..509c49be35 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -7,6 +7,7 @@ from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.toggle import Toggle, WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT +from openpilot.system.ui.widgets.label import gui_label from openpilot.system.ui.widgets.html_render import HtmlRenderer, ElementType ITEM_BASE_WIDTH = 600 @@ -42,6 +43,10 @@ class ItemAction(Widget, ABC): self.set_rect(rl.Rectangle(0, 0, width, 0)) self._enabled_source = enabled + def get_width_hint(self) -> float: + # Return's action ideal width, 0 means use full width + return self._rect.width + def set_enabled(self, enabled: bool | Callable[[], bool]): self._enabled_source = enabled @@ -147,17 +152,14 @@ class TextAction(ItemAction): def text(self): return _resolve_value(self._text_source, "Error") - def _update_state(self): + def get_width_hint(self) -> float: text_width = measure_text_cached(self._font, self.text, ITEM_TEXT_FONT_SIZE).x - self._rect.width = int(text_width + TEXT_PADDING) + return text_width + TEXT_PADDING def _render(self, rect: rl.Rectangle) -> bool: - current_text = self.text - text_size = measure_text_cached(self._font, current_text, ITEM_TEXT_FONT_SIZE) - - text_x = rect.x + (rect.width - text_size.x) / 2 - text_y = rect.y + (rect.height - text_size.y) / 2 - rl.draw_text_ex(self._font, current_text, rl.Vector2(text_x, text_y), ITEM_TEXT_FONT_SIZE, 0, self.color) + gui_label(self._rect, self.text, font_size=ITEM_TEXT_FONT_SIZE, color=self.color, + font_weight=FontWeight.NORMAL, alignment=rl.GuiTextAlignment.TEXT_ALIGN_RIGHT, + alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE) return False def set_text(self, text: str | Callable[[], str]): @@ -374,11 +376,16 @@ class ListItem(Widget): if not self.action_item: return rl.Rectangle(0, 0, 0, 0) - right_width = self.action_item.rect.width + right_width = self.action_item.get_width_hint() if right_width == 0: # Full width action (like DualButtonAction) return rl.Rectangle(item_rect.x + ITEM_PADDING, item_rect.y, item_rect.width - (ITEM_PADDING * 2), ITEM_BASE_HEIGHT) + # Clip width to available space, never overlapping this Item's title + content_width = item_rect.width - (ITEM_PADDING * 2) + title_width = measure_text_cached(self._font, self.title, ITEM_TEXT_FONT_SIZE).x + right_width = min(content_width - title_width, right_width) + right_x = item_rect.x + item_rect.width - right_width right_y = item_rect.y return rl.Rectangle(right_x, right_y, right_width, ITEM_BASE_HEIGHT) From e423f8f605bfb521393de9ce80f13c1d281f3613 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 23:17:51 -0700 Subject: [PATCH 108/341] raylib: elide version on homescreen (#36263) * elide ver on hom * rm line * blank --- selfdrive/ui/layouts/home.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 39b39d33d9..f7f57c680d 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -8,7 +8,8 @@ from openpilot.selfdrive.ui.widgets.exp_mode_button import ExperimentalModeButto from openpilot.selfdrive.ui.widgets.prime import PrimeWidget from openpilot.selfdrive.ui.widgets.setup import SetupWidget from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, DEFAULT_TEXT_COLOR +from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos +from openpilot.system.ui.widgets.label import gui_label from openpilot.system.ui.widgets import Widget HEADER_HEIGHT = 80 @@ -126,8 +127,12 @@ class HomeLayout(Widget): def _render_header(self): font = gui_app.font(FontWeight.MEDIUM) + version_text_width = self.header_rect.width + # Update notification button if self.update_available: + version_text_width -= self.update_notif_rect.width + # Highlight if currently viewing updates highlight_color = rl.Color(75, 95, 255, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(54, 77, 239, 255) rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color) @@ -140,6 +145,8 @@ class HomeLayout(Widget): # Alert notification button if self.alert_count > 0: + version_text_width -= self.alert_notif_rect.width + # Highlight if currently viewing alerts highlight_color = rl.Color(255, 70, 70, 255) if self.current_state == HomeLayoutState.ALERTS else rl.Color(226, 44, 44, 255) rl.draw_rectangle_rounded(self.alert_notif_rect, 0.3, 10, highlight_color) @@ -151,11 +158,12 @@ class HomeLayout(Widget): rl.draw_text_ex(font, alert_text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE) # Version text (right aligned) - version_text = self._get_version_text() - text_width = measure_text_cached(gui_app.font(FontWeight.NORMAL), version_text, 48).x - version_x = self.header_rect.x + self.header_rect.width - text_width - version_y = self.header_rect.y + (self.header_rect.height - 48) // 2 - rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), version_text, rl.Vector2(int(version_x), int(version_y)), 48, 0, DEFAULT_TEXT_COLOR) + if self.update_available or self.alert_count > 0: + version_text_width -= SPACING * 1.5 + + version_rect = rl.Rectangle(self.header_rect.x + self.header_rect.width - version_text_width, self.header_rect.y, + version_text_width, self.header_rect.height) + gui_label(version_rect, self._get_version_text(), 48, rl.WHITE, alignment=rl.GuiTextAlignment.TEXT_ALIGN_RIGHT) def _render_home_content(self): self._render_left_column() From 3fd9e94a3414fae490fb5169186ffe528c012333 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 23:18:20 -0700 Subject: [PATCH 109/341] raylib: all system apps work without anything built (#36261) * all system apps work without scons * better * fix * revert * fix * dont add * huh --- system/ui/lib/wifi_manager.py | 15 ++++++++++----- system/ui/widgets/network.py | 13 ++++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index e24686566d..5594742cb3 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -16,7 +16,6 @@ from jeepney.low_level import MessageType from jeepney.wrappers import Properties from openpilot.common.swaglog import cloudlog -from openpilot.common.params import Params from openpilot.system.ui.lib.networkmanager import (NM, NM_WIRELESS_IFACE, NM_802_11_AP_SEC_PAIR_WEP40, NM_802_11_AP_SEC_PAIR_WEP104, NM_802_11_AP_SEC_GROUP_WEP40, NM_802_11_AP_SEC_GROUP_WEP104, NM_802_11_AP_SEC_KEY_MGMT_PSK, @@ -28,6 +27,11 @@ from openpilot.system.ui.lib.networkmanager import (NM, NM_WIRELESS_IFACE, NM_80 NM_DEVICE_STATE_REASON_NEW_ACTIVATION, NM_ACTIVE_CONNECTION_IFACE, NM_IP4_CONFIG_IFACE, NMDeviceState) +try: + from openpilot.common.params import Params +except Exception: + Params = None + TETHERING_IP_ADDRESS = "192.168.43.1" DEFAULT_TETHERING_PASSWORD = "swagswagcomma" SIGNAL_QUEUE_SIZE = 10 @@ -149,9 +153,10 @@ class WifiManager: self._callback_queue: list[Callable] = [] self._tethering_ssid = "weedle" - dongle_id = Params().get("DongleId") - if dongle_id: - self._tethering_ssid += "-" + dongle_id[:4] + if Params is not None: + dongle_id = Params().get("DongleId") + if dongle_id: + self._tethering_ssid += "-" + dongle_id[:4] # Callbacks self._need_auth: list[Callable[[str], None]] = [] @@ -173,7 +178,7 @@ class WifiManager: self._scan_thread.start() self._state_thread.start() - if self._tethering_ssid not in self._get_connections(): + if Params is not None and self._tethering_ssid not in self._get_connections(): self._add_tethering_connection() self._tethering_password = self._get_tethering_password() diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 8a7bd9f512..85b98f10ac 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -3,7 +3,6 @@ from functools import partial from typing import cast import pyray as rl -from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType @@ -14,8 +13,16 @@ from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.label import TextAlignment, gui_label from openpilot.system.ui.widgets.scroller import Scroller from openpilot.system.ui.widgets.list_view import ButtonAction, ListItem, MultipleButtonAction, ToggleAction, button_item, text_item -from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.selfdrive.ui.lib.prime_state import PrimeType + +# These are only used for AdvancedNetworkSettings, standalone apps just need WifiManagerUI +try: + from openpilot.common.params import Params + from openpilot.selfdrive.ui.ui_state import ui_state + from openpilot.selfdrive.ui.lib.prime_state import PrimeType +except Exception: + Params = None + ui_state = None # type: ignore + PrimeType = None # type: ignore NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 From 943aaef76a885bc337a3253b3b1cd09ecb97fa53 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 23:32:17 -0700 Subject: [PATCH 110/341] raylib: match Qt onroad alert colors (#36264) fix alert colors --- selfdrive/ui/onroad/alert_renderer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index c529694df4..e81129b137 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -26,9 +26,9 @@ SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds # Constants ALERT_COLORS = { - AlertStatus.normal: rl.Color(0, 0, 0, 235), # Black - AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 235), # Orange - AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 235), # Red + AlertStatus.normal: rl.Color(0x15, 0x15, 0x15, 0xF1), # #151515 with alpha 0xF1 + AlertStatus.userPrompt: rl.Color(0xDA, 0x6F, 0x25, 0xF1), # #DA6F25 with alpha 0xF1 + AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 0xF1), # #C92231 with alpha 0xF1 } From c88ab5cd123923f66aed2a2f74beaad5515d8ce9 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 3 Oct 2025 23:38:10 -0700 Subject: [PATCH 111/341] Switch to raylib for UI (#36238) * flip * change this --- system/manager/manager.py | 2 +- system/manager/process_config.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system/manager/manager.py b/system/manager/manager.py index 36055d8635..d97aa8b66b 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -221,7 +221,7 @@ if __name__ == "__main__": cloudlog.exception("Manager failed to start") try: - managed_processes['ui'].stop() + managed_processes['raylib_ui'].stop() except Exception: pass diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 22f159e891..55e2812efd 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -80,8 +80,8 @@ procs = [ PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), PythonProcess("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)), - PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), + NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), + PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False), From 2bc97ee23f7d97f34d2469f698f974e723e8a1df Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 4 Oct 2025 00:05:20 -0700 Subject: [PATCH 112/341] raylib: fix DM popup (#36265) * come on * try * better * better * multiple places! * debug * works * temp * whoops * wonder if this wortks * ah need this! * wtf is this when deleted? * another day no modal show event * clean * fix * ugh * need this --- selfdrive/ui/onroad/driver_camera_dialog.py | 11 +++++++++-- system/ui/lib/application.py | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/onroad/driver_camera_dialog.py b/selfdrive/ui/onroad/driver_camera_dialog.py index 6c5508ce7d..a3bd2e23e0 100644 --- a/selfdrive/ui/onroad/driver_camera_dialog.py +++ b/selfdrive/ui/onroad/driver_camera_dialog.py @@ -3,7 +3,7 @@ import pyray as rl from msgq.visionipc import VisionStreamType from openpilot.selfdrive.ui.onroad.cameraview import CameraView from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer -from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.ui_state import ui_state, device from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets.label import gui_label @@ -12,10 +12,17 @@ class DriverCameraDialog(CameraView): def __init__(self): super().__init__("camerad", VisionStreamType.VISION_STREAM_DRIVER) self.driver_state_renderer = DriverStateRenderer() + # TODO: this can grow unbounded, should be given some thought + device.add_interactive_timeout_callback(self.stop_dmonitoringmodeld) + ui_state.params.put_bool("IsDriverViewEnabled", True) + + def stop_dmonitoringmodeld(self): + ui_state.params.put_bool("IsDriverViewEnabled", False) + gui_app.set_modal_overlay(None) def _handle_mouse_release(self, _): super()._handle_mouse_release(_) - gui_app.set_modal_overlay(None) + self.stop_dmonitoringmodeld() def _render(self, rect): super()._render(rect) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index a42b1d1129..473eaa5fa8 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -178,6 +178,10 @@ class GuiApplication: self._mouse.start() def set_modal_overlay(self, overlay, callback: Callable | None = None): + if self._modal_overlay.overlay is not None: + if self._modal_overlay.callback is not None: + self._modal_overlay.callback(-1) + self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback) def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): From 7933c10c975ee24afbd2ef5554ba85005c94101a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 4 Oct 2025 00:32:49 -0700 Subject: [PATCH 113/341] raylib: font sizes from QT should match (#36237) * debug * hacks everywhere but kind of works * by font * fix sidebar * stash * test update * just use a const * just use a const * better * clean up * fix label * simplify * gpt5 is yet again garbage * rm that * clean up * rm * blank * clean up * I really don't like this but shrug * fix * fix experimental text --- selfdrive/ui/layouts/sidebar.py | 29 +++++++++++++++---------- selfdrive/ui/widgets/exp_mode_button.py | 6 ++--- system/ui/lib/application.py | 15 +++++++++++++ system/ui/lib/text_measure.py | 3 ++- system/ui/widgets/html_render.py | 10 ++++----- 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 5987d062ff..34354ecbab 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -216,14 +216,21 @@ class Sidebar(Widget): # Draw border rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.3, 10, 2, Colors.METRIC_BORDER) - # Draw label and value - labels = [metric.label, metric.value] - text_y = metric_rect.y + (metric_rect.height / 2 - len(labels) * FONT_SIZE) - for text in labels: - text_size = measure_text_cached(self._font_bold, text, FONT_SIZE) - text_y += text_size.y - text_pos = rl.Vector2( - metric_rect.x + 22 + (metric_rect.width - 22 - text_size.x) / 2, - text_y - ) - rl.draw_text_ex(self._font_bold, text, text_pos, FONT_SIZE, 0, Colors.WHITE) + label_size = measure_text_cached(self._font_bold, metric.label, FONT_SIZE) + value_size = measure_text_cached(self._font_bold, metric.value, FONT_SIZE) + text_height = label_size.y + value_size.y + + label_y = metric_rect.y + (metric_rect.height - text_height) / 2 + value_y = label_y + label_size.y + + # label + rl.draw_text_ex(self._font_bold, metric.label, rl.Vector2( + metric_rect.x + 22 + (metric_rect.width - 22 - label_size.x) / 2, + label_y + ), FONT_SIZE, 0, Colors.WHITE) + + # value + rl.draw_text_ex(self._font_bold, metric.value, rl.Vector2( + metric_rect.x + 22 + (metric_rect.width - 22 - value_size.x) / 2, + value_y + ), FONT_SIZE, 0, Colors.WHITE) diff --git a/selfdrive/ui/widgets/exp_mode_button.py b/selfdrive/ui/widgets/exp_mode_button.py index 9618768957..6fe7b6843c 100644 --- a/selfdrive/ui/widgets/exp_mode_button.py +++ b/selfdrive/ui/widgets/exp_mode_button.py @@ -1,6 +1,6 @@ import pyray as rl from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.widgets import Widget @@ -9,7 +9,7 @@ class ExperimentalModeButton(Widget): super().__init__() self.img_width = 80 - self.horizontal_padding = 50 + self.horizontal_padding = 30 self.button_height = 125 self.params = Params() @@ -51,7 +51,7 @@ class ExperimentalModeButton(Widget): # Draw text label (left aligned) text = "EXPERIMENTAL MODE ON" if self.experimental_mode else "CHILL MODE ON" text_x = rect.x + self.horizontal_padding - text_y = rect.y + rect.height / 2 - 45 // 2 # Center vertically + text_y = rect.y + rect.height / 2 - 45 * FONT_SCALE // 2 # Center vertically rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), text, rl.Vector2(int(text_x), int(text_y)), 45, 0, rl.BLACK) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 473eaa5fa8..755a335e4d 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -30,6 +30,10 @@ SCALE = float(os.getenv("SCALE", "1.0")) DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE +# Qt draws fonts accounting for ascent/descent differently, so compensate to match old styles +# The real scales for the fonts below range from 1.212 to 1.266 +FONT_SCALE = 1.242 + ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") @@ -173,6 +177,7 @@ class GuiApplication: self._target_fps = fps self._set_styles() self._load_fonts() + self._patch_text_functions() if not PC: self._mouse.start() @@ -356,6 +361,16 @@ class GuiApplication: rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_COLOR_NORMAL, rl.color_to_int(DEFAULT_TEXT_COLOR)) rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(rl.Color(50, 50, 50, 255))) + def _patch_text_functions(self): + # Wrap pyray text APIs to apply a global text size scale so our px sizes match Qt + if not hasattr(rl, "_orig_draw_text_ex"): + rl._orig_draw_text_ex = rl.draw_text_ex + + def _draw_text_ex_scaled(font, text, position, font_size, spacing, tint): + return rl._orig_draw_text_ex(font, text, position, font_size * FONT_SCALE, spacing, tint) + + rl.draw_text_ex = _draw_text_ex_scaled + def _set_log_callback(self): ffi_libc = cffi.FFI() ffi_libc.cdef(""" diff --git a/system/ui/lib/text_measure.py b/system/ui/lib/text_measure.py index c172f94251..fcb7b25ccd 100644 --- a/system/ui/lib/text_measure.py +++ b/system/ui/lib/text_measure.py @@ -1,4 +1,5 @@ import pyray as rl +from openpilot.system.ui.lib.application import FONT_SCALE _cache: dict[int, rl.Vector2] = {} @@ -9,6 +10,6 @@ def measure_text_cached(font: rl.Font, text: str, font_size: int, spacing: int = if key in _cache: return _cache[key] - result = rl.measure_text_ex(font, text, font_size, spacing) # noqa: TID251 + result = rl.measure_text_ex(font, text, font_size * FONT_SCALE, spacing) # noqa: TID251 _cache[key] = result return result diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index 2c3eeb3793..91a1ccbbc4 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -3,7 +3,7 @@ import pyray as rl from dataclasses import dataclass from enum import Enum from typing import Any -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget @@ -176,8 +176,8 @@ class HtmlRenderer(Widget): wrapped_lines = wrap_text(font, element.content, element.font_size, int(content_width)) for line in wrapped_lines: - if current_y < rect.y - element.font_size: - current_y += element.font_size * element.line_height + if current_y < rect.y - element.font_size * FONT_SCALE: + current_y += element.font_size * FONT_SCALE * element.line_height continue if current_y > rect.y + rect.height: @@ -186,7 +186,7 @@ class HtmlRenderer(Widget): text_x = rect.x + (max(element.indent_level - 1, 0) * LIST_INDENT_PX) rl.draw_text_ex(font, line, rl.Vector2(text_x + padding, current_y), element.font_size, 0, self._text_color) - current_y += element.font_size * element.line_height + current_y += element.font_size * FONT_SCALE * element.line_height # Apply bottom margin current_y += element.margin_bottom @@ -210,7 +210,7 @@ class HtmlRenderer(Widget): wrapped_lines = wrap_text(font, element.content, element.font_size, int(usable_width)) for _ in wrapped_lines: - total_height += element.font_size * element.line_height + total_height += element.font_size * FONT_SCALE * element.line_height total_height += element.margin_bottom From ebe47a580ccea938d5cb6d3140e5f6a2760cdc76 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 4 Oct 2025 01:04:05 -0700 Subject: [PATCH 114/341] raylib: fix registration box height --- selfdrive/ui/widgets/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index bf6d113f62..e4e44b7d04 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -28,7 +28,7 @@ class SetupWidget(Widget): def _render_registration(self, rect: rl.Rectangle): """Render registration prompt.""" - rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 590), 0.02, 20, rl.Color(51, 51, 51, 255)) + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 630), 0.02, 20, rl.Color(51, 51, 51, 255)) x = rect.x + 64 y = rect.y + 48 From 586e49cab35566258468abae75ff4152a5c3a1ac Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 4 Oct 2025 01:04:20 -0700 Subject: [PATCH 115/341] Revert "Switch to raylib for UI (#36238)" This reverts commit c88ab5cd123923f66aed2a2f74beaad5515d8ce9. --- system/manager/manager.py | 2 +- system/manager/process_config.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system/manager/manager.py b/system/manager/manager.py index d97aa8b66b..36055d8635 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -221,7 +221,7 @@ if __name__ == "__main__": cloudlog.exception("Manager failed to start") try: - managed_processes['raylib_ui'].stop() + managed_processes['ui'].stop() except Exception: pass diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 55e2812efd..22f159e891 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -80,8 +80,8 @@ procs = [ PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), PythonProcess("sensord", "system.sensord.sensord", only_onroad, enabled=not PC), - NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), - PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, watchdog_max_dt=(5 if not PC else None)), + NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)), + PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False), From cc7ecd53c7135d1a9ef53bddc9881486de00b3ce Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 4 Oct 2025 02:45:40 -0700 Subject: [PATCH 116/341] raylib: bump commit --- third_party/raylib/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/third_party/raylib/build.sh b/third_party/raylib/build.sh index 544feb1c59..f786213da7 100755 --- a/third_party/raylib/build.sh +++ b/third_party/raylib/build.sh @@ -30,7 +30,7 @@ fi cd raylib_repo -COMMIT=${1:-39e6d8b52db159ba2ab3214b46d89a8069e09394} +COMMIT=${1:-63ea627abb4488adc9c7b5e2f8dcc77a7d6bfa3b} git fetch origin $COMMIT git reset --hard $COMMIT git clean -xdff . @@ -57,7 +57,7 @@ if [ -f /TICI ]; then cd raylib_python_repo - BINDINGS_COMMIT="ef8141c7979d5fa630ef4108605fc221f07d8cb7" + BINDINGS_COMMIT="ab0191f445272ca66758f9dd345b7395518d6a77" git fetch origin $BINDINGS_COMMIT git reset --hard $BINDINGS_COMMIT git clean -xdff . From 31801a73121b729d11a7dd4a7ccd9698f4d8fc92 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 4 Oct 2025 02:47:25 -0700 Subject: [PATCH 117/341] no more wayland for installer --- selfdrive/ui/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index ba9fa8b7a6..2b57be7227 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -71,7 +71,7 @@ if GetOption('extras'): raylib_libs = common + ["raylib"] if arch == "larch64": - raylib_libs += ["GLESv2", "wayland-client", "wayland-egl", "EGL"] + raylib_libs += ["GLESv2", "EGL", "gdb", "drm"] else: raylib_libs += ["GL"] From 35296a869286f7b19a9a5da9b2b7b36317d0717e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 6 Oct 2025 00:56:13 -0700 Subject: [PATCH 118/341] flip setting order (#36266) flip --- selfdrive/ui/layouts/settings/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index c41c8b0a26..62abd2b998 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -55,8 +55,8 @@ class DeviceLayout(Widget): self._pair_device_btn, button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt), - regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide), + regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt), ] From a7fe9db773e126147c84c91015a7869e4ebb0fea Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 6 Oct 2025 16:37:50 -0700 Subject: [PATCH 119/341] fix installer build --- selfdrive/ui/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 2b57be7227..7ede8c394a 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -71,7 +71,7 @@ if GetOption('extras'): raylib_libs = common + ["raylib"] if arch == "larch64": - raylib_libs += ["GLESv2", "EGL", "gdb", "drm"] + raylib_libs += ["GLESv2", "EGL", "gbm", "drm"] else: raylib_libs += ["GL"] From e1912fa5bef1519451a9f082abaeb4fc2364e1cb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 8 Oct 2025 03:51:37 -0700 Subject: [PATCH 120/341] raylib: speed up polygon shader (#36275) * actually works * fix shader grad * switch * our own triangulate * this is amazing * ok 100 is too much for 3x. 10? * fix colors * review intern chad * fmt * rm for the line count * bye * rm * see the diff * start to revert nulleffect * fix * fix * always feather * aliasing doesn't seem necessary * aliasing doesn't seem necessary * fix lane lines disappearing halfway up due to buggy deduping -- very simple triangulation function takes ~same CPU time + same GPU utilization on PC (nvidia-smi) * remove old * even simpler triangulate * this is useless * more revert * split color out again * clean up ai bs * back to original names * more clean up * stop it * this limiting logic split out feels more even // less super dense * typing * clean up a little * move to get grad color * RM * flip * document * clean up * clean up * clean * clean up * not a "state" * clean up * that did nothing * cmt --- selfdrive/ui/onroad/model_renderer.py | 32 +-- system/ui/lib/shader_polygon.py | 277 ++++++++++---------------- 2 files changed, 118 insertions(+), 191 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index a770c0f92f..3877da81c5 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -8,7 +8,7 @@ from openpilot.common.params import Params from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app -from openpilot.system.ui.lib.shader_polygon import draw_polygon +from openpilot.system.ui.lib.shader_polygon import draw_polygon, Gradient from openpilot.system.ui.widgets import Widget CLIP_MARGIN = 500 @@ -66,12 +66,12 @@ class ModelRenderer(Widget): self._transform_dirty = True self._clip_region = None - self._exp_gradient = { - 'start': (0.0, 1.0), # Bottom of path - 'end': (0.0, 0.0), # Top of path - 'colors': [], - 'stops': [], - } + self._exp_gradient = Gradient( + start=(0.0, 1.0), # Bottom of path + end=(0.0, 0.0), # Top of path + colors=[], + stops=[], + ) # Get longitudinal control setting from car parameters if car_params := Params().get("CarParams"): @@ -226,8 +226,8 @@ class ModelRenderer(Widget): i += 1 + (1 if (i + 2) < max_len else 0) # Store the gradient in the path object - self._exp_gradient['colors'] = segment_colors - self._exp_gradient['stops'] = gradient_stops + self._exp_gradient.colors = segment_colors + self._exp_gradient.stops = gradient_stops def _update_lead_vehicle(self, d_rel, v_rel, point, rect): speed_buff, lead_buff = 10.0, 40.0 @@ -281,7 +281,7 @@ class ModelRenderer(Widget): if self._experimental_mode: # Draw with acceleration coloring - if len(self._exp_gradient['colors']) > 1: + if len(self._exp_gradient.colors) > 1: draw_polygon(self._rect, self._path.projected_points, gradient=self._exp_gradient) else: draw_polygon(self._rect, self._path.projected_points, rl.Color(255, 255, 255, 30)) @@ -289,12 +289,12 @@ class ModelRenderer(Widget): # Blend throttle/no throttle colors based on transition blend_factor = round(self._blend_filter.x * 100) / 100 blended_colors = self._blend_colors(NO_THROTTLE_COLORS, THROTTLE_COLORS, blend_factor) - gradient = { - 'start': (0.0, 1.0), # Bottom of path - 'end': (0.0, 0.0), # Top of path - 'colors': blended_colors, - 'stops': [0.0, 0.5, 1.0], - } + gradient = Gradient( + start=(0.0, 1.0), # Bottom of path + end=(0.0, 0.0), # Top of path + colors=blended_colors, + stops=[0.0, 0.5, 1.0], + ) draw_polygon(self._rect, self._path.projected_points, gradient=gradient) def _draw_lead_indicator(self): diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index d03ad5d8c6..44c26c80e5 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -1,9 +1,33 @@ import platform import pyray as rl import numpy as np -from typing import Any +from dataclasses import dataclass +from typing import Any, Optional, cast +from openpilot.system.ui.lib.application import gui_app + +MAX_GRADIENT_COLORS = 15 # includes stops as well + + +@dataclass +class Gradient: + start: tuple[float, float] + end: tuple[float, float] + colors: list[rl.Color] + stops: list[float] + + def __post_init__(self): + if len(self.colors) > MAX_GRADIENT_COLORS: + self.colors = self.colors[:MAX_GRADIENT_COLORS] + print(f"Warning: Gradient colors truncated to {MAX_GRADIENT_COLORS} entries") + + if len(self.stops) > MAX_GRADIENT_COLORS: + self.stops = self.stops[:MAX_GRADIENT_COLORS] + print(f"Warning: Gradient stops truncated to {MAX_GRADIENT_COLORS} entries") + + if not len(self.stops): + color_count = min(len(self.colors), MAX_GRADIENT_COLORS) + self.stops = [i / max(1, color_count - 1) for i in range(color_count)] -MAX_GRADIENT_COLORS = 15 VERSION = """ #version 300 es @@ -18,100 +42,43 @@ FRAGMENT_SHADER = VERSION + """ in vec2 fragTexCoord; out vec4 finalColor; -uniform vec2 points[100]; -uniform int pointCount; uniform vec4 fillColor; -uniform vec2 resolution; +// Gradient line defined in *screen pixels* uniform int useGradient; -uniform vec2 gradientStart; -uniform vec2 gradientEnd; +uniform vec2 gradientStart; // e.g. vec2(0, 0) +uniform vec2 gradientEnd; // e.g. vec2(0, screenHeight) uniform vec4 gradientColors[15]; uniform float gradientStops[15]; uniform int gradientColorCount; -vec4 getGradientColor(vec2 pos) { - vec2 gradientDir = gradientEnd - gradientStart; - float gradientLength = length(gradientDir); - if (gradientLength < 0.001) return gradientColors[0]; +vec4 getGradientColor(vec2 p) { + // Compute t from screen-space position + vec2 d = gradientStart - gradientEnd; + float len2 = max(dot(d, d), 1e-6); + float t = clamp(dot(p - gradientEnd, d) / len2, 0.0, 1.0); - vec2 normalizedDir = gradientDir / gradientLength; - float t = clamp(dot(pos - gradientStart, normalizedDir) / gradientLength, 0.0, 1.0); + // Clamp to range + float t0 = gradientStops[0]; + float tn = gradientStops[gradientColorCount-1]; + if (t <= t0) return gradientColors[0]; + if (t >= tn) return gradientColors[gradientColorCount-1]; - if (gradientColorCount <= 1) return gradientColors[0]; - - // handle t before first / after last stop - if (t <= gradientStops[0]) return gradientColors[0]; - if (t >= gradientStops[gradientColorCount-1]) return gradientColors[gradientColorCount-1]; for (int i = 0; i < gradientColorCount - 1; i++) { - if (t >= gradientStops[i] && t <= gradientStops[i+1]) { - float segmentT = (t - gradientStops[i]) / (gradientStops[i+1] - gradientStops[i]); - return mix(gradientColors[i], gradientColors[i+1], segmentT); + float a = gradientStops[i]; + float b = gradientStops[i+1]; + if (t >= a && t <= b) { + float k = (t - a) / max(b - a, 1e-6); + return mix(gradientColors[i], gradientColors[i+1], k); } } return gradientColors[gradientColorCount-1]; } -bool isPointInsidePolygon(vec2 p) { - if (pointCount < 3) return false; - int crossings = 0; - for (int i = 0, j = pointCount - 1; i < pointCount; j = i++) { - vec2 pi = points[i]; - vec2 pj = points[j]; - if (distance(pi, pj) < 0.001) continue; - if (((pi.y > p.y) != (pj.y > p.y)) && - (p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y + 0.001) + pi.x)) { - crossings++; - } - } - return (crossings & 1) == 1; -} - -float distanceToEdge(vec2 p) { - float minDist = 1000.0; - - for (int i = 0, j = pointCount - 1; i < pointCount; j = i++) { - vec2 edge0 = points[j]; - vec2 edge1 = points[i]; - - if (distance(edge0, edge1) < 0.0001) continue; - - vec2 v1 = p - edge0; - vec2 v2 = edge1 - edge0; - float l2 = dot(v2, v2); - - if (l2 < 0.0001) { - float dist = length(v1); - minDist = min(minDist, dist); - continue; - } - - float t = clamp(dot(v1, v2) / l2, 0.0, 1.0); - vec2 projection = edge0 + t * v2; - float dist = length(p - projection); - minDist = min(minDist, dist); - } - - return minDist; -} - void main() { - vec2 pixel = fragTexCoord * resolution; - - bool inside = isPointInsidePolygon(pixel); - float sd = (inside ? 1.0 : -1.0) * distanceToEdge(pixel); - - // ~1 pixel wide anti-aliasing - float w = max(0.75, fwidth(sd)); - - float alpha = smoothstep(-w, w, sd); - if (alpha > 0.0){ - vec4 color = useGradient == 1 ? getGradientColor(pixel) : fillColor; - finalColor = vec4(color.rgb, color.a * alpha); - } else { - discard; - } + // TODO: do proper antialiasing + finalColor = useGradient == 1 ? getGradientColor(gl_FragCoord.xy) : fillColor; } """ @@ -149,14 +116,10 @@ class ShaderState: self.initialized = False self.shader = None - self.white_texture = None # Shader uniform locations self.locations = { - 'pointCount': None, 'fillColor': None, - 'resolution': None, - 'points': None, 'useGradient': None, 'gradientStart': None, 'gradientEnd': None, @@ -167,12 +130,8 @@ class ShaderState: } # Pre-allocated FFI objects - self.point_count_ptr = rl.ffi.new("int[]", [0]) - self.resolution_ptr = rl.ffi.new("float[]", [0.0, 0.0]) self.fill_color_ptr = rl.ffi.new("float[]", [0.0, 0.0, 0.0, 0.0]) self.use_gradient_ptr = rl.ffi.new("int[]", [0]) - self.gradient_start_ptr = rl.ffi.new("float[]", [0.0, 0.0]) - self.gradient_end_ptr = rl.ffi.new("float[]", [0.0, 0.0]) self.color_count_ptr = rl.ffi.new("int[]", [0]) self.gradient_colors_ptr = rl.ffi.new("float[]", MAX_GRADIENT_COLORS * 4) self.gradient_stops_ptr = rl.ffi.new("float[]", MAX_GRADIENT_COLORS) @@ -183,30 +142,19 @@ class ShaderState: self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAGMENT_SHADER) - # Create and cache white texture - white_img = rl.gen_image_color(2, 2, rl.WHITE) - self.white_texture = rl.load_texture_from_image(white_img) - rl.set_texture_filter(self.white_texture, rl.TEXTURE_FILTER_BILINEAR) - rl.unload_image(white_img) - # Cache all uniform locations for uniform in self.locations.keys(): self.locations[uniform] = rl.get_shader_location(self.shader, uniform) - # Setup default MVP matrix - mvp_ptr = rl.ffi.new("float[16]", [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]) - rl.set_shader_value_matrix(self.shader, self.locations['mvp'], rl.Matrix(*mvp_ptr)) + # Orthographic MVP (origin top-left) + proj = rl.matrix_ortho(0, gui_app.width, gui_app.height, 0, -1, 1) + rl.set_shader_value_matrix(self.shader, self.locations['mvp'], proj) self.initialized = True def cleanup(self): if not self.initialized: return - - if self.white_texture: - rl.unload_texture(self.white_texture) - self.white_texture = None - if self.shader: rl.unload_shader(self.shader) self.shader = None @@ -214,103 +162,82 @@ class ShaderState: self.initialized = False -def _configure_shader_color(state, color, gradient, clipped_rect, original_rect): - use_gradient = 1 if gradient else 0 +def _configure_shader_color(state: ShaderState, color: Optional[rl.Color], # noqa: UP045 + gradient: Gradient | None, origin_rect: rl.Rectangle): + assert (color is not None) != (gradient is not None), "Either color or gradient must be provided" + + use_gradient = 1 if (gradient is not None and len(gradient.colors) >= 1) else 0 state.use_gradient_ptr[0] = use_gradient rl.set_shader_value(state.shader, state.locations['useGradient'], state.use_gradient_ptr, UNIFORM_INT) if use_gradient: - start = np.array(gradient['start']) * np.array([original_rect.width, original_rect.height]) + np.array([original_rect.x, original_rect.y]) - end = np.array(gradient['end']) * np.array([original_rect.width, original_rect.height]) + np.array([original_rect.x, original_rect.y]) - start = start - np.array([clipped_rect.x, clipped_rect.y]) - end = end - np.array([clipped_rect.x, clipped_rect.y]) - state.gradient_start_ptr[0:2] = start.astype(np.float32) - state.gradient_end_ptr[0:2] = end.astype(np.float32) - rl.set_shader_value(state.shader, state.locations['gradientStart'], state.gradient_start_ptr, UNIFORM_VEC2) - rl.set_shader_value(state.shader, state.locations['gradientEnd'], state.gradient_end_ptr, UNIFORM_VEC2) + gradient = cast(Gradient, gradient) + state.color_count_ptr[0] = len(gradient.colors) + for i in range(len(gradient.colors)): + c = gradient.colors[i] + base = i * 4 + state.gradient_colors_ptr[base:base + 4] = [c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0] + rl.set_shader_value_v(state.shader, state.locations['gradientColors'], state.gradient_colors_ptr, UNIFORM_VEC4, len(gradient.colors)) - colors = gradient['colors'] - color_count = min(len(colors), MAX_GRADIENT_COLORS) - state.color_count_ptr[0] = color_count - for i, c in enumerate(colors[:color_count]): - base_idx = i * 4 - state.gradient_colors_ptr[base_idx:base_idx+4] = [c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0] - rl.set_shader_value_v(state.shader, state.locations['gradientColors'], state.gradient_colors_ptr, UNIFORM_VEC4, color_count) - - stops = gradient.get('stops', [i / max(1, color_count - 1) for i in range(color_count)]) - stops = np.clip(stops[:color_count], 0.0, 1.0) - state.gradient_stops_ptr[0:color_count] = stops - rl.set_shader_value_v(state.shader, state.locations['gradientStops'], state.gradient_stops_ptr, UNIFORM_FLOAT, color_count) + for i in range(len(gradient.stops)): + s = float(gradient.stops[i]) + state.gradient_stops_ptr[i] = 0.0 if s < 0.0 else 1.0 if s > 1.0 else s + rl.set_shader_value_v(state.shader, state.locations['gradientStops'], state.gradient_stops_ptr, UNIFORM_FLOAT, len(gradient.stops)) rl.set_shader_value(state.shader, state.locations['gradientColorCount'], state.color_count_ptr, UNIFORM_INT) + + # Map normalized start/end to screen pixels + start_vec = rl.Vector2(origin_rect.x + gradient.start[0] * origin_rect.width, origin_rect.y + gradient.start[1] * origin_rect.height) + end_vec = rl.Vector2(origin_rect.x + gradient.end[0] * origin_rect.width, origin_rect.y + gradient.end[1] * origin_rect.height) + rl.set_shader_value(state.shader, state.locations['gradientStart'], start_vec, UNIFORM_VEC2) + rl.set_shader_value(state.shader, state.locations['gradientEnd'], end_vec, UNIFORM_VEC2) else: color = color or rl.WHITE state.fill_color_ptr[0:4] = [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0] rl.set_shader_value(state.shader, state.locations['fillColor'], state.fill_color_ptr, UNIFORM_VEC4) -def draw_polygon(origin_rect: rl.Rectangle, points: np.ndarray, color=None, gradient=None): - """ - Draw a complex polygon using shader-based even-odd fill rule +def triangulate(pts: np.ndarray) -> list[tuple[float, float]]: + """Only supports simple polygons with two chains (ribbon).""" - Args: - rect: Rectangle defining the drawing area - points: numpy array of (x,y) points defining the polygon - color: Solid fill color (rl.Color) - gradient: Dict with gradient parameters: - { - 'start': (x1, y1), # Start point (normalized 0-1) - 'end': (x2, y2), # End point (normalized 0-1) - 'colors': [rl.Color], # List of colors at stops - 'stops': [float] # List of positions (0-1) - } + # TODO: consider deduping close screenspace points + # interleave points to produce a triangle strip + assert len(pts) % 2 == 0, "Interleaving expects even number of points" + + tri_strip = [] + for i in range(len(pts) // 2): + tri_strip.append(pts[i]) + tri_strip.append(pts[-i - 1]) + + return cast(list, np.array(tri_strip).tolist()) + + +def draw_polygon(origin_rect: rl.Rectangle, points: np.ndarray, + color: Optional[rl.Color] = None, gradient: Gradient | None = None): # noqa: UP045 + + """ + Draw a ribbon polygon (two chains) with a triangle strip and gradient. + - Input must be [L0..Lk-1, Rk-1..R0], even count, no crossings/holes. """ if len(points) < 3: return + # Initialize shader on-demand state = ShaderState.get_instance() - if not state.initialized: - state.initialize() + state.initialize() - # Find bounding box - min_xy = np.min(points, axis=0) - max_xy = np.max(points, axis=0) - clip_x = max(origin_rect.x, min_xy[0]) - clip_y = max(origin_rect.y, min_xy[1]) - clip_right = min(origin_rect.x + origin_rect.width, max_xy[0]) - clip_bottom = min(origin_rect.y + origin_rect.height, max_xy[1]) + # Ensure (N,2) float32 contiguous array + pts = np.ascontiguousarray(points, dtype=np.float32) + assert pts.ndim == 2 and pts.shape[1] == 2, "points must be (N,2)" - # Check if polygon is completely off-screen - if clip_x >= clip_right or clip_y >= clip_bottom: - return + # Configure gradient shader + _configure_shader_color(state, color, gradient, origin_rect) - clipped_rect = rl.Rectangle(clip_x, clip_y, clip_right - clip_x, clip_bottom - clip_y) + # Triangulate via interleaving + tri_strip = triangulate(pts) - # Transform points relative to the CLIPPED area - transformed_points = points - np.array([clip_x, clip_y]) - - # Set shader values - state.point_count_ptr[0] = len(transformed_points) - rl.set_shader_value(state.shader, state.locations['pointCount'], state.point_count_ptr, UNIFORM_INT) - - state.resolution_ptr[0:2] = [clipped_rect.width, clipped_rect.height] - rl.set_shader_value(state.shader, state.locations['resolution'], state.resolution_ptr, UNIFORM_VEC2) - - flat_points = np.ascontiguousarray(transformed_points.flatten().astype(np.float32)) - points_ptr = rl.ffi.cast("float *", flat_points.ctypes.data) - rl.set_shader_value_v(state.shader, state.locations['points'], points_ptr, UNIFORM_VEC2, len(transformed_points)) - - _configure_shader_color(state, color, gradient, clipped_rect, origin_rect) - - # Render + # Draw strip, color here doesn't matter rl.begin_shader_mode(state.shader) - rl.draw_texture_pro( - state.white_texture, - rl.Rectangle(0, 0, 2, 2), - clipped_rect, - rl.Vector2(0, 0), - 0.0, - rl.WHITE, - ) + rl.draw_triangle_strip(tri_strip, len(tri_strip), rl.WHITE) rl.end_shader_mode() From e62781cccbbbd4eeed89c3c18a8873b90dccdc74 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 8 Oct 2025 04:05:49 -0700 Subject: [PATCH 121/341] Revert "raylib: font sizes from QT should match (#36237)" This reverts commit 7933c10c975ee24afbd2ef5554ba85005c94101a. --- selfdrive/ui/layouts/sidebar.py | 29 ++++++++++--------------- selfdrive/ui/widgets/exp_mode_button.py | 6 ++--- system/ui/lib/application.py | 15 ------------- system/ui/lib/text_measure.py | 3 +-- system/ui/widgets/html_render.py | 10 ++++----- 5 files changed, 20 insertions(+), 43 deletions(-) diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 34354ecbab..5987d062ff 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -216,21 +216,14 @@ class Sidebar(Widget): # Draw border rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.3, 10, 2, Colors.METRIC_BORDER) - label_size = measure_text_cached(self._font_bold, metric.label, FONT_SIZE) - value_size = measure_text_cached(self._font_bold, metric.value, FONT_SIZE) - text_height = label_size.y + value_size.y - - label_y = metric_rect.y + (metric_rect.height - text_height) / 2 - value_y = label_y + label_size.y - - # label - rl.draw_text_ex(self._font_bold, metric.label, rl.Vector2( - metric_rect.x + 22 + (metric_rect.width - 22 - label_size.x) / 2, - label_y - ), FONT_SIZE, 0, Colors.WHITE) - - # value - rl.draw_text_ex(self._font_bold, metric.value, rl.Vector2( - metric_rect.x + 22 + (metric_rect.width - 22 - value_size.x) / 2, - value_y - ), FONT_SIZE, 0, Colors.WHITE) + # Draw label and value + labels = [metric.label, metric.value] + text_y = metric_rect.y + (metric_rect.height / 2 - len(labels) * FONT_SIZE) + for text in labels: + text_size = measure_text_cached(self._font_bold, text, FONT_SIZE) + text_y += text_size.y + text_pos = rl.Vector2( + metric_rect.x + 22 + (metric_rect.width - 22 - text_size.x) / 2, + text_y + ) + rl.draw_text_ex(self._font_bold, text, text_pos, FONT_SIZE, 0, Colors.WHITE) diff --git a/selfdrive/ui/widgets/exp_mode_button.py b/selfdrive/ui/widgets/exp_mode_button.py index 6fe7b6843c..9618768957 100644 --- a/selfdrive/ui/widgets/exp_mode_button.py +++ b/selfdrive/ui/widgets/exp_mode_button.py @@ -1,6 +1,6 @@ import pyray as rl from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget @@ -9,7 +9,7 @@ class ExperimentalModeButton(Widget): super().__init__() self.img_width = 80 - self.horizontal_padding = 30 + self.horizontal_padding = 50 self.button_height = 125 self.params = Params() @@ -51,7 +51,7 @@ class ExperimentalModeButton(Widget): # Draw text label (left aligned) text = "EXPERIMENTAL MODE ON" if self.experimental_mode else "CHILL MODE ON" text_x = rect.x + self.horizontal_padding - text_y = rect.y + rect.height / 2 - 45 * FONT_SCALE // 2 # Center vertically + text_y = rect.y + rect.height / 2 - 45 // 2 # Center vertically rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), text, rl.Vector2(int(text_x), int(text_y)), 45, 0, rl.BLACK) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 755a335e4d..473eaa5fa8 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -30,10 +30,6 @@ SCALE = float(os.getenv("SCALE", "1.0")) DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE -# Qt draws fonts accounting for ascent/descent differently, so compensate to match old styles -# The real scales for the fonts below range from 1.212 to 1.266 -FONT_SCALE = 1.242 - ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") @@ -177,7 +173,6 @@ class GuiApplication: self._target_fps = fps self._set_styles() self._load_fonts() - self._patch_text_functions() if not PC: self._mouse.start() @@ -361,16 +356,6 @@ class GuiApplication: rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_COLOR_NORMAL, rl.color_to_int(DEFAULT_TEXT_COLOR)) rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(rl.Color(50, 50, 50, 255))) - def _patch_text_functions(self): - # Wrap pyray text APIs to apply a global text size scale so our px sizes match Qt - if not hasattr(rl, "_orig_draw_text_ex"): - rl._orig_draw_text_ex = rl.draw_text_ex - - def _draw_text_ex_scaled(font, text, position, font_size, spacing, tint): - return rl._orig_draw_text_ex(font, text, position, font_size * FONT_SCALE, spacing, tint) - - rl.draw_text_ex = _draw_text_ex_scaled - def _set_log_callback(self): ffi_libc = cffi.FFI() ffi_libc.cdef(""" diff --git a/system/ui/lib/text_measure.py b/system/ui/lib/text_measure.py index fcb7b25ccd..c172f94251 100644 --- a/system/ui/lib/text_measure.py +++ b/system/ui/lib/text_measure.py @@ -1,5 +1,4 @@ import pyray as rl -from openpilot.system.ui.lib.application import FONT_SCALE _cache: dict[int, rl.Vector2] = {} @@ -10,6 +9,6 @@ def measure_text_cached(font: rl.Font, text: str, font_size: int, spacing: int = if key in _cache: return _cache[key] - result = rl.measure_text_ex(font, text, font_size * FONT_SCALE, spacing) # noqa: TID251 + result = rl.measure_text_ex(font, text, font_size, spacing) # noqa: TID251 _cache[key] = result return result diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index 91a1ccbbc4..2c3eeb3793 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -3,7 +3,7 @@ import pyray as rl from dataclasses import dataclass from enum import Enum from typing import Any -from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget @@ -176,8 +176,8 @@ class HtmlRenderer(Widget): wrapped_lines = wrap_text(font, element.content, element.font_size, int(content_width)) for line in wrapped_lines: - if current_y < rect.y - element.font_size * FONT_SCALE: - current_y += element.font_size * FONT_SCALE * element.line_height + if current_y < rect.y - element.font_size: + current_y += element.font_size * element.line_height continue if current_y > rect.y + rect.height: @@ -186,7 +186,7 @@ class HtmlRenderer(Widget): text_x = rect.x + (max(element.indent_level - 1, 0) * LIST_INDENT_PX) rl.draw_text_ex(font, line, rl.Vector2(text_x + padding, current_y), element.font_size, 0, self._text_color) - current_y += element.font_size * FONT_SCALE * element.line_height + current_y += element.font_size * element.line_height # Apply bottom margin current_y += element.margin_bottom @@ -210,7 +210,7 @@ class HtmlRenderer(Widget): wrapped_lines = wrap_text(font, element.content, element.font_size, int(usable_width)) for _ in wrapped_lines: - total_height += element.font_size * FONT_SCALE * element.line_height + total_height += element.font_size * element.line_height total_height += element.margin_bottom From 9f32f217e665e0f7162f88be39381da87ba645d9 Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:26:53 -0700 Subject: [PATCH 122/341] Latcontrol: type annotate update inputs and clip_curvature output (#36282) --- selfdrive/controls/lib/drive_helpers.py | 2 +- selfdrive/controls/lib/latcontrol.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index e28fa3021c..bf6dd04f60 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -22,7 +22,7 @@ def smooth_value(val, prev_val, tau, dt=DT_MDL): alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1 return alpha * val + (1 - alpha) * prev_val -def clip_curvature(v_ego, prev_curvature, new_curvature, roll): +def clip_curvature(v_ego, prev_curvature, new_curvature, roll) -> tuple[float, bool]: # This function respects ISO lateral jerk and acceleration limits + a max curvature v_ego = max(v_ego, MIN_SPEED) max_curvature_rate = MAX_LATERAL_JERK / (v_ego ** 2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755 diff --git a/selfdrive/controls/lib/latcontrol.py b/selfdrive/controls/lib/latcontrol.py index 2a8b873e2e..7cd04240d0 100644 --- a/selfdrive/controls/lib/latcontrol.py +++ b/selfdrive/controls/lib/latcontrol.py @@ -15,7 +15,7 @@ class LatControl(ABC): self.steer_max = 1.0 @abstractmethod - def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited): + def update(self, active: bool, CS, VM, params, steer_limited_by_safety: bool, desired_curvature: float, calibrated_pose, curvature_limited: bool): pass def reset(self): From 2deb4e6f6577908f3befca0ce940e9f8de69292e Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:39:05 -0700 Subject: [PATCH 123/341] Lateral controllers: pass dt (delta time) explictly (#36281) --- selfdrive/car/tests/test_car_interfaces.py | 6 +++--- selfdrive/controls/controlsd.py | 8 ++++---- selfdrive/controls/lib/latcontrol.py | 7 +++---- selfdrive/controls/lib/latcontrol_angle.py | 4 ++-- selfdrive/controls/lib/latcontrol_pid.py | 4 ++-- selfdrive/controls/lib/latcontrol_torque.py | 4 ++-- selfdrive/controls/tests/test_latcontrol.py | 3 ++- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index c40443d7e7..24d2faa0db 100644 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -54,8 +54,8 @@ class TestCarInterfaces: # hypothesis also slows down significantly with just one more message draw LongControl(car_params) if car_params.steerControlType == CarParams.SteerControlType.angle: - LatControlAngle(car_params, car_interface) + LatControlAngle(car_params, car_interface, DT_CTRL) elif car_params.lateralTuning.which() == 'pid': - LatControlPID(car_params, car_interface) + LatControlPID(car_params, car_interface, DT_CTRL) elif car_params.lateralTuning.which() == 'torque': - LatControlTorque(car_params, car_interface) + LatControlTorque(car_params, car_interface, DT_CTRL) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 029d16e59e..4442e6cbe4 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -6,7 +6,7 @@ from cereal import car, log import cereal.messaging as messaging from openpilot.common.constants import CV from openpilot.common.params import Params -from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper +from openpilot.common.realtime import config_realtime_process, DT_CTRL, Priority, Ratekeeper from openpilot.common.swaglog import cloudlog from opendbc.car.car_helpers import interfaces @@ -51,11 +51,11 @@ class Controls: self.VM = VehicleModel(self.CP) self.LaC: LatControl if self.CP.steerControlType == car.CarParams.SteerControlType.angle: - self.LaC = LatControlAngle(self.CP, self.CI) + self.LaC = LatControlAngle(self.CP, self.CI, DT_CTRL) elif self.CP.lateralTuning.which() == 'pid': - self.LaC = LatControlPID(self.CP, self.CI) + self.LaC = LatControlPID(self.CP, self.CI, DT_CTRL) elif self.CP.lateralTuning.which() == 'torque': - self.LaC = LatControlTorque(self.CP, self.CI) + self.LaC = LatControlTorque(self.CP, self.CI, DT_CTRL) def update(self): self.sm.update(15) diff --git a/selfdrive/controls/lib/latcontrol.py b/selfdrive/controls/lib/latcontrol.py index 7cd04240d0..a97ed3e54e 100644 --- a/selfdrive/controls/lib/latcontrol.py +++ b/selfdrive/controls/lib/latcontrol.py @@ -1,12 +1,11 @@ import numpy as np from abc import abstractmethod, ABC -from openpilot.common.realtime import DT_CTRL - class LatControl(ABC): - def __init__(self, CP, CI): - self.sat_count_rate = 1.0 * DT_CTRL + def __init__(self, CP, CI, dt): + self.dt = dt + self.sat_count_rate = 1.0 * self.dt self.sat_limit = CP.steerLimitTimer self.sat_count = 0. self.sat_check_min_speed = 10. diff --git a/selfdrive/controls/lib/latcontrol_angle.py b/selfdrive/controls/lib/latcontrol_angle.py index ac35151487..cea9f74814 100644 --- a/selfdrive/controls/lib/latcontrol_angle.py +++ b/selfdrive/controls/lib/latcontrol_angle.py @@ -8,8 +8,8 @@ STEER_ANGLE_SATURATION_THRESHOLD = 2.5 # Degrees class LatControlAngle(LatControl): - def __init__(self, CP, CI): - super().__init__(CP, CI) + def __init__(self, CP, CI, dt): + super().__init__(CP, CI, dt) self.sat_check_min_speed = 5. self.use_steer_limited_by_safety = CP.brand == "tesla" diff --git a/selfdrive/controls/lib/latcontrol_pid.py b/selfdrive/controls/lib/latcontrol_pid.py index 00a083509f..d8eae451ed 100644 --- a/selfdrive/controls/lib/latcontrol_pid.py +++ b/selfdrive/controls/lib/latcontrol_pid.py @@ -6,8 +6,8 @@ from openpilot.common.pid import PIDController class LatControlPID(LatControl): - def __init__(self, CP, CI): - super().__init__(CP, CI) + def __init__(self, CP, CI, dt): + super().__init__(CP, CI, dt) self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV), (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV), k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 5a2814e089..bcaf934586 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -23,8 +23,8 @@ LOW_SPEED_Y = [15, 13, 10, 5] class LatControlTorque(LatControl): - def __init__(self, CP, CI): - super().__init__(CP, CI) + def __init__(self, CP, CI, dt): + super().__init__(CP, CI, dt) self.torque_params = CP.lateralTuning.torque.as_builder() self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.lateral_accel_from_torque = CI.lateral_accel_from_torque() diff --git a/selfdrive/controls/tests/test_latcontrol.py b/selfdrive/controls/tests/test_latcontrol.py index 0ce06dc996..c564e9d3e5 100644 --- a/selfdrive/controls/tests/test_latcontrol.py +++ b/selfdrive/controls/tests/test_latcontrol.py @@ -7,6 +7,7 @@ from opendbc.car.toyota.values import CAR as TOYOTA from opendbc.car.nissan.values import CAR as NISSAN from opendbc.car.gm.values import CAR as GM from opendbc.car.vehicle_model import VehicleModel +from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle @@ -22,7 +23,7 @@ class TestLatControl: CI = CarInterface(CP) VM = VehicleModel(CP) - controller = controller(CP.as_reader(), CI) + controller = controller(CP.as_reader(), CI, DT_CTRL) CS = car.CarState.new_message() CS.vEgo = 30 From 0b62dbe16b80acb69515b45a80a966f49d806c64 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 8 Oct 2025 17:29:45 -0700 Subject: [PATCH 124/341] raylib: more closely match Qt alert sizes (#36283) * hmm this doesn't work * clean up * more * bad fmtr * match sidebar net texts * better --- selfdrive/ui/layouts/sidebar.py | 8 ++++---- selfdrive/ui/onroad/alert_renderer.py | 19 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 5987d062ff..8fa38145d3 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -40,13 +40,13 @@ class Colors: NETWORK_TYPES = { - NetworkType.none: "Offline", - NetworkType.wifi: "WiFi", + NetworkType.none: "--", + NetworkType.wifi: "Wi-Fi", + NetworkType.ethernet: "ETH", NetworkType.cell2G: "2G", NetworkType.cell3G: "3G", NetworkType.cell4G: "LTE", NetworkType.cell5G: "5G", - NetworkType.ethernet: "Ethernet", } @@ -172,7 +172,7 @@ class Sidebar(Widget): # Microphone button if self._recording_audio: - self._mic_indicator_rect = rl.Rectangle(rect.x + rect.width - 138, rect.y + 245, 75, 40) + self._mic_indicator_rect = rl.Rectangle(rect.x + rect.width - 130, rect.y + 245, 75, 40) mic_pressed = mouse_down and rl.check_collision_point_rec(mouse_pos, self._mic_indicator_rect) bg_color = rl.Color(Colors.DANGER.r, Colors.DANGER.g, Colors.DANGER.b, int(255 * 0.65)) if mic_pressed else Colors.DANGER diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index e81129b137..2802363f81 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -21,6 +21,11 @@ ALERT_FONT_SMALL = 66 ALERT_FONT_MEDIUM = 74 ALERT_FONT_BIG = 88 +ALERT_HEIGHTS = { + AlertSize.small: 271, + AlertSize.mid: 420, +} + SELFDRIVE_STATE_TIMEOUT = 5 # Seconds SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds @@ -119,15 +124,9 @@ class AlertRenderer(Widget): if size == AlertSize.full: return rect - height = (ALERT_FONT_MEDIUM + 2 * ALERT_PADDING if size == AlertSize.small else - ALERT_FONT_BIG + ALERT_LINE_SPACING + ALERT_FONT_SMALL + 2 * ALERT_PADDING) - - return rl.Rectangle( - rect.x + ALERT_MARGIN, - rect.y + rect.height - ALERT_MARGIN - height, - rect.width - 2 * ALERT_MARGIN, - height - ) + h = ALERT_HEIGHTS.get(size, rect.height) + return rl.Rectangle(rect.x + ALERT_MARGIN, rect.y + rect.height - h + ALERT_MARGIN, + rect.width - ALERT_MARGIN * 2, h - ALERT_MARGIN * 2) def _draw_background(self, rect: rl.Rectangle, alert: Alert) -> None: color = ALERT_COLORS.get(alert.status, ALERT_COLORS[AlertStatus.normal]) @@ -152,7 +151,7 @@ class AlertRenderer(Widget): font_size1 = 132 if is_long else 177 align_ment = rl.GuiTextAlignment.TEXT_ALIGN_CENTER vertical_align = rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE - text_rect = rl.Rectangle(rect.x, rect.y, rect.width, rect.height // 2) + text_rect = rl.Rectangle(rect.x, rect.y, rect.width, rect.height) gui_text_box(text_rect, alert.text1, font_size1, alignment=align_ment, alignment_vertical=vertical_align, font_weight=FontWeight.BOLD) text_rect.y = rect.y + rect.height // 2 From 226465e8827e76579b8aca1cbb25b9734d0fb124 Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Wed, 8 Oct 2025 18:29:54 -0700 Subject: [PATCH 125/341] Latcontrol: refactor pid error to factor out lateral jerk component (#36280) --- selfdrive/controls/controlsd.py | 6 ++-- selfdrive/controls/lib/latcontrol.py | 2 +- selfdrive/controls/lib/latcontrol_angle.py | 2 +- selfdrive/controls/lib/latcontrol_pid.py | 2 +- selfdrive/controls/lib/latcontrol_torque.py | 34 ++++++++++++++------- selfdrive/controls/tests/test_latcontrol.py | 6 ++-- selfdrive/test/process_replay/ref_commit | 2 +- 7 files changed, 34 insertions(+), 20 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 4442e6cbe4..9e31ac1526 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -17,6 +17,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.controls.lib.longcontrol import LongControl +from openpilot.selfdrive.modeld.modeld import LAT_SMOOTH_SECONDS from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose State = log.SelfdriveState.OpenpilotState @@ -35,7 +36,7 @@ class Controls: self.CI = interfaces[self.CP.carFingerprint](self.CP) - self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState', + self.sm = messaging.SubMaster(['liveDelay', 'liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState', 'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput', 'driverMonitoringState', 'onroadEvents', 'driverAssistance'], poll='selfdriveState') self.pm = messaging.PubMaster(['carControl', 'controlsState']) @@ -117,11 +118,12 @@ class Controls: # Reset desired curvature to current to avoid violating the limits on engage new_desired_curvature = model_v2.action.desiredCurvature if CC.latActive else self.curvature self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll) + lat_delay = self.sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS actuators.curvature = self.desired_curvature steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp, self.steer_limited_by_safety, self.desired_curvature, - curvature_limited) # TODO what if not available + curvature_limited, lat_delay) actuators.torque = float(steer) actuators.steeringAngleDeg = float(steeringAngleDeg) # Ensure no NaNs/Infs diff --git a/selfdrive/controls/lib/latcontrol.py b/selfdrive/controls/lib/latcontrol.py index a97ed3e54e..00cf0dab91 100644 --- a/selfdrive/controls/lib/latcontrol.py +++ b/selfdrive/controls/lib/latcontrol.py @@ -14,7 +14,7 @@ class LatControl(ABC): self.steer_max = 1.0 @abstractmethod - def update(self, active: bool, CS, VM, params, steer_limited_by_safety: bool, desired_curvature: float, calibrated_pose, curvature_limited: bool): + def update(self, active: bool, CS, VM, params, steer_limited_by_safety: bool, desired_curvature: float, curvature_limited: bool, lat_delay: float): pass def reset(self): diff --git a/selfdrive/controls/lib/latcontrol_angle.py b/selfdrive/controls/lib/latcontrol_angle.py index cea9f74814..808c9a659a 100644 --- a/selfdrive/controls/lib/latcontrol_angle.py +++ b/selfdrive/controls/lib/latcontrol_angle.py @@ -13,7 +13,7 @@ class LatControlAngle(LatControl): self.sat_check_min_speed = 5. self.use_steer_limited_by_safety = CP.brand == "tesla" - def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited): + def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay): angle_log = log.ControlsState.LateralAngleState.new_message() if not active: diff --git a/selfdrive/controls/lib/latcontrol_pid.py b/selfdrive/controls/lib/latcontrol_pid.py index d8eae451ed..4cc1c47f61 100644 --- a/selfdrive/controls/lib/latcontrol_pid.py +++ b/selfdrive/controls/lib/latcontrol_pid.py @@ -13,7 +13,7 @@ class LatControlPID(LatControl): k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max) self.get_steer_feedforward = CI.get_steer_feedforward_function() - def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited): + def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay): pid_log = log.ControlsState.LateralPIDState.new_message() pid_log.steeringAngleDeg = float(CS.steeringAngleDeg) pid_log.steeringRateDeg = float(CS.steeringRateDeg) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index bcaf934586..e50d91351d 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -1,9 +1,11 @@ import math import numpy as np +from collections import deque from cereal import log from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY +from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.common.pid import PIDController @@ -29,9 +31,11 @@ class LatControlTorque(LatControl): self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.lateral_accel_from_torque = CI.lateral_accel_from_torque() self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, - k_f=self.torque_params.kf) + k_f=self.torque_params.kf, rate=1/self.dt) self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg + self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES = int(1 / self.dt) + self.requested_lateral_accel_buffer = deque([0.] * self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES , maxlen=self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES) def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction): self.torque_params.latAccelFactor = latAccelFactor @@ -43,7 +47,7 @@ class LatControlTorque(LatControl): self.pid.set_limits(self.lateral_accel_from_torque(self.steer_max, self.torque_params), self.lateral_accel_from_torque(-self.steer_max, self.torque_params)) - def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited): + def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay): pid_log = log.ControlsState.LateralTorqueState.new_message() if not active: output_torque = 0.0 @@ -53,21 +57,29 @@ class LatControlTorque(LatControl): roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0)) - desired_lateral_accel = desired_curvature * CS.vEgo ** 2 + delay_frames = int(np.clip(lat_delay / self.dt, 1, self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES)) + expected_lateral_accel = self.requested_lateral_accel_buffer[-delay_frames] + # TODO factor out lateral jerk from error to later replace it with delay independent alternative + future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2 + self.requested_lateral_accel_buffer.append(future_desired_lateral_accel) + gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation + desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay actual_lateral_accel = actual_curvature * CS.vEgo ** 2 lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2 - low_speed_factor = np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2 - setpoint = desired_lateral_accel + low_speed_factor * desired_curvature - measurement = actual_lateral_accel + low_speed_factor * actual_curvature - gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation + low_speed_factor = np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2 / (np.clip(CS.vEgo, MIN_SPEED, np.inf) ** 2) + setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel + measurement = actual_lateral_accel + error = setpoint - measurement + error_lsf = error + low_speed_factor * error # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly - pid_log.error = float(setpoint - measurement) - ff = gravity_adjusted_lateral_accel + pid_log.error = float(error_lsf) + ff = gravity_adjusted_future_lateral_accel # latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll ff -= self.torque_params.latAccelOffset - ff += get_friction(desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) + # TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it + ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 output_lataccel = self.pid.update(pid_log.error, @@ -83,7 +95,7 @@ class LatControlTorque(LatControl): pid_log.f = float(self.pid.f) pid_log.output = float(-output_torque) # TODO: log lat accel? pid_log.actualLateralAccel = float(actual_lateral_accel) - pid_log.desiredLateralAccel = float(desired_lateral_accel) + pid_log.desiredLateralAccel = float(expected_lateral_accel) pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited)) # TODO left is positive in this convention diff --git a/selfdrive/controls/tests/test_latcontrol.py b/selfdrive/controls/tests/test_latcontrol.py index c564e9d3e5..354c7f00ad 100644 --- a/selfdrive/controls/tests/test_latcontrol.py +++ b/selfdrive/controls/tests/test_latcontrol.py @@ -33,13 +33,13 @@ class TestLatControl: # Saturate for curvature limited and controller limited for _ in range(1000): - _, _, lac_log = controller.update(True, CS, VM, params, False, 0, True) + _, _, lac_log = controller.update(True, CS, VM, params, False, 0, True, 0.2) assert lac_log.saturated for _ in range(1000): - _, _, lac_log = controller.update(True, CS, VM, params, False, 0, False) + _, _, lac_log = controller.update(True, CS, VM, params, False, 0, False, 0.2) assert not lac_log.saturated for _ in range(1000): - _, _, lac_log = controller.update(True, CS, VM, params, False, 1, False) + _, _, lac_log = controller.update(True, CS, VM, params, False, 1, False, 0.2) assert lac_log.saturated diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index a833fadb94..f55bcc3787 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -afcab1abb62b9d5678342956cced4712f44e909e \ No newline at end of file +4c2f4e9d3d6667c990900ad63f7f5a6b23a8bcdf \ No newline at end of file From dcc5afa8fa10b6a0aff551971aff5bbf71bb9191 Mon Sep 17 00:00:00 2001 From: "kostas.pats" <35031825+kostas1507@users.noreply.github.com> Date: Thu, 9 Oct 2025 05:30:32 +0000 Subject: [PATCH 126/341] improve webrtc stack for use in camera focusing (#36268) * made LiveStreamVideoStreamTrack use system time to calculate pts * fixes as requested * Align panda submodule with master (panda@615009c) * made loggerd accept a run time env variable to pick stream bitrate * added /notify endpoint to send json to all session's data channel * fixed static analysis error * adapted webrtc stream test to new pts calculation method * fixed static erro * fixed wrong indent * fixed import order * delete accidental newline * remove excess spaces Co-authored-by: Maxime Desroches * remove excess spaces Co-authored-by: Maxime Desroches * changed exeption handling based on review * fixed typo on exception handling --------- Co-authored-by: Maxime Desroches --- system/loggerd/loggerd.h | 4 +++- system/webrtc/device/video.py | 8 +++++--- system/webrtc/tests/test_stream_session.py | 7 +++++-- system/webrtc/webrtcd.py | 15 +++++++++++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/system/loggerd/loggerd.h b/system/loggerd/loggerd.h index 967caec867..8e3a74d2d9 100644 --- a/system/loggerd/loggerd.h +++ b/system/loggerd/loggerd.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "cereal/messaging/messaging.h" @@ -46,7 +47,8 @@ struct EncoderSettings { } static EncoderSettings StreamEncoderSettings() { - return EncoderSettings{.encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .bitrate = 1'000'000, .gop_size = 15}; + int _stream_bitrate = getenv("STREAM_BITRATE") ? atoi(getenv("STREAM_BITRATE")) : 1'000'000; + return EncoderSettings{.encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .bitrate = _stream_bitrate , .gop_size = 15}; } }; diff --git a/system/webrtc/device/video.py b/system/webrtc/device/video.py index 1bca909294..50feab4f4a 100644 --- a/system/webrtc/device/video.py +++ b/system/webrtc/device/video.py @@ -1,4 +1,5 @@ import asyncio +import time import av from teleoprtc.tracks import TiciVideoStreamTrack @@ -20,6 +21,7 @@ class LiveStreamVideoStreamTrack(TiciVideoStreamTrack): self._sock = messaging.sub_sock(self.camera_to_sock_mapping[camera_type], conflate=True) self._pts = 0 + self._t0_ns = time.monotonic_ns() async def recv(self): while True: @@ -32,10 +34,10 @@ class LiveStreamVideoStreamTrack(TiciVideoStreamTrack): packet = av.Packet(evta.header + evta.data) packet.time_base = self._time_base - packet.pts = self._pts - self.log_debug("track sending frame %s", self._pts) - self._pts += self._dt * self._clock_rate + self._pts = ((time.monotonic_ns() - self._t0_ns) * self._clock_rate) // 1_000_000_000 + packet.pts = self._pts + self.log_debug("track sending frame %d", self._pts) return packet diff --git a/system/webrtc/tests/test_stream_session.py b/system/webrtc/tests/test_stream_session.py index 113fa5e7e6..e31fda3728 100644 --- a/system/webrtc/tests/test_stream_session.py +++ b/system/webrtc/tests/test_stream_session.py @@ -1,5 +1,6 @@ import asyncio import json +import time # for aiortc and its dependencies import warnings warnings.filterwarnings("ignore", category=DeprecationWarning) @@ -14,7 +15,6 @@ from cereal import messaging, log from openpilot.system.webrtc.webrtcd import CerealOutgoingMessageProxy, CerealIncomingMessageProxy from openpilot.system.webrtc.device.video import LiveStreamVideoStreamTrack from openpilot.system.webrtc.device.audio import AudioInputStreamTrack -from openpilot.common.realtime import DT_DMON class TestStreamSession: @@ -81,7 +81,10 @@ class TestStreamSession: for i in range(5): packet = self.loop.run_until_complete(track.recv()) assert packet.time_base == VIDEO_TIME_BASE - assert packet.pts == int(i * DT_DMON * VIDEO_CLOCK_RATE) + if i == 0: + start_ns = time.monotonic_ns() + start_pts = packet.pts + assert abs(i + packet.pts - (start_pts + (((time.monotonic_ns() - start_ns) * VIDEO_CLOCK_RATE) // 1_000_000_000))) < 450 #5ms assert packet.size == 0 def test_input_audio_track(self, mocker): diff --git a/system/webrtc/webrtcd.py b/system/webrtc/webrtcd.py index fb93e565ff..c19f1bf9dd 100755 --- a/system/webrtc/webrtcd.py +++ b/system/webrtc/webrtcd.py @@ -239,6 +239,20 @@ async def get_schema(request: 'web.Request'): schema_dict = {s: generate_field(log.Event.schema.fields[s]) for s in services} return web.json_response(schema_dict) +async def post_notify(request: 'web.Request'): + try: + payload = await request.json() + except Exception as e: + raise web.HTTPBadRequest(text="Invalid JSON") from e + + for session in list(request.app.get('streams', {}).values()): + try: + ch = session.stream.get_messaging_channel() + ch.send(json.dumps(payload)) + except Exception: + continue + + return web.Response(status=200, text="OK") async def on_shutdown(app: 'web.Application'): for session in app['streams'].values(): @@ -258,6 +272,7 @@ def webrtcd_thread(host: str, port: int, debug: bool): app['debug'] = debug app.on_shutdown.append(on_shutdown) app.router.add_post("/stream", get_stream) + app.router.add_post("/notify", post_notify) app.router.add_get("/schema", get_schema) web.run_app(app, host=host, port=port) From 0736f325fcee68bcd8834fcfa4a374089e316b19 Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Thu, 9 Oct 2025 10:29:35 -0700 Subject: [PATCH 127/341] Latcontrol torque: cleaner low_speed_factor calculation (#36287) --- selfdrive/controls/lib/latcontrol_torque.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index e50d91351d..4878bde3af 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -67,7 +67,7 @@ class LatControlTorque(LatControl): actual_lateral_accel = actual_curvature * CS.vEgo ** 2 lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2 - low_speed_factor = np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2 / (np.clip(CS.vEgo, MIN_SPEED, np.inf) ** 2) + low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2 setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel measurement = actual_lateral_accel error = setpoint - measurement From 4c9ca91b98e244849556e59342e7447c400bb2f3 Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Thu, 9 Oct 2025 10:34:26 -0700 Subject: [PATCH 128/341] Latcontrol: use more accurate naming for saturation time (#36286) --- selfdrive/controls/lib/latcontrol.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/selfdrive/controls/lib/latcontrol.py b/selfdrive/controls/lib/latcontrol.py index 00cf0dab91..d69796738f 100644 --- a/selfdrive/controls/lib/latcontrol.py +++ b/selfdrive/controls/lib/latcontrol.py @@ -5,9 +5,8 @@ from abc import abstractmethod, ABC class LatControl(ABC): def __init__(self, CP, CI, dt): self.dt = dt - self.sat_count_rate = 1.0 * self.dt self.sat_limit = CP.steerLimitTimer - self.sat_count = 0. + self.sat_time = 0. self.sat_check_min_speed = 10. # we define the steer torque scale as [-1.0...1.0] @@ -18,13 +17,13 @@ class LatControl(ABC): pass def reset(self): - self.sat_count = 0. + self.sat_time = 0. def _check_saturation(self, saturated, CS, steer_limited_by_safety, curvature_limited): # Saturated only if control output is not being limited by car torque/angle rate limits if (saturated or curvature_limited) and CS.vEgo > self.sat_check_min_speed and not steer_limited_by_safety and not CS.steeringPressed: - self.sat_count += self.sat_count_rate + self.sat_time += self.dt else: - self.sat_count -= self.sat_count_rate - self.sat_count = np.clip(self.sat_count, 0.0, self.sat_limit) - return self.sat_count > (self.sat_limit - 1e-3) + self.sat_time -= self.dt + self.sat_time = np.clip(self.sat_time, 0.0, self.sat_limit) + return self.sat_time > (self.sat_limit - 1e-3) From 22d5cbd0fac51a440749193cf5565ab71cf181f6 Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Thu, 9 Oct 2025 11:10:44 -0700 Subject: [PATCH 129/341] PID: coefficients should be in front, i_rate should be i_dt (#36288) --- common/pid.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/pid.py b/common/pid.py index 99142280ca..ff5084781f 100644 --- a/common/pid.py +++ b/common/pid.py @@ -16,7 +16,7 @@ class PIDController: self.set_limits(pos_limit, neg_limit) - self.i_rate = 1.0 / rate + self.i_dt = 1.0 / rate self.speed = 0.0 self.reset() @@ -46,12 +46,12 @@ class PIDController: def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False): self.speed = speed - self.p = float(error) * self.k_p - self.f = feedforward * self.k_f - self.d = error_rate * self.k_d + self.p = self.k_p * float(error) + self.d = self.k_d * error_rate + self.f = self.k_f * feedforward if not freeze_integrator: - i = self.i + error * self.k_i * self.i_rate + i = self.i + self.k_i * self.i_dt * error # Don't allow windup if already clipping test_control = self.p + i + self.d + self.f From d07981ea3cebae51e0654b532c399bb69972d9fd Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Thu, 9 Oct 2025 11:48:13 -0700 Subject: [PATCH 130/341] bump opendbc (#36289) --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index aa3d32a63b..dfa6807ebf 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit aa3d32a63b7d05e69777fa90147192220abde8a3 +Subproject commit dfa6807ebf9f0edb593189a703b857c834a88a75 From 4d085424f80009ee5ea4ae8f4519ed871905b382 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Thu, 9 Oct 2025 12:58:27 -0700 Subject: [PATCH 131/341] =?UTF-8?q?North=20Nevada=20Model=20=F0=9F=8F=9C?= =?UTF-8?q?=EF=B8=8F=20(#36276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * e2d9c622-25a8-4ccd-8c8e-c62537b7aa0c/400 * 0e620593-e85f-40c2-9adf-1e945651ed13/400 --- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index cb5a6b3a47..89444c3ea7 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b7c686e13637733ec432d774ff5b665c4e4663982ff03deeefd6f6ffd511741 -size 12343531 +oid sha256:792fca7c24faadc1183327eec7e04d06dd6ec0e53ac7bb43cb2b9823c6cfb32f +size 12343535 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 4b4fa05df8..0411ddb744 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:befac016a247b7ad5dc5b55d339d127774ed7bd2b848f1583f72aa4caee37781 -size 46271991 +oid sha256:cf6376aa9a090f0da26c280ef69eabf9bbdd51d1faac9ed392919c3db69be916 +size 46271942 From de805e4af734722f3c73db358d03fe5d5dc489fd Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Thu, 9 Oct 2025 14:57:12 -0700 Subject: [PATCH 132/341] Lateral torque controller: use measurement rate as error rate (#36291) --- selfdrive/controls/lib/latcontrol_torque.py | 25 +++++++++++++-------- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 4878bde3af..2a1b666efd 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -4,7 +4,9 @@ from collections import deque from cereal import log from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction +from opendbc.car.tests.test_lateral_limits import MAX_LAT_JERK_UP from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY +from openpilot.common.filter_simple import FirstOrderFilter from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.common.pid import PIDController @@ -36,6 +38,8 @@ class LatControlTorque(LatControl): self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES = int(1 / self.dt) self.requested_lateral_accel_buffer = deque([0.] * self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES , maxlen=self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES) + self.previous_measurement = 0.0 + self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * (MAX_LAT_JERK_UP - 0.5)), self.dt) def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction): self.torque_params.latAccelFactor = latAccelFactor @@ -53,9 +57,10 @@ class LatControlTorque(LatControl): output_torque = 0.0 pid_log.active = False else: - actual_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll) + measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll) roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0)) + lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2 delay_frames = int(np.clip(lat_delay / self.dt, 1, self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES)) expected_lateral_accel = self.requested_lateral_accel_buffer[-delay_frames] @@ -64,12 +69,13 @@ class LatControlTorque(LatControl): self.requested_lateral_accel_buffer.append(future_desired_lateral_accel) gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay - actual_lateral_accel = actual_curvature * CS.vEgo ** 2 - lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2 + + measurement = measured_curvature * CS.vEgo ** 2 + measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt) + self.previous_measurement = measurement low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2 setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel - measurement = actual_lateral_accel error = setpoint - measurement error_lsf = error + low_speed_factor * error @@ -83,9 +89,10 @@ class LatControlTorque(LatControl): freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 output_lataccel = self.pid.update(pid_log.error, - feedforward=ff, - speed=CS.vEgo, - freeze_integrator=freeze_integrator) + -measurement_rate, + feedforward=ff, + speed=CS.vEgo, + freeze_integrator=freeze_integrator) output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params) pid_log.active = True @@ -94,8 +101,8 @@ class LatControlTorque(LatControl): pid_log.d = float(self.pid.d) pid_log.f = float(self.pid.f) pid_log.output = float(-output_torque) # TODO: log lat accel? - pid_log.actualLateralAccel = float(actual_lateral_accel) - pid_log.desiredLateralAccel = float(expected_lateral_accel) + pid_log.actualLateralAccel = float(measurement) + pid_log.desiredLateralAccel = float(setpoint) pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited)) # TODO left is positive in this convention diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index f55bcc3787..538aa34f2a 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -4c2f4e9d3d6667c990900ad63f7f5a6b23a8bcdf \ No newline at end of file +5342f5684c2498a33d6178f3b59efd5e83b73acc \ No newline at end of file From 1b90b42647b78d7d007712b063f049a137fa1779 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Oct 2025 19:10:10 -0700 Subject: [PATCH 133/341] Html renderer: reset tag so untagged text doesn't inherit last tag --- system/ui/widgets/html_render.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index 2c3eeb3793..8d713ee7ea 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -127,9 +127,10 @@ class HtmlRenderer(Widget): # Always add content regardless of opening or closing tag close_tag() - # TODO: reset to None if end tag? if is_start_tag: current_tag = tag + else: + current_tag = None # increment after we add the content for the current tag if tag == ElementType.UL: From 29767988525b1e1d3dac3bd573a7328da788c909 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Oct 2025 19:50:27 -0700 Subject: [PATCH 134/341] raylib: implement toggles (#36284) * start on exp mode * more * fmt * rm * 2nd try * almost there * clean up * and this * fmt * more * exp is colored when active * move out, and rm redudnant self.state * revert html changes for now * fix untagged text inheriting previous tag * why would this be unknown * here too * update live with car * clean up + refresh toggles on showEvent + catch from cursor about setting desc if no carparams * not sure why * fix disengaged re-enabling locked toggles --- selfdrive/ui/layouts/settings/toggles.py | 189 ++++++++++++++++++----- selfdrive/ui/ui_state.py | 24 ++- system/ui/widgets/list_view.py | 28 ++-- system/ui/widgets/toggle.py | 8 +- 4 files changed, 195 insertions(+), 54 deletions(-) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 1e0e7bfd53..01f663d4b9 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -1,7 +1,11 @@ -from openpilot.common.params import Params +from cereal import log +from openpilot.common.params import Params, UnknownKeyName from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_item from openpilot.system.ui.widgets.scroller import Scroller +from openpilot.selfdrive.ui.ui_state import ui_state + +PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants # Description constants DESCRIPTIONS = { @@ -30,66 +34,179 @@ class TogglesLayout(Widget): def __init__(self): super().__init__() self._params = Params() - items = [ - toggle_item( + + # param, title, desc, icon, needs_restart + self._toggle_defs = { + "OpenpilotEnabledToggle": ( "Enable openpilot", DESCRIPTIONS["OpenpilotEnabledToggle"], - self._params.get_bool("OpenpilotEnabledToggle"), - icon="chffr_wheel.png", + "chffr_wheel.png", + True, ), - toggle_item( + "ExperimentalMode": ( "Experimental Mode", - initial_state=self._params.get_bool("ExperimentalMode"), - icon="experimental_white.png", + "", + "experimental_white.png", + False, ), - toggle_item( + "DisengageOnAccelerator": ( "Disengage on Accelerator Pedal", DESCRIPTIONS["DisengageOnAccelerator"], - self._params.get_bool("DisengageOnAccelerator"), - icon="disengage_on_accelerator.png", + "disengage_on_accelerator.png", + False, ), - multiple_button_item( - "Driving Personality", - DESCRIPTIONS["LongitudinalPersonality"], - buttons=["Aggressive", "Standard", "Relaxed"], - button_width=255, - callback=self._set_longitudinal_personality, - selected_index=self._params.get("LongitudinalPersonality", return_default=True), - icon="speed_limit.png" - ), - toggle_item( + "IsLdwEnabled": ( "Enable Lane Departure Warnings", DESCRIPTIONS["IsLdwEnabled"], - self._params.get_bool("IsLdwEnabled"), - icon="warning.png", + "warning.png", + False, ), - toggle_item( + "AlwaysOnDM": ( "Always-On Driver Monitoring", DESCRIPTIONS["AlwaysOnDM"], - self._params.get_bool("AlwaysOnDM"), - icon="monitoring.png", + "monitoring.png", + False, ), - toggle_item( + "RecordFront": ( "Record and Upload Driver Camera", DESCRIPTIONS["RecordFront"], - self._params.get_bool("RecordFront"), - icon="monitoring.png", + "monitoring.png", + True, ), - toggle_item( + "RecordAudio": ( "Record and Upload Microphone Audio", DESCRIPTIONS["RecordAudio"], - self._params.get_bool("RecordAudio"), - icon="microphone.png", + "microphone.png", + True, ), - toggle_item( - "Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="metric.png" + "IsMetric": ( + "Use Metric System", + DESCRIPTIONS["IsMetric"], + "metric.png", + False, ), - ] + } - self._scroller = Scroller(items, line_separator=True, spacing=0) + self._long_personality_setting = multiple_button_item( + "Driving Personality", + DESCRIPTIONS["LongitudinalPersonality"], + buttons=["Aggressive", "Standard", "Relaxed"], + button_width=255, + callback=self._set_longitudinal_personality, + selected_index=self._params.get("LongitudinalPersonality", return_default=True), + icon="speed_limit.png" + ) + + self._toggles = {} + self._locked_toggles = set() + for param, (title, desc, icon, needs_restart) in self._toggle_defs.items(): + toggle = toggle_item( + title, + desc, + self._params.get_bool(param), + callback=lambda state, p=param: self._toggle_callback(state, p), + icon=icon, + ) + + try: + locked = self._params.get_bool(param + "Lock") + except UnknownKeyName: + locked = False + toggle.action_item.set_enabled(not locked) + + if needs_restart and not locked: + toggle.set_description(toggle.description + " Changing this setting will restart openpilot if the car is powered on.") + + # track for engaged state updates + if locked: + self._locked_toggles.add(param) + + self._toggles[param] = toggle + + # insert longitudinal personality after NDOG toggle + if param == "DisengageOnAccelerator": + self._toggles["LongitudinalPersonality"] = self._long_personality_setting + + self._update_experimental_mode_icon() + self._scroller = Scroller(list(self._toggles.values()), line_separator=True, spacing=0) + + def _update_state(self): + if ui_state.sm.updated["selfdriveState"]: + personality = PERSONALITY_TO_INT[ui_state.sm["selfdriveState"].personality] + if personality != ui_state.personality and ui_state.started: + self._long_personality_setting.action_item.set_selected_button(personality) + ui_state.personality = personality + + # these toggles need restart, block while engaged + for toggle_def in self._toggle_defs: + if self._toggle_defs[toggle_def][3] and toggle_def not in self._locked_toggles: + self._toggles[toggle_def].action_item.set_enabled(not ui_state.engaged) + + def show_event(self): + self._update_toggles() + + def _update_toggles(self): + e2e_description = ( + "openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " + + "Experimental features are listed below:
" + + "

End-to-End Longitudinal Control


" + + "Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. " + + "Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; " + + "mistakes should be expected.
" + + "

New Driving Visualization


" + + "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." + ) + + is_release = self._params.get_bool("IsReleaseBranch") + + if ui_state.CP is not None: + if ui_state.has_longitudinal_control: + self._toggles["ExperimentalMode"].action_item.set_enabled(True) + self._toggles["ExperimentalMode"].set_description(e2e_description) + self._long_personality_setting.action_item.set_enabled(True) + else: + # no long for now + self._toggles["ExperimentalMode"].action_item.set_enabled(False) + self._toggles["ExperimentalMode"].action_item.set_state(False) + self._long_personality_setting.action_item.set_enabled(False) + self._params.remove("ExperimentalMode") + + unavailable = "Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control." + + long_desc = unavailable + " openpilot longitudinal control may come in a future update." + if ui_state.CP.getAlphaLongitudinalAvailable(): + if is_release: + long_desc = unavailable + " " + ("An alpha version of openpilot longitudinal control can be tested, along with " + + "Experimental mode, on non-release branches.") + else: + long_desc = "Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode." + + self._toggles["ExperimentalMode"].set_description("" + long_desc + "

" + e2e_description) + else: + self._toggles["ExperimentalMode"].set_description(e2e_description) + + self._update_experimental_mode_icon() + + # TODO: make a param control list item so we don't need to manage internal state as much here + # refresh toggles from params to mirror external changes + for param in self._toggle_defs: + self._toggles[param].action_item.set_state(self._params.get_bool(param)) def _render(self, rect): self._scroller.render(rect) + def _update_experimental_mode_icon(self): + icon = "experimental.png" if self._toggles["ExperimentalMode"].action_item.get_state() else "experimental_white.png" + self._toggles["ExperimentalMode"].set_icon(icon) + + def _toggle_callback(self, state: bool, param: str): + if param == "ExperimentalMode": + self._update_experimental_mode_icon() + + self._params.put_bool(param, state) + if self._toggle_defs[param][3]: + self._params.put_bool("OnroadCycleRequested", True) + def _set_longitudinal_personality(self, button_index: int): self._params.put("LongitudinalPersonality", button_index) diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 9c45519b4b..be83517980 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -4,9 +4,9 @@ import time import threading from collections.abc import Callable from enum import Enum -from cereal import messaging, log +from cereal import messaging, car, log from openpilot.common.filter_simple import FirstOrderFilter -from openpilot.common.params import Params, UnknownKeyName +from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.lib.prime_state import PrimeState from openpilot.system.ui.lib.application import gui_app @@ -69,7 +69,10 @@ class UIState: self.ignition: bool = False self.panda_type: log.PandaState.PandaType = log.PandaState.PandaType.unknown self.personality: log.LongitudinalPersonality = log.LongitudinalPersonality.standard + self.has_longitudinal_control: bool = False + self.CP: car.CarParams | None = None self.light_sensor: float = -1.0 + self._param_update_time: float = 0.0 self._update_params() @@ -87,6 +90,9 @@ class UIState: self.sm.update(0) self._update_state() self._update_status() + if time.monotonic() - self._param_update_time > 5.0: + self._update_params() + self._param_update_time = time.monotonic() device.update() def _update_state(self) -> None: @@ -137,10 +143,16 @@ class UIState: self._started_prev = self.started def _update_params(self) -> None: - try: - self.is_metric = self.params.get_bool("IsMetric") - except UnknownKeyName: - self.is_metric = False + self.is_metric = self.params.get_bool("IsMetric") + + # Update longitudinal control state + CP_bytes = self.params.get("CarParams") + if CP_bytes is not None: + self.CP = messaging.log_from_bytes(CP_bytes, car.CarParams) + if self.CP.alphaLongitudinalAvailable: + self.has_longitudinal_control = self.params.get_bool("AlphaLongitudinalEnabled") + else: + self.has_longitudinal_control = self.CP.openpilotLongitudinalControl class Device: diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index 509c49be35..bbf66c6555 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -56,10 +56,10 @@ class ItemAction(Widget, ABC): class ToggleAction(ItemAction): - def __init__(self, initial_state: bool = False, width: int = TOGGLE_WIDTH, enabled: bool | Callable[[], bool] = True): + def __init__(self, initial_state: bool = False, width: int = TOGGLE_WIDTH, enabled: bool | Callable[[], bool] = True, + callback: Callable[[bool], None] | None = None): super().__init__(width, enabled) - self.toggle = Toggle(initial_state=initial_state) - self.state = initial_state + self.toggle = Toggle(initial_state=initial_state, callback=callback) def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: super().set_touch_valid_callback(touch_callback) @@ -68,15 +68,13 @@ class ToggleAction(ItemAction): def _render(self, rect: rl.Rectangle) -> bool: self.toggle.set_enabled(self.enabled) clicked = self.toggle.render(rl.Rectangle(rect.x, rect.y + (rect.height - TOGGLE_HEIGHT) / 2, self._rect.width, TOGGLE_HEIGHT)) - self.state = self.toggle.get_state() return bool(clicked) def set_state(self, state: bool): - self.state = state self.toggle.set_state(state) def get_state(self) -> bool: - return self.state + return self.toggle.get_state() class ButtonAction(ItemAction): @@ -207,6 +205,13 @@ class MultipleButtonAction(ItemAction): self.callback = callback self._font = gui_app.font(FontWeight.MEDIUM) + def set_selected_button(self, index: int): + if 0 <= index < len(self.buttons): + self.selected_button = index + + def get_selected_button(self) -> int: + return self.selected_button + def _render(self, rect: rl.Rectangle): spacing = RIGHT_ITEM_PADDING button_y = rect.y + (rect.height - BUTTON_HEIGHT) / 2 @@ -259,7 +264,7 @@ class ListItem(Widget): action_item: ItemAction | None = None): super().__init__() self.title = title - self.icon = icon + self.set_icon(icon) self._description = description self.description_visible = description_visible self.callback = callback @@ -267,7 +272,6 @@ class ListItem(Widget): self.set_rect(rl.Rectangle(0, 0, ITEM_BASE_WIDTH, ITEM_BASE_HEIGHT)) self._font = gui_app.font(FontWeight.NORMAL) - self._icon_texture = gui_app.texture(os.path.join("icons", self.icon), ICON_SIZE, ICON_SIZE) if self.icon else None self._html_renderer = HtmlRenderer(text="", text_size={ElementType.P: ITEM_DESC_FONT_SIZE}, text_color=ITEM_DESC_TEXT_COLOR) @@ -352,6 +356,10 @@ class ListItem(Widget): if self.callback: self.callback() + def set_icon(self, icon: str | None): + self.icon = icon + self._icon_texture = gui_app.texture(os.path.join("icons", self.icon), ICON_SIZE, ICON_SIZE) if self.icon else None + def set_description(self, description: str | Callable[[], str] | None): self._description = description new_desc = self.description @@ -398,8 +406,8 @@ def simple_item(title: str, callback: Callable | None = None) -> ListItem: def toggle_item(title: str, description: str | Callable[[], str] | None = None, initial_state: bool = False, callback: Callable | None = None, icon: str = "", enabled: bool | Callable[[], bool] = True) -> ListItem: - action = ToggleAction(initial_state=initial_state, enabled=enabled) - return ListItem(title=title, description=description, action_item=action, icon=icon, callback=callback) + action = ToggleAction(initial_state=initial_state, enabled=enabled, callback=callback) + return ListItem(title=title, description=description, action_item=action, icon=icon) def button_item(title: str, button_text: str | Callable[[], str], description: str | Callable[[], str] | None = None, diff --git a/system/ui/widgets/toggle.py b/system/ui/widgets/toggle.py index 968afda9c8..0fbf3c844a 100644 --- a/system/ui/widgets/toggle.py +++ b/system/ui/widgets/toggle.py @@ -1,4 +1,5 @@ import pyray as rl +from collections.abc import Callable from openpilot.system.ui.lib.application import MousePos from openpilot.system.ui.widgets import Widget @@ -14,9 +15,10 @@ ANIMATION_SPEED = 8.0 class Toggle(Widget): - def __init__(self, initial_state=False): + def __init__(self, initial_state: bool = False, callback: Callable[[bool], None] | None = None): super().__init__() self._state = initial_state + self._callback = callback self._enabled = True self._progress = 1.0 if initial_state else 0.0 self._target = self._progress @@ -32,8 +34,10 @@ class Toggle(Widget): self._clicked = True self._state = not self._state self._target = 1.0 if self._state else 0.0 + if self._callback: + self._callback(self._state) - def get_state(self): + def get_state(self) -> bool: return self._state def set_state(self, state: bool): From d6651ccd82eafd79ee1f70f5979cb2e17442e5e7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Oct 2025 23:09:55 -0700 Subject: [PATCH 135/341] raylib: implement developer panel (#36292) * first pass by cursor * fix * tell it what's good * stash * desc * clean up junk * alpha long can't use onroad cycle again due to faults * lint * fix kb --- selfdrive/ui/layouts/settings/developer.py | 142 ++++++++++++++---- selfdrive/ui/layouts/settings/toggles.py | 5 +- .../ui/tests/test_ui/raylib_screenshots.py | 2 +- 3 files changed, 115 insertions(+), 34 deletions(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index cfef0f84d1..143bb2c262 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -1,5 +1,6 @@ from openpilot.common.params import Params from openpilot.selfdrive.ui.widgets.ssh_key import ssh_key_item +from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.list_view import toggle_item from openpilot.system.ui.widgets.scroller import Scroller @@ -10,11 +11,15 @@ DESCRIPTIONS = { "ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " + "See https://docs.comma.ai/how-to/connect-to-comma for more info." ), - 'joystick_debug_mode': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)", 'ssh_key': ( "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " + "other than your own. A comma employee will NEVER ask you to add their GitHub username." ), + 'alpha_longitudinal': ( + "WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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." + ), } @@ -22,32 +27,55 @@ class DeveloperLayout(Widget): def __init__(self): super().__init__() self._params = Params() + self._is_release = self._params.get_bool("IsReleaseBranch") + + # Build items and keep references for callbacks/state updates + self._adb_toggle = toggle_item( + "Enable ADB", + description=DESCRIPTIONS["enable_adb"], + initial_state=self._params.get_bool("AdbEnabled"), + callback=self._on_enable_adb, + ) + + # SSH enable toggle + SSH key management + self._ssh_toggle = toggle_item( + "Enable SSH", + description="", + initial_state=self._params.get_bool("SshEnabled"), + callback=self._on_enable_ssh, + ) + self._ssh_keys = ssh_key_item("SSH Keys", description=DESCRIPTIONS["ssh_key"]) + + self._joystick_toggle = toggle_item( + "Joystick Debug Mode", + description="", + initial_state=self._params.get_bool("JoystickDebugMode"), + callback=self._on_joystick_debug_mode, + ) + + self._long_maneuver_toggle = toggle_item( + "Longitudinal Maneuver Mode", + description="", + initial_state=self._params.get_bool("LongitudinalManeuverMode"), + callback=self._on_long_maneuver_mode, + ) + + self._alpha_long_toggle = toggle_item( + "openpilot Longitudinal Control (Alpha)", + description=DESCRIPTIONS["alpha_longitudinal"], + initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), + callback=self._on_alpha_long_enabled, + ) + + self._alpha_long_toggle.set_description(self._alpha_long_toggle.description + " Changing this setting will restart openpilot if the car is powered on.") + items = [ - toggle_item( - "Enable ADB", - description=DESCRIPTIONS["enable_adb"], - initial_state=self._params.get_bool("AdbEnabled"), - callback=self._on_enable_adb, - ), - ssh_key_item("SSH Key", description=DESCRIPTIONS["ssh_key"]), - toggle_item( - "Joystick Debug Mode", - description=DESCRIPTIONS["joystick_debug_mode"], - initial_state=self._params.get_bool("JoystickDebugMode"), - callback=self._on_joystick_debug_mode, - ), - toggle_item( - "Longitudinal Maneuver Mode", - description="", - initial_state=self._params.get_bool("LongitudinalManeuverMode"), - callback=self._on_long_maneuver_mode, - ), - toggle_item( - "openpilot Longitudinal Control (Alpha)", - description="", - initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), - callback=self._on_alpha_long_enabled, - ), + self._adb_toggle, + self._ssh_toggle, + self._ssh_keys, + self._joystick_toggle, + self._long_maneuver_toggle, + self._alpha_long_toggle, ] self._scroller = Scroller(items, line_separator=True, spacing=0) @@ -55,7 +83,61 @@ class DeveloperLayout(Widget): def _render(self, rect): self._scroller.render(rect) - def _on_enable_adb(self): pass - def _on_joystick_debug_mode(self): pass - def _on_long_maneuver_mode(self): pass - def _on_alpha_long_enabled(self): pass + def show_event(self): + self._update_toggles() + + def _update_toggles(self): + # Hide non-release toggles on release builds + for item in (self._adb_toggle, self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle): + item.set_visible(not self._is_release) + + # CP gating + if ui_state.CP is not None: + alpha_avail = ui_state.CP.alphaLongitudinalAvailable + if not alpha_avail or self._is_release: + self._alpha_long_toggle.set_visible(False) + self._params.remove("AlphaLongitudinalEnabled") + else: + self._alpha_long_toggle.set_visible(True) + + self._long_maneuver_toggle.action_item.set_enabled(ui_state.has_longitudinal_control and ui_state.is_offroad) + else: + self._long_maneuver_toggle.action_item.set_enabled(False) + self._alpha_long_toggle.set_visible(False) + + # TODO: make a param control list item so we don't need to manage internal state as much here + # refresh toggles from params to mirror external changes + for key, item in ( + ("AdbEnabled", self._adb_toggle), + ("SshEnabled", self._ssh_toggle), + ("JoystickDebugMode", self._joystick_toggle), + ("LongitudinalManeuverMode", self._long_maneuver_toggle), + ("AlphaLongitudinalEnabled", self._alpha_long_toggle), + ): + item.action_item.set_state(self._params.get_bool(key)) + + def _update_state(self): + # Disable toggles that require onroad restart + # TODO: we can do an onroad cycle, but alpha long toggle requires a deinit function to re-enable radar and not fault + for item in (self._adb_toggle, self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle): + item.action_item.set_enabled(ui_state.is_offroad) + + def _on_enable_adb(self, state: bool): + self._params.put_bool("AdbEnabled", state) + + def _on_enable_ssh(self, state: bool): + self._params.put_bool("SshEnabled", state) + + def _on_joystick_debug_mode(self, state: bool): + self._params.put_bool("JoystickDebugMode", state) + self._params.put_bool("LongitudinalManeuverMode", False) + self._long_maneuver_toggle.action_item.set_state(False) + + def _on_long_maneuver_mode(self, state: bool): + self._params.put_bool("LongitudinalManeuverMode", state) + self._params.put_bool("JoystickDebugMode", False) + self._joystick_toggle.action_item.set_state(False) + + def _on_alpha_long_enabled(self, state: bool): + self._params.put_bool("AlphaLongitudinalEnabled", state) + self._update_toggles() diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 01f663d4b9..fddcf32fbd 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -34,6 +34,7 @@ class TogglesLayout(Widget): def __init__(self): super().__init__() self._params = Params() + self._is_release = self._params.get_bool("IsReleaseBranch") # param, title, desc, icon, needs_restart self._toggle_defs = { @@ -158,8 +159,6 @@ class TogglesLayout(Widget): "The Experimental mode logo will also be shown in the top right corner." ) - is_release = self._params.get_bool("IsReleaseBranch") - if ui_state.CP is not None: if ui_state.has_longitudinal_control: self._toggles["ExperimentalMode"].action_item.set_enabled(True) @@ -176,7 +175,7 @@ class TogglesLayout(Widget): long_desc = unavailable + " openpilot longitudinal control may come in a future update." if ui_state.CP.getAlphaLongitudinalAvailable(): - if is_release: + if self._is_release: long_desc = unavailable + " " + ("An alpha version of openpilot longitudinal control can be tested, along with " + "Experimental mode, on non-release branches.") else: diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index e77a2d4d7e..fb42b94d6d 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -76,7 +76,7 @@ def setup_settings_developer(click, pm: PubMaster): def setup_keyboard(click, pm: PubMaster): setup_settings_developer(click, pm) - click(1930, 270) + click(1930, 470) def setup_pair_device(click, pm: PubMaster): From b521a913ab4bb12a33d506efae7f8a939bff4ae2 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Oct 2025 01:18:07 -0700 Subject: [PATCH 136/341] raylib: confirm dialog uses HTML renderer 2 (#36297) * start * keep it simple * rm --- system/ui/widgets/confirm_dialog.py | 35 ++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index 606b04b6e9..e9d0cb53bd 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -3,27 +3,35 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.label import Label +from openpilot.system.ui.widgets.html_render import HtmlRenderer from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.widgets.scroller import Scroller -DIALOG_WIDTH = 1748 -DIALOG_HEIGHT = 690 +OUTER_MARGIN = 200 +RICH_OUTER_MARGIN = 100 BUTTON_HEIGHT = 160 MARGIN = 50 -TEXT_AREA_HEIGHT_REDUCTION = 200 +TEXT_PADDING = 10 BACKGROUND_COLOR = rl.Color(27, 27, 27, 255) class ConfirmDialog(Widget): - def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel"): + def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel", rich: bool = False): super().__init__() self._label = Label(text, 70, FontWeight.BOLD, text_color=rl.Color(201, 201, 201, 255)) + self._html_renderer = HtmlRenderer(text=text) self._cancel_button = Button(cancel_text, self._cancel_button_callback) self._confirm_button = Button(confirm_text, self._confirm_button_callback, button_style=ButtonStyle.PRIMARY) + self._rich = rich self._dialog_result = DialogResult.NO_ACTION self._cancel_text = cancel_text + self._scroller = Scroller([self._html_renderer], line_separator=False, spacing=0) def set_text(self, text): - self._label.set_text(text) + if not self._rich: + self._label.set_text(text) + else: + self._html_renderer.parse_html_content(text) def reset(self): self._dialog_result = DialogResult.NO_ACTION @@ -35,9 +43,11 @@ class ConfirmDialog(Widget): self._dialog_result = DialogResult.CONFIRM def _render(self, rect: rl.Rectangle): - dialog_x = (gui_app.width - DIALOG_WIDTH) / 2 - dialog_y = (gui_app.height - DIALOG_HEIGHT) / 2 - dialog_rect = rl.Rectangle(dialog_x, dialog_y, DIALOG_WIDTH, DIALOG_HEIGHT) + dialog_x = OUTER_MARGIN if not self._rich else RICH_OUTER_MARGIN + dialog_y = OUTER_MARGIN if not self._rich else RICH_OUTER_MARGIN + dialog_width = gui_app.width - 2 * dialog_x + dialog_height = gui_app.height - 2 * dialog_y + dialog_rect = rl.Rectangle(dialog_x, dialog_y, dialog_width, dialog_height) bottom = dialog_rect.y + dialog_rect.height button_width = (dialog_rect.width - 3 * MARGIN) // 2 @@ -49,8 +59,13 @@ class ConfirmDialog(Widget): rl.draw_rectangle_rec(dialog_rect, BACKGROUND_COLOR) - text_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y, dialog_rect.width - 2 * MARGIN, dialog_rect.height - TEXT_AREA_HEIGHT_REDUCTION) - self._label.render(text_rect) + text_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y + TEXT_PADDING, + dialog_rect.width - 2 * MARGIN, dialog_rect.height - BUTTON_HEIGHT - MARGIN - TEXT_PADDING * 2) + if not self._rich: + self._label.render(text_rect) + else: + self._html_renderer.set_rect(text_rect) + self._scroller.render(text_rect) if rl.is_key_pressed(rl.KeyboardKey.KEY_ENTER): self._dialog_result = DialogResult.CONFIRM From cac8d3f405c760cfcb8272b7065916b4da77456d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Oct 2025 01:40:29 -0700 Subject: [PATCH 137/341] raylib: fix missing showing dialog in setup/updater (#36298) * fix showing dialog * here for safety --- system/ui/reset.py | 4 +++- system/ui/setup.py | 4 +++- system/ui/updater.py | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/system/ui/reset.py b/system/ui/reset.py index a5cf1731dc..85ce4a858b 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -126,7 +126,9 @@ def main(): if mode == ResetMode.FORMAT: reset.start_reset() - for _ in gui_app.render(): + for showing_dialog in gui_app.render(): + if showing_dialog: + continue if not reset.render(rl.Rectangle(45, 200, gui_app.width - 90, gui_app.height - 245)): break diff --git a/system/ui/setup.py b/system/ui/setup.py index e0d737cb1c..4068af01e7 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -423,7 +423,9 @@ def main(): try: gui_app.init_window("Setup", 20) setup = Setup() - for _ in gui_app.render(): + for showing_dialog in gui_app.render(): + if showing_dialog: + continue setup.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) setup.close() except Exception as e: diff --git a/system/ui/updater.py b/system/ui/updater.py index 48903fa5bd..4184c5cb12 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -158,7 +158,9 @@ def main(): try: gui_app.init_window("System Update") updater = Updater(updater_path, manifest_path) - for _ in gui_app.render(): + for showing_dialog in gui_app.render(): + if showing_dialog: + continue updater.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) finally: # Make sure we clean up even if there's an error From 0f40afa357af84a04e65c8ed40e51c87dafa7920 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Oct 2025 01:41:11 -0700 Subject: [PATCH 138/341] raylib: fix black updater bg for network (#36299) fix black bg --- system/ui/updater.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/system/ui/updater.py b/system/ui/updater.py index 4184c5cb12..38a93687e0 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -113,8 +113,11 @@ class Updater(Widget): def render_wifi_screen(self, rect: rl.Rectangle): # Draw the Wi-Fi manager UI - wifi_rect = rl.Rectangle(MARGIN + 50, MARGIN, rect.width - MARGIN * 2 - 100, rect.height - MARGIN * 2 - BUTTON_HEIGHT - 20) - self.wifi_manager_ui.render(wifi_rect) + wifi_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, + rect.height - BUTTON_HEIGHT - MARGIN * 3) + rl.draw_rectangle_rounded(wifi_rect, 0.035, 10, rl.Color(51, 51, 51, 255)) + wifi_content_rect = rl.Rectangle(wifi_rect.x + 50, wifi_rect.y, wifi_rect.width - 100, wifi_rect.height) + self.wifi_manager_ui.render(wifi_content_rect) back_button_rect = rl.Rectangle(MARGIN, rect.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) self._back_button.render(back_button_rect) From ddbbcc6f5dab250cfeaec478cf819c9cec737632 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Oct 2025 03:03:35 -0700 Subject: [PATCH 139/341] raylib: add experimental mode + alpha long confirmation dialog + related fixes (#36295) * here's everything * just the dev part * same for exp mode! * use rich * fix br not working in p * html height needs to be different than content/text rect * fix confirmation * fix * fix 2.5s lag * clean up * use correct param * add offroad and engaged callback too * nl * lint --- selfdrive/ui/layouts/settings/developer.py | 37 +++++++++++++++---- selfdrive/ui/layouts/settings/toggles.py | 42 ++++++++++++++++++---- selfdrive/ui/ui_state.py | 25 ++++++++++--- system/ui/widgets/confirm_dialog.py | 8 +++-- system/ui/widgets/html_render.py | 2 ++ 5 files changed, 92 insertions(+), 22 deletions(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index 143bb2c262..32d278ee9b 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -4,6 +4,9 @@ from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.list_view import toggle_item from openpilot.system.ui.widgets.scroller import Scroller +from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog +from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.widgets import DialogResult # Description constants DESCRIPTIONS = { @@ -80,6 +83,9 @@ class DeveloperLayout(Widget): self._scroller = Scroller(items, line_separator=True, spacing=0) + # Toggles should be not available to change in onroad state + ui_state.add_offroad_transition_callback(self._update_toggles) + def _render(self, rect): self._scroller.render(rect) @@ -87,7 +93,10 @@ class DeveloperLayout(Widget): self._update_toggles() def _update_toggles(self): + ui_state.update_params() + # Hide non-release toggles on release builds + # TODO: we can do an onroad cycle, but alpha long toggle requires a deinit function to re-enable radar and not fault for item in (self._adb_toggle, self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle): item.set_visible(not self._is_release) @@ -100,7 +109,11 @@ class DeveloperLayout(Widget): else: self._alpha_long_toggle.set_visible(True) - self._long_maneuver_toggle.action_item.set_enabled(ui_state.has_longitudinal_control and ui_state.is_offroad) + long_man_enabled = ui_state.has_longitudinal_control and ui_state.is_offroad() + self._long_maneuver_toggle.action_item.set_enabled(long_man_enabled) + if not long_man_enabled: + self._long_maneuver_toggle.action_item.set_state(False) + self._params.put_bool("LongitudinalManeuverMode", False) else: self._long_maneuver_toggle.action_item.set_enabled(False) self._alpha_long_toggle.set_visible(False) @@ -116,12 +129,6 @@ class DeveloperLayout(Widget): ): item.action_item.set_state(self._params.get_bool(key)) - def _update_state(self): - # Disable toggles that require onroad restart - # TODO: we can do an onroad cycle, but alpha long toggle requires a deinit function to re-enable radar and not fault - for item in (self._adb_toggle, self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle): - item.action_item.set_enabled(ui_state.is_offroad) - def _on_enable_adb(self, state: bool): self._params.put_bool("AdbEnabled", state) @@ -139,5 +146,21 @@ class DeveloperLayout(Widget): self._joystick_toggle.action_item.set_state(False) def _on_alpha_long_enabled(self, state: bool): + if state: + def confirm_callback(result: int): + if result == DialogResult.CONFIRM: + self._params.put_bool("AlphaLongitudinalEnabled", True) + self._update_toggles() + else: + self._alpha_long_toggle.action_item.set_state(False) + + # show confirmation dialog + content = (f"

{self._alpha_long_toggle.title}


" + + f"

{self._alpha_long_toggle.description}

") + + dlg = ConfirmDialog(content, "Enable", rich=True) + gui_app.set_modal_overlay(dlg, callback=confirm_callback) + return + self._params.put_bool("AlphaLongitudinalEnabled", state) self._update_toggles() diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index fddcf32fbd..8fdbd6f9a6 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -3,6 +3,9 @@ from openpilot.common.params import Params, UnknownKeyName from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_item from openpilot.system.ui.widgets.scroller import Scroller +from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog +from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.widgets import DialogResult from openpilot.selfdrive.ui.ui_state import ui_state PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants @@ -131,6 +134,8 @@ class TogglesLayout(Widget): self._update_experimental_mode_icon() self._scroller = Scroller(list(self._toggles.values()), line_separator=True, spacing=0) + ui_state.add_engaged_transition_callback(self._update_toggles) + def _update_state(self): if ui_state.sm.updated["selfdriveState"]: personality = PERSONALITY_TO_INT[ui_state.sm["selfdriveState"].personality] @@ -138,15 +143,12 @@ class TogglesLayout(Widget): self._long_personality_setting.action_item.set_selected_button(personality) ui_state.personality = personality - # these toggles need restart, block while engaged - for toggle_def in self._toggle_defs: - if self._toggle_defs[toggle_def][3] and toggle_def not in self._locked_toggles: - self._toggles[toggle_def].action_item.set_enabled(not ui_state.engaged) - def show_event(self): self._update_toggles() def _update_toggles(self): + ui_state.update_params() + e2e_description = ( "openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " + "Experimental features are listed below:
" + @@ -174,7 +176,7 @@ class TogglesLayout(Widget): unavailable = "Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control." long_desc = unavailable + " openpilot longitudinal control may come in a future update." - if ui_state.CP.getAlphaLongitudinalAvailable(): + if ui_state.CP.alphaLongitudinalAvailable: if self._is_release: long_desc = unavailable + " " + ("An alpha version of openpilot longitudinal control can be tested, along with " + "Experimental mode, on non-release branches.") @@ -192,6 +194,11 @@ class TogglesLayout(Widget): for param in self._toggle_defs: self._toggles[param].action_item.set_state(self._params.get_bool(param)) + # these toggles need restart, block while engaged + for toggle_def in self._toggle_defs: + if self._toggle_defs[toggle_def][3] and toggle_def not in self._locked_toggles: + self._toggles[toggle_def].action_item.set_enabled(not ui_state.engaged) + def _render(self, rect): self._scroller.render(rect) @@ -199,9 +206,30 @@ class TogglesLayout(Widget): icon = "experimental.png" if self._toggles["ExperimentalMode"].action_item.get_state() else "experimental_white.png" self._toggles["ExperimentalMode"].set_icon(icon) + def _handle_experimental_mode_toggle(self, state: bool): + confirmed = self._params.get_bool("ExperimentalModeConfirmed") + if state and not confirmed: + def confirm_callback(result: int): + if result == DialogResult.CONFIRM: + self._params.put_bool("ExperimentalMode", True) + self._params.put_bool("ExperimentalModeConfirmed", True) + else: + self._toggles["ExperimentalMode"].action_item.set_state(False) + self._update_experimental_mode_icon() + + # show confirmation dialog + content = (f"

{self._toggles['ExperimentalMode'].title}


" + + f"

{self._toggles['ExperimentalMode'].description}

") + dlg = ConfirmDialog(content, "Enable", rich=True) + gui_app.set_modal_overlay(dlg, callback=confirm_callback) + else: + self._update_experimental_mode_icon() + self._params.put_bool("ExperimentalMode", state) + def _toggle_callback(self, state: bool, param: str): if param == "ExperimentalMode": - self._update_experimental_mode_icon() + self._handle_experimental_mode_toggle(state) + return self._params.put_bool(param, state) if self._toggle_defs[param][3]: diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index be83517980..f4b9e1a9be 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -74,7 +74,17 @@ class UIState: self.light_sensor: float = -1.0 self._param_update_time: float = 0.0 - self._update_params() + # Callbacks + self._offroad_transition_callbacks: list[Callable[[], None]] = [] + self._engaged_transition_callbacks: list[Callable[[], None]] = [] + + self.update_params() + + def add_offroad_transition_callback(self, callback: Callable[[], None]): + self._offroad_transition_callbacks.append(callback) + + def add_engaged_transition_callback(self, callback: Callable[[], None]): + self._engaged_transition_callbacks.append(callback) @property def engaged(self) -> bool: @@ -91,8 +101,7 @@ class UIState: self._update_state() self._update_status() if time.monotonic() - self._param_update_time > 5.0: - self._update_params() - self._param_update_time = time.monotonic() + self.update_params() device.update() def _update_state(self) -> None: @@ -131,6 +140,8 @@ class UIState: # Check for engagement state changes if self.engaged != self._engaged_prev: + for callback in self._engaged_transition_callbacks: + callback() self._engaged_prev = self.engaged # Handle onroad/offroad transition @@ -140,19 +151,23 @@ class UIState: self.started_frame = self.sm.frame self.started_time = time.monotonic() + for callback in self._offroad_transition_callbacks: + callback() + self._started_prev = self.started - def _update_params(self) -> None: + def update_params(self) -> None: self.is_metric = self.params.get_bool("IsMetric") # Update longitudinal control state - CP_bytes = self.params.get("CarParams") + CP_bytes = self.params.get("CarParamsPersistent") if CP_bytes is not None: self.CP = messaging.log_from_bytes(CP_bytes, car.CarParams) if self.CP.alphaLongitudinalAvailable: self.has_longitudinal_control = self.params.get_bool("AlphaLongitudinalEnabled") else: self.has_longitudinal_control = self.CP.openpilotLongitudinalControl + self._param_update_time = time.monotonic() class Device: diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index e9d0cb53bd..789ae53f3d 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -3,7 +3,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.label import Label -from openpilot.system.ui.widgets.html_render import HtmlRenderer +from openpilot.system.ui.widgets.html_render import HtmlRenderer, ElementType from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.scroller import Scroller @@ -19,7 +19,7 @@ class ConfirmDialog(Widget): def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel", rich: bool = False): super().__init__() self._label = Label(text, 70, FontWeight.BOLD, text_color=rl.Color(201, 201, 201, 255)) - self._html_renderer = HtmlRenderer(text=text) + self._html_renderer = HtmlRenderer(text=text, text_size={ElementType.P: 50}) self._cancel_button = Button(cancel_text, self._cancel_button_callback) self._confirm_button = Button(confirm_text, self._confirm_button_callback, button_style=ButtonStyle.PRIMARY) self._rich = rich @@ -64,7 +64,9 @@ class ConfirmDialog(Widget): if not self._rich: self._label.render(text_rect) else: - self._html_renderer.set_rect(text_rect) + html_rect = rl.Rectangle(text_rect.x, text_rect.y, text_rect.width, + self._html_renderer.get_total_height(int(text_rect.width))) + self._html_renderer.set_rect(html_rect) self._scroller.render(text_rect) if rl.is_key_pressed(rl.KeyboardKey.KEY_ENTER): diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index 8d713ee7ea..938867f748 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -121,6 +121,8 @@ class HtmlRenderer(Widget): is_start_tag, is_end_tag, tag = is_tag(token) if tag is not None: if tag == ElementType.BR: + # Close current tag and add a line break + close_tag() self._add_element(ElementType.BR, "") elif is_start_tag or is_end_tag: From f04ee804529b71c5976fe24fcc0902191befa223 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Oct 2025 03:57:12 -0700 Subject: [PATCH 140/341] raylib: implement calibration description (#36300) * this is all cursor * also cursor * inline reset calib * calib desc * way better * huh * clean up * rcvr * stash changes to change params * Revert "stash changes to change params" This reverts commit ee998f04c4235ed20493b83e35c9f28e182d89b0. --- selfdrive/ui/layouts/settings/developer.py | 2 - selfdrive/ui/layouts/settings/device.py | 82 ++++++++++++++++++---- system/ui/widgets/confirm_dialog.py | 8 +-- system/ui/widgets/list_view.py | 8 +++ 4 files changed, 79 insertions(+), 21 deletions(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index 32d278ee9b..d3a829df81 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -70,8 +70,6 @@ class DeveloperLayout(Widget): callback=self._on_alpha_long_enabled, ) - self._alpha_long_toggle.set_description(self._alpha_long_toggle.description + " Changing this setting will restart openpilot if the car is powered on.") - items = [ self._adb_toggle, self._ssh_toggle, diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 62abd2b998..758200c0ad 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -1,8 +1,11 @@ import os import json +import math +from cereal import messaging, log from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide @@ -20,10 +23,7 @@ from openpilot.system.ui.widgets.scroller import Scroller DESCRIPTIONS = { 'pair_device': "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.", 'driver_camera': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)", - 'reset_calibration': ( - "openpilot requires the device to be mounted within 4° left or right and within 5° " + - "up or 9° down. openpilot is continuously calibrating, resetting is rarely required." - ), + 'reset_calibration': "openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down.", 'review_guide': "Review the rules, features, and limitations of openpilot", } @@ -49,12 +49,15 @@ class DeviceLayout(Widget): self._pair_device_btn = button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device) self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired()) + self._reset_calib_btn = button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt) + self._reset_calib_btn.set_description_opened_callback(self._update_calib_description) + items = [ text_item("Dongle ID", dongle_id), text_item("Serial", serial), self._pair_device_btn, button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), - button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt), + self._reset_calib_btn, button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide), regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), @@ -95,19 +98,68 @@ class DeviceLayout(Widget): gui_app.set_modal_overlay(alert_dialog("Disengage to Reset Calibration")) return + def reset_calibration(result: int): + # Check engaged again in case it changed while the dialog was open + if ui_state.engaged or result != DialogResult.CONFIRM: + return + + self._params.remove("CalibrationParams") + self._params.remove("LiveTorqueParameters") + self._params.remove("LiveParameters") + self._params.remove("LiveParametersV2") + self._params.remove("LiveDelay") + self._params.put_bool("OnroadCycleRequested", True) + self._update_calib_description() + dialog = ConfirmDialog("Are you sure you want to reset calibration?", "Reset") - gui_app.set_modal_overlay(dialog, callback=self._reset_calibration) + gui_app.set_modal_overlay(dialog, callback=reset_calibration) - def _reset_calibration(self, result: int): - if ui_state.engaged or result != DialogResult.CONFIRM: - return + def _update_calib_description(self): + desc = DESCRIPTIONS['reset_calibration'] - self._params.remove("CalibrationParams") - self._params.remove("LiveTorqueParameters") - self._params.remove("LiveParameters") - self._params.remove("LiveParametersV2") - self._params.remove("LiveDelay") - self._params.put_bool("OnroadCycleRequested", True) + calib_bytes = self._params.get("CalibrationParams") + if calib_bytes: + try: + calib = messaging.log_from_bytes(calib_bytes, log.Event).liveCalibration + + if calib.calStatus != log.LiveCalibrationData.Status.uncalibrated: + pitch = math.degrees(calib.rpyCalib[1]) + yaw = math.degrees(calib.rpyCalib[2]) + desc += f" Your device is pointed {abs(pitch):.1f}° {'down' if pitch > 0 else 'up'} and {abs(yaw):.1f}° {'left' if yaw > 0 else 'right'}." + except Exception: + cloudlog.exception("invalid CalibrationParams") + + lag_perc = 0 + lag_bytes = self._params.get("LiveDelay") + if lag_bytes: + try: + lag_perc = messaging.log_from_bytes(lag_bytes, log.Event).liveDelay.calPerc + except Exception: + cloudlog.exception("invalid LiveDelay") + if lag_perc < 100: + desc += f"

Steering lag calibration is {lag_perc}% complete." + else: + desc += "

Steering lag calibration is complete." + + torque_bytes = self._params.get("LiveTorqueParameters") + if torque_bytes: + try: + torque = messaging.log_from_bytes(torque_bytes, log.Event).liveTorqueParameters + # don't add for non-torque cars + if torque.useParams: + torque_perc = torque.calPerc + if torque_perc < 100: + desc += f" Steering torque response calibration is {torque_perc}% complete." + else: + desc += " Steering torque response calibration is complete." + except Exception: + cloudlog.exception("invalid LiveTorqueParameters") + + desc += "

" + desc += ("openpilot is continuously calibrating, resetting is rarely required. " + + "Resetting calibration will restart openpilot if the car is powered on.") + + self._reset_calib_btn.set_description(desc) def _reboot_prompt(self): if ui_state.engaged: diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index 789ae53f3d..6d3e143b53 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -78,12 +78,12 @@ class ConfirmDialog(Widget): self._confirm_button.render(confirm_button) self._cancel_button.render(cancel_button) else: - centered_button_x = dialog_rect.x + (dialog_rect.width - button_width) / 2 - centered_confirm_button = rl.Rectangle(centered_button_x, button_y, button_width, BUTTON_HEIGHT) - self._confirm_button.render(centered_confirm_button) + full_button_width = dialog_rect.width - 2 * MARGIN + full_confirm_button = rl.Rectangle(dialog_rect.x + MARGIN, button_y, full_button_width, BUTTON_HEIGHT) + self._confirm_button.render(full_confirm_button) return self._dialog_result -def alert_dialog(message: str, button_text: str = "OK"): +def alert_dialog(message: str, button_text: str = "Ok"): return ConfirmDialog(message, button_text, cancel_text="") diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index bbf66c6555..d203ec8139 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -268,6 +268,7 @@ class ListItem(Widget): self._description = description self.description_visible = description_visible self.callback = callback + self.description_opened_callback: Callable | None = None self.action_item = action_item self.set_rect(rl.Rectangle(0, 0, ITEM_BASE_WIDTH, ITEM_BASE_HEIGHT)) @@ -280,6 +281,9 @@ class ListItem(Widget): # Cached properties for performance self._prev_description: str | None = self.description + def set_description_opened_callback(self, callback: Callable) -> None: + self.description_opened_callback = callback + def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: super().set_touch_valid_callback(touch_callback) if self.action_item: @@ -302,6 +306,10 @@ class ListItem(Widget): if self.description: self.description_visible = not self.description_visible + # do callback first in case receiver changes description + if self.description_visible and self.description_opened_callback is not None: + self.description_opened_callback() + content_width = int(self._rect.width - ITEM_PADDING * 2) self._rect.height = self.get_item_height(self._font, content_width) From 4ff77a47525174f42e6d1af385336ccb9150a871 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Oct 2025 04:10:24 -0700 Subject: [PATCH 141/341] raylib: fix DMoji (#36301) * wow first time cursor made a tiny change to fix a problem! * rm --- selfdrive/ui/onroad/driver_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/onroad/driver_state.py b/selfdrive/ui/onroad/driver_state.py index a25d9bd316..69ed5f458f 100644 --- a/selfdrive/ui/onroad/driver_state.py +++ b/selfdrive/ui/onroad/driver_state.py @@ -222,7 +222,7 @@ class DriverStateRenderer(Widget): radius_y = arc_data.height / 2 x_coords = center_x + np.cos(angles) * radius_x - y_coords = center_y + np.sin(angles) * radius_y + y_coords = center_y - np.sin(angles) * radius_y arc_lines = self.h_arc_lines if is_horizontal else self.v_arc_lines for i, (x_coord, y_coord) in enumerate(zip(x_coords, y_coords, strict=True)): From fdcf8b592e5596386c5639875ce260b19267cc2f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Oct 2025 21:07:36 -0700 Subject: [PATCH 142/341] raylib: reset scrollers and description expansions on show event (#36304) * scroll up on hide * switch to show * dismiss descriptions too! * all is show * all is show * clean up * visible items helper * Revert "visible items helper" This reverts commit e64f05b69155483aa0f3d74bd511f5d7c1ecfb79. * reset --- selfdrive/ui/layouts/home.py | 15 ++++++++++++--- selfdrive/ui/layouts/settings/developer.py | 1 + selfdrive/ui/layouts/settings/device.py | 3 +++ selfdrive/ui/layouts/settings/firehose.py | 3 +++ selfdrive/ui/layouts/settings/software.py | 3 +++ selfdrive/ui/layouts/settings/toggles.py | 1 + selfdrive/ui/widgets/offroad_alerts.py | 3 +++ system/ui/lib/scroll_panel.py | 5 +++++ system/ui/widgets/list_view.py | 10 ++++++++-- system/ui/widgets/scroller.py | 12 ++++++++++++ 10 files changed, 51 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index f7f57c680d..9243d8ea1f 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -36,6 +36,8 @@ class HomeLayout(Widget): self.update_alert = UpdateAlert() self.offroad_alert = OffroadAlert() + self._layout_widgets = {HomeLayoutState.UPDATE: self.update_alert, HomeLayoutState.ALERTS: self.offroad_alert} + self.current_state = HomeLayoutState.HOME self.last_refresh = 0 self.settings_callback: callable | None = None @@ -71,6 +73,13 @@ class HomeLayout(Widget): self.settings_callback = callback def _set_state(self, state: HomeLayoutState): + # propagate show/hide events + if state != self.current_state: + if state in self._layout_widgets: + self._layout_widgets[state].show_event() + if self.current_state in self._layout_widgets: + self._layout_widgets[self.current_state].hide_event() + self.current_state = state def _render(self, rect: rl.Rectangle): @@ -201,11 +210,11 @@ class HomeLayout(Widget): # Show panels on transition from no alert/update to any alerts/update if not update_available and not alerts_present: - self.current_state = HomeLayoutState.HOME + self._set_state(HomeLayoutState.HOME) elif update_available and ((not self._prev_update_available) or (not alerts_present and self.current_state == HomeLayoutState.ALERTS)): - self.current_state = HomeLayoutState.UPDATE + self._set_state(HomeLayoutState.UPDATE) elif alerts_present and ((not self._prev_alerts_present) or (not update_available and self.current_state == HomeLayoutState.UPDATE)): - self.current_state = HomeLayoutState.ALERTS + self._set_state(HomeLayoutState.ALERTS) self.update_available = update_available self.alert_count = alert_count diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index d3a829df81..bc8fc953c4 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -88,6 +88,7 @@ class DeveloperLayout(Widget): self._scroller.render(rect) def show_event(self): + self._scroller.show_event() self._update_toggles() def _update_toggles(self): diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 758200c0ad..286ba36f07 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -66,6 +66,9 @@ class DeviceLayout(Widget): regulatory_btn.set_visible(TICI) return items + def show_event(self): + self._scroller.show_event() + def _render(self, rect): self._scroller.render(rect) diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py index 7e1d3b61ba..353a9d976f 100644 --- a/selfdrive/ui/layouts/settings/firehose.py +++ b/selfdrive/ui/layouts/settings/firehose.py @@ -49,6 +49,9 @@ class FirehoseLayout(Widget): self.update_thread.start() self.last_update_time = 0 + def show_event(self): + self.scroll_panel.set_offset(0) + def _get_segment_count(self) -> int: stats = self.params.get(self.PARAM_KEY) if not stats: diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 7aebc609f8..a337b9034d 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -69,6 +69,9 @@ class SoftwareLayout(Widget): ] return items + def show_event(self): + self._scroller.show_event() + def _render(self, rect): self._scroller.render(rect) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 8fdbd6f9a6..c75cc7574d 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -144,6 +144,7 @@ class TogglesLayout(Widget): ui_state.personality = personality def show_event(self): + self._scroller.show_event() self._update_toggles() def _update_toggles(self): diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 1045971636..49edafa0f1 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -117,6 +117,9 @@ class AbstractAlert(Widget, ABC): self.scroll_panel_rect = rl.Rectangle(0, 0, 0, 0) self.scroll_panel = GuiScrollPanel() + def show_event(self): + self.scroll_panel.set_offset(0) + def set_dismiss_callback(self, callback: Callable): self.dismiss_callback = callback self.dismiss_btn.set_click_callback(self.dismiss_callback) diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index d0d59df1ff..f0031138fe 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -123,3 +123,8 @@ class GuiScrollPanel: def is_touch_valid(self): return self._scroll_state == ScrollState.IDLE and abs(self._velocity_filter_y.x) < MIN_VELOCITY_FOR_CLICKING + + def set_offset(self, position: float) -> None: + self._offset_filter_y.x = position + self._velocity_filter_y.x = 0.0 + self._scroll_state = ScrollState.IDLE diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index d203ec8139..b1c4b44df1 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -281,6 +281,9 @@ class ListItem(Widget): # Cached properties for performance self._prev_description: str | None = self.description + def show_event(self): + self._set_description_visible(False) + def set_description_opened_callback(self, callback: Callable) -> None: self.description_opened_callback = callback @@ -304,8 +307,11 @@ class ListItem(Widget): # Click was on right item, don't toggle description return - if self.description: - self.description_visible = not self.description_visible + self._set_description_visible(not self.description_visible) + + def _set_description_visible(self, visible: bool): + if self.description and self.description_visible != visible: + self.description_visible = visible # do callback first in case receiver changes description if self.description_visible and self.description_opened_callback is not None: self.description_opened_callback() diff --git a/system/ui/widgets/scroller.py b/system/ui/widgets/scroller.py index c76f30d196..f19a6fbfdb 100644 --- a/system/ui/widgets/scroller.py +++ b/system/ui/widgets/scroller.py @@ -76,3 +76,15 @@ class Scroller(Widget): item.render() rl.end_scissor_mode() + + def show_event(self): + super().show_event() + # Reset to top + self.scroll_panel.set_offset(0) + for item in self._items: + item.show_event() + + def hide_event(self): + super().hide_event() + for item in self._items: + item.hide_event() From b6dbb0fd8d79915f6f67d70500d5c971f72f33df Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 03:29:24 -0700 Subject: [PATCH 143/341] raylib: font sizes from QT should match (#36306) * pt 2 * fix line height * fixup html renderer * fix sidebar * fix label line height * firehose fixups * fix ssh value font styling * fixup inputbot * do experimental mode * pairing dialog numbers * fix radius for prime user * add emoji to firehose mode * full screen registration * fix registration btn size * fix update and alerts * debugging * Revert "debugging" This reverts commit 0095372e9479d8c727bcc8a78061f582d852133d. * firehose styling * fix offroad alerts missing bottom spacing expansion * huge oof * huge oof --- selfdrive/ui/layouts/home.py | 13 ++-- selfdrive/ui/layouts/main.py | 1 + selfdrive/ui/layouts/settings/firehose.py | 77 ++++++++--------------- selfdrive/ui/layouts/sidebar.py | 4 +- selfdrive/ui/widgets/exp_mode_button.py | 11 +--- selfdrive/ui/widgets/offroad_alerts.py | 20 +++--- selfdrive/ui/widgets/pairing_dialog.py | 4 +- selfdrive/ui/widgets/prime.py | 2 +- selfdrive/ui/widgets/setup.py | 19 +++--- selfdrive/ui/widgets/ssh_key.py | 10 +-- system/ui/lib/application.py | 15 +++++ system/ui/lib/scroll_panel.py | 4 ++ system/ui/lib/text_measure.py | 3 +- system/ui/widgets/html_render.py | 32 +++++----- system/ui/widgets/inputbox.py | 6 +- system/ui/widgets/label.py | 6 +- 16 files changed, 113 insertions(+), 114 deletions(-) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 9243d8ea1f..70960ee22f 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -68,6 +68,7 @@ class HomeLayout(Widget): def _setup_callbacks(self): self.update_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME)) self.offroad_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME)) + self._exp_mode_button.set_click_callback(lambda: self.settings_callback() if self.settings_callback else None) def set_settings_callback(self, callback: Callable): self.settings_callback = callback @@ -147,9 +148,9 @@ class HomeLayout(Widget): rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color) text = "UPDATE" - text_width = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE).x - text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_width) // 2 - text_y = self.update_notif_rect.y + (self.update_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2 + text_size = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE) + text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_size.x) // 2 + text_y = self.update_notif_rect.y + (self.update_notif_rect.height - text_size.y) // 2 rl.draw_text_ex(font, text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE) # Alert notification button @@ -161,9 +162,9 @@ class HomeLayout(Widget): rl.draw_rectangle_rounded(self.alert_notif_rect, 0.3, 10, highlight_color) alert_text = f"{self.alert_count} ALERT{'S' if self.alert_count > 1 else ''}" - text_width = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE).x - text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_width) // 2 - text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2 + text_size = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE) + text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_size.x) // 2 + text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - text_size.y) // 2 rl.draw_text_ex(font, alert_text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE) # Version text (right aligned) diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index a2401ef8be..702854f98a 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -50,6 +50,7 @@ class MainLayout(Widget): on_flag=self._on_bookmark_clicked, open_settings=lambda: self.open_settings(PanelType.TOGGLES)) self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE)) + self._layouts[MainState.HOME].set_settings_callback(lambda: self.open_settings(PanelType.TOGGLES)) self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state) self._layouts[MainState.ONROAD].set_click_callback(self._on_onroad_clicked) device.add_interactive_timeout_callback(self._set_mode_for_state) diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py index 353a9d976f..e8eaaa44f2 100644 --- a/selfdrive/ui/layouts/settings/firehose.py +++ b/selfdrive/ui/layouts/settings/firehose.py @@ -7,7 +7,8 @@ from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget @@ -21,7 +22,7 @@ DESCRIPTION = ( ) INSTRUCTIONS = ( "For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n" - + "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n" + + "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n\n" + "Frequently Asked Questions\n\n" + "Does it matter how or where I drive? Nope, just drive as you normally would.\n\n" + "Do all of my segments get pulled in Firehose Mode? No, we selectively pull a subset of your segments.\n\n" @@ -43,6 +44,7 @@ class FirehoseLayout(Widget): self.params = Params() self.segment_count = self._get_segment_count() self.scroll_panel = GuiScrollPanel() + self._content_height = 0 self.running = True self.update_thread = threading.Thread(target=self._update_loop, daemon=True) @@ -69,88 +71,61 @@ class FirehoseLayout(Widget): def _render(self, rect: rl.Rectangle): # Calculate content dimensions - content_width = rect.width - 80 - content_height = self._calculate_content_height(int(content_width)) - content_rect = rl.Rectangle(rect.x, rect.y, rect.width, content_height) + content_rect = rl.Rectangle(rect.x, rect.y, rect.width, self._content_height) # Handle scrolling and render with clipping scroll_offset = self.scroll_panel.update(rect, content_rect) rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - self._render_content(rect, scroll_offset) + self._content_height = self._render_content(rect, scroll_offset) rl.end_scissor_mode() - def _calculate_content_height(self, content_width: int) -> int: - height = 80 # Top margin - - # Title - height += 100 + 40 - - # Description - desc_font = gui_app.font(FontWeight.NORMAL) - desc_lines = wrap_text(desc_font, DESCRIPTION, 45, content_width) - height += len(desc_lines) * 45 + 40 - - # Status section - height += 32 # Separator - status_text, _ = self._get_status() - status_lines = wrap_text(gui_app.font(FontWeight.BOLD), status_text, 60, content_width) - height += len(status_lines) * 60 + 20 - - # Contribution count (if available) - if self.segment_count > 0: - contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far." - contrib_lines = wrap_text(gui_app.font(FontWeight.BOLD), contrib_text, 52, content_width) - height += len(contrib_lines) * 52 + 20 - - # Instructions section - height += 32 # Separator - inst_lines = wrap_text(gui_app.font(FontWeight.NORMAL), INSTRUCTIONS, 40, content_width) - height += len(inst_lines) * 40 + 40 # Bottom margin - - return height - - def _render_content(self, rect: rl.Rectangle, scroll_offset: float): + def _render_content(self, rect: rl.Rectangle, scroll_offset: float) -> int: x = int(rect.x + 40) y = int(rect.y + 40 + scroll_offset) w = int(rect.width - 80) - # Title + # Title (centered) title_font = gui_app.font(FontWeight.MEDIUM) - rl.draw_text_ex(title_font, TITLE, rl.Vector2(x, y), 100, 0, rl.WHITE) - y += 140 + text_width = measure_text_cached(title_font, TITLE, 100).x + title_x = rect.x + (rect.width - text_width) / 2 + rl.draw_text_ex(title_font, TITLE, rl.Vector2(title_x, y), 100, 0, rl.WHITE) + y += 200 # Description y = self._draw_wrapped_text(x, y, w, DESCRIPTION, gui_app.font(FontWeight.NORMAL), 45, rl.WHITE) - y += 40 + y += 40 + 20 # Separator rl.draw_rectangle(x, y, w, 2, self.GRAY) - y += 30 + y += 30 + 20 # Status status_text, status_color = self._get_status() y = self._draw_wrapped_text(x, y, w, status_text, gui_app.font(FontWeight.BOLD), 60, status_color) - y += 20 + y += 20 + 20 # Contribution count (if available) if self.segment_count > 0: contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far." y = self._draw_wrapped_text(x, y, w, contrib_text, gui_app.font(FontWeight.BOLD), 52, rl.WHITE) - y += 20 + y += 20 + 20 # Separator rl.draw_rectangle(x, y, w, 2, self.GRAY) - y += 30 + y += 30 + 20 # Instructions - self._draw_wrapped_text(x, y, w, INSTRUCTIONS, gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY) + y = self._draw_wrapped_text(x, y, w, INSTRUCTIONS, gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY) - def _draw_wrapped_text(self, x, y, width, text, font, size, color): - wrapped = wrap_text(font, text, size, width) + # bottom margin + remove effect of scroll offset + return int(round(y - self.scroll_panel.offset + 40)) + + def _draw_wrapped_text(self, x, y, width, text, font, font_size, color): + wrapped = wrap_text(font, text, font_size, width) for line in wrapped: - rl.draw_text_ex(font, line, rl.Vector2(x, y), size, 0, color) - y += size - return y + rl.draw_text_ex(font, line, rl.Vector2(x, y), font_size, 0, color) + y += font_size * FONT_SCALE + return round(y) def _get_status(self) -> tuple[str, rl.Color]: network_type = ui_state.sm["deviceState"].networkType diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 8fa38145d3..c499d35d4d 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from collections.abc import Callable from cereal import log from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos +from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, FONT_SCALE from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget @@ -218,7 +218,7 @@ class Sidebar(Widget): # Draw label and value labels = [metric.label, metric.value] - text_y = metric_rect.y + (metric_rect.height / 2 - len(labels) * FONT_SIZE) + text_y = metric_rect.y + (metric_rect.height / 2 - len(labels) * FONT_SIZE * FONT_SCALE) for text in labels: text_size = measure_text_cached(self._font_bold, text, FONT_SIZE) text_y += text_size.y diff --git a/selfdrive/ui/widgets/exp_mode_button.py b/selfdrive/ui/widgets/exp_mode_button.py index 9618768957..40a649899d 100644 --- a/selfdrive/ui/widgets/exp_mode_button.py +++ b/selfdrive/ui/widgets/exp_mode_button.py @@ -1,6 +1,6 @@ import pyray as rl from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.widgets import Widget @@ -9,7 +9,7 @@ class ExperimentalModeButton(Widget): super().__init__() self.img_width = 80 - self.horizontal_padding = 50 + self.horizontal_padding = 25 self.button_height = 125 self.params = Params() @@ -31,11 +31,6 @@ class ExperimentalModeButton(Widget): rl.draw_rectangle_gradient_h(int(rect.x), int(rect.y), int(rect.width), int(rect.height), start_color, end_color) - def _handle_mouse_release(self, mouse_pos): - self.experimental_mode = not self.experimental_mode - # TODO: Opening settings for ExperimentalMode - self.params.put_bool("ExperimentalMode", self.experimental_mode) - def _render(self, rect): rl.draw_rectangle_rounded(rect, 0.08, 20, rl.WHITE) @@ -51,7 +46,7 @@ class ExperimentalModeButton(Widget): # Draw text label (left aligned) text = "EXPERIMENTAL MODE ON" if self.experimental_mode else "CHILL MODE ON" text_x = rect.x + self.horizontal_padding - text_y = rect.y + rect.height / 2 - 45 // 2 # Center vertically + text_y = rect.y + rect.height / 2 - 45 * FONT_SCALE // 2 # Center vertically rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), text, rl.Vector2(int(text_x), int(text_y)), 45, 0, rl.BLACK) diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 49edafa0f1..0b3ca8176f 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -5,7 +5,7 @@ from collections.abc import Callable from dataclasses import dataclass from openpilot.common.params import Params from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text @@ -65,8 +65,8 @@ class ActionButton(Widget): def set_text(self, text: str): self._text = text - self._text_width = measure_text_cached(gui_app.font(FontWeight.MEDIUM), self._text, AlertConstants.FONT_SIZE).x - self._rect.width = max(self._text_width + 60 * 2, self._min_width) + self._text_size = measure_text_cached(gui_app.font(FontWeight.MEDIUM), self._text, AlertConstants.FONT_SIZE) + self._rect.width = max(self._text_size.x + 60 * 2, self._min_width) self._rect.height = AlertConstants.BUTTON_HEIGHT def _render(self, _): @@ -79,8 +79,8 @@ class ActionButton(Widget): # center text color = rl.WHITE if self._style == ButtonStyle.DARK else rl.BLACK - text_x = int(self._rect.x + (self._rect.width - self._text_width) // 2) - text_y = int(self._rect.y + (self._rect.height - AlertConstants.FONT_SIZE) // 2) + text_x = int(self._rect.x + (self._rect.width - self._text_size.x) // 2) + text_y = int(self._rect.y + (self._rect.height - self._text_size.y) // 2) rl.draw_text_ex(self._font, self._text, rl.Vector2(text_x, text_y), AlertConstants.FONT_SIZE, 0, color) @@ -250,9 +250,9 @@ class OffroadAlert(AbstractAlert): text_width = int(self.content_rect.width - (AlertConstants.ALERT_INSET * 2)) wrapped_lines = wrap_text(font, alert_data.text, AlertConstants.FONT_SIZE, text_width) line_count = len(wrapped_lines) - text_height = line_count * (AlertConstants.FONT_SIZE + 5) + text_height = line_count * (AlertConstants.FONT_SIZE * FONT_SCALE) alert_item_height = max(text_height + (AlertConstants.ALERT_INSET * 2), AlertConstants.ALERT_HEIGHT) - total_height += alert_item_height + AlertConstants.ALERT_SPACING + total_height += round(alert_item_height + AlertConstants.ALERT_SPACING) if total_height > 20: total_height = total_height - AlertConstants.ALERT_SPACING + 20 @@ -278,7 +278,7 @@ class OffroadAlert(AbstractAlert): text_width = int(content_rect.width - (AlertConstants.ALERT_INSET * 2)) wrapped_lines = wrap_text(font, alert_data.text, AlertConstants.FONT_SIZE, text_width) line_count = len(wrapped_lines) - text_height = line_count * (AlertConstants.FONT_SIZE + 5) + text_height = line_count * (AlertConstants.FONT_SIZE * FONT_SCALE) alert_item_height = max(text_height + (AlertConstants.ALERT_INSET * 2), AlertConstants.ALERT_HEIGHT) alert_rect = rl.Rectangle( @@ -298,13 +298,13 @@ class OffroadAlert(AbstractAlert): rl.draw_text_ex( font, line, - rl.Vector2(text_x, text_y + i * (AlertConstants.FONT_SIZE + 5)), + rl.Vector2(text_x, text_y + i * AlertConstants.FONT_SIZE * FONT_SCALE), AlertConstants.FONT_SIZE, 0, AlertColors.TEXT, ) - y_offset += alert_item_height + AlertConstants.ALERT_SPACING + y_offset += round(alert_item_height + AlertConstants.ALERT_SPACING) class UpdateAlert(AbstractAlert): diff --git a/selfdrive/ui/widgets/pairing_dialog.py b/selfdrive/ui/widgets/pairing_dialog.py index 64e1b701f1..252a7dd94d 100644 --- a/selfdrive/ui/widgets/pairing_dialog.py +++ b/selfdrive/ui/widgets/pairing_dialog.py @@ -145,8 +145,8 @@ class PairingDialog(Widget): # Circle and number rl.draw_circle(int(circle_x), int(circle_y), circle_radius, rl.Color(70, 70, 70, 255)) number = str(i + 1) - number_width = measure_text_cached(font, number, 30).x - rl.draw_text_ex(font, number, (int(circle_x - number_width // 2), int(circle_y - 15)), 30, 0, rl.WHITE) + number_size = measure_text_cached(font, number, 30) + rl.draw_text_ex(font, number, (int(circle_x - number_size.x // 2), int(circle_y - number_size.y // 2)), 30, 0, rl.WHITE) # Text rl.draw_text_ex(font, "\n".join(wrapped), rl.Vector2(text_x, y), 47, 0.0, rl.BLACK) diff --git a/selfdrive/ui/widgets/prime.py b/selfdrive/ui/widgets/prime.py index 6b601f6dff..1e31ac8a1a 100644 --- a/selfdrive/ui/widgets/prime.py +++ b/selfdrive/ui/widgets/prime.py @@ -52,7 +52,7 @@ class PrimeWidget(Widget): def _render_for_prime_user(self, rect: rl.Rectangle): """Renders the prime user widget with subscription status.""" - rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 230), 0.02, 10, self.PRIME_BG_COLOR) + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 230), 0.05, 10, self.PRIME_BG_COLOR) x = rect.x + 56 y = rect.y + 40 diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index e4e44b7d04..dfaa3e4008 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -1,10 +1,11 @@ import pyray as rl from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle +from openpilot.system.ui.widgets.label import Label class SetupWidget(Widget): @@ -15,6 +16,7 @@ class SetupWidget(Widget): self._pair_device_btn = Button("Pair device", self._show_pairing, button_style=ButtonStyle.PRIMARY) self._open_settings_btn = Button("Open", lambda: self._open_settings_callback() if self._open_settings_callback else None, button_style=ButtonStyle.PRIMARY) + self._firehose_label = Label("🔥Firehose Mode 🔥", font_weight=FontWeight.MEDIUM, font_size=64) def set_open_settings_callback(self, callback): self._open_settings_callback = callback @@ -28,7 +30,7 @@ class SetupWidget(Widget): def _render_registration(self, rect: rl.Rectangle): """Render registration prompt.""" - rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 630), 0.02, 20, rl.Color(51, 51, 51, 255)) + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, rect.height), 0.02, 20, rl.Color(51, 51, 51, 255)) x = rect.x + 64 y = rect.y + 48 @@ -45,15 +47,15 @@ class SetupWidget(Widget): wrapped = wrap_text(light_font, desc, 50, int(w)) for line in wrapped: rl.draw_text_ex(light_font, line, rl.Vector2(x, y), 50, 0, rl.WHITE) - y += 50 + y += 50 * FONT_SCALE - button_rect = rl.Rectangle(x, y + 50, w, 128) + button_rect = rl.Rectangle(x, y + 30, w, 200) self._pair_device_btn.render(button_rect) def _render_firehose_prompt(self, rect: rl.Rectangle): """Render firehose prompt widget.""" - rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 450), 0.02, 20, rl.Color(51, 51, 51, 255)) + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 500), 0.02, 20, rl.Color(51, 51, 51, 255)) # Content margins (56, 40, 56, 40) x = rect.x + 56 @@ -62,9 +64,8 @@ class SetupWidget(Widget): spacing = 42 # Title with fire emojis - title_font = gui_app.font(FontWeight.MEDIUM) - title_text = "Firehose Mode" - rl.draw_text_ex(title_font, title_text, rl.Vector2(x, y), 64, 0, rl.WHITE) + # TODO: fix Label centering with emojis + self._firehose_label.render(rl.Rectangle(x - 40, y, w, 64)) y += 64 + spacing # Description @@ -74,7 +75,7 @@ class SetupWidget(Widget): for line in wrapped_desc: rl.draw_text_ex(desc_font, line, rl.Vector2(x, y), 40, 0, rl.WHITE) - y += 40 + y += 40 * FONT_SCALE y += spacing diff --git a/selfdrive/ui/widgets/ssh_key.py b/selfdrive/ui/widgets/ssh_key.py index 370141bd64..dc3c5a4a76 100644 --- a/selfdrive/ui/widgets/ssh_key.py +++ b/selfdrive/ui/widgets/ssh_key.py @@ -21,6 +21,8 @@ from openpilot.system.ui.widgets.list_view import ( BUTTON_WIDTH, ) +VALUE_FONT_SIZE = 48 + class SshKeyActionState(Enum): LOADING = "LOADING" @@ -38,7 +40,7 @@ class SshKeyAction(ItemAction): self._keyboard = Keyboard(min_text_size=1) self._params = Params() self._error_message: str = "" - self._text_font = gui_app.font(FontWeight.MEDIUM) + self._text_font = gui_app.font(FontWeight.NORMAL) self._button = Button("", click_callback=self._handle_button_click, button_style=ButtonStyle.LIST_ACTION, border_radius=BUTTON_BORDER_RADIUS, font_size=BUTTON_FONT_SIZE) @@ -62,14 +64,14 @@ class SshKeyAction(ItemAction): # Draw username if exists if self._username: - text_size = measure_text_cached(self._text_font, self._username, BUTTON_FONT_SIZE) + text_size = measure_text_cached(self._text_font, self._username, VALUE_FONT_SIZE) rl.draw_text_ex( self._text_font, self._username, (rect.x + rect.width - BUTTON_WIDTH - text_size.x - 30, rect.y + (rect.height - text_size.y) / 2), - BUTTON_FONT_SIZE, + VALUE_FONT_SIZE, 1.0, - rl.WHITE, + rl.Color(170, 170, 170, 255), ) # Draw button diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 473eaa5fa8..755a335e4d 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -30,6 +30,10 @@ SCALE = float(os.getenv("SCALE", "1.0")) DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE +# Qt draws fonts accounting for ascent/descent differently, so compensate to match old styles +# The real scales for the fonts below range from 1.212 to 1.266 +FONT_SCALE = 1.242 + ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") @@ -173,6 +177,7 @@ class GuiApplication: self._target_fps = fps self._set_styles() self._load_fonts() + self._patch_text_functions() if not PC: self._mouse.start() @@ -356,6 +361,16 @@ class GuiApplication: rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_COLOR_NORMAL, rl.color_to_int(DEFAULT_TEXT_COLOR)) rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(rl.Color(50, 50, 50, 255))) + def _patch_text_functions(self): + # Wrap pyray text APIs to apply a global text size scale so our px sizes match Qt + if not hasattr(rl, "_orig_draw_text_ex"): + rl._orig_draw_text_ex = rl.draw_text_ex + + def _draw_text_ex_scaled(font, text, position, font_size, spacing, tint): + return rl._orig_draw_text_ex(font, text, position, font_size * FONT_SCALE, spacing, tint) + + rl.draw_text_ex = _draw_text_ex_scaled + def _set_log_callback(self): ffi_libc = cffi.FFI() ffi_libc.cdef(""" diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index f0031138fe..a5b9fc70d3 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -128,3 +128,7 @@ class GuiScrollPanel: self._offset_filter_y.x = position self._velocity_filter_y.x = 0.0 self._scroll_state = ScrollState.IDLE + + @property + def offset(self) -> float: + return float(self._offset_filter_y.x) diff --git a/system/ui/lib/text_measure.py b/system/ui/lib/text_measure.py index c172f94251..fcb7b25ccd 100644 --- a/system/ui/lib/text_measure.py +++ b/system/ui/lib/text_measure.py @@ -1,4 +1,5 @@ import pyray as rl +from openpilot.system.ui.lib.application import FONT_SCALE _cache: dict[int, rl.Vector2] = {} @@ -9,6 +10,6 @@ def measure_text_cached(font: rl.Font, text: str, font_size: int, spacing: int = if key in _cache: return _cache[key] - result = rl.measure_text_ex(font, text, font_size, spacing) # noqa: TID251 + result = rl.measure_text_ex(font, text, font_size * FONT_SCALE, spacing) # noqa: TID251 _cache[key] = result return result diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index 938867f748..7ca62409d8 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -3,7 +3,7 @@ import pyray as rl from dataclasses import dataclass from enum import Enum from typing import Any -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget @@ -45,7 +45,7 @@ class HtmlElement: font_weight: FontWeight margin_top: int margin_bottom: int - line_height: float = 1.2 + line_height: float = 0.9 # matches Qt visually, unsure why not default 1.2 indent_level: int = 0 @@ -61,16 +61,19 @@ class HtmlRenderer(Widget): if text_size is None: text_size = {} + # Base paragraph size (Qt stylesheet default is 48px in offroad alerts) + base_p_size = int(text_size.get(ElementType.P, 48)) + # Untagged text defaults to

self.styles: dict[ElementType, dict[str, Any]] = { - ElementType.H1: {"size": 68, "weight": FontWeight.BOLD, "margin_top": 20, "margin_bottom": 16}, - ElementType.H2: {"size": 60, "weight": FontWeight.BOLD, "margin_top": 24, "margin_bottom": 12}, - ElementType.H3: {"size": 52, "weight": FontWeight.BOLD, "margin_top": 20, "margin_bottom": 10}, - ElementType.H4: {"size": 48, "weight": FontWeight.BOLD, "margin_top": 16, "margin_bottom": 8}, - ElementType.H5: {"size": 44, "weight": FontWeight.BOLD, "margin_top": 12, "margin_bottom": 6}, - ElementType.H6: {"size": 40, "weight": FontWeight.BOLD, "margin_top": 10, "margin_bottom": 4}, - ElementType.P: {"size": text_size.get(ElementType.P, 38), "weight": FontWeight.NORMAL, "margin_top": 8, "margin_bottom": 12}, - ElementType.LI: {"size": 38, "weight": FontWeight.NORMAL, "color": rl.Color(40, 40, 40, 255), "margin_top": 6, "margin_bottom": 6}, + ElementType.H1: {"size": round(base_p_size * 2), "weight": FontWeight.BOLD, "margin_top": 20, "margin_bottom": 16}, + ElementType.H2: {"size": round(base_p_size * 1.50), "weight": FontWeight.BOLD, "margin_top": 24, "margin_bottom": 12}, + ElementType.H3: {"size": round(base_p_size * 1.17), "weight": FontWeight.BOLD, "margin_top": 20, "margin_bottom": 10}, + ElementType.H4: {"size": round(base_p_size * 1.00), "weight": FontWeight.BOLD, "margin_top": 16, "margin_bottom": 8}, + ElementType.H5: {"size": round(base_p_size * 0.83), "weight": FontWeight.BOLD, "margin_top": 12, "margin_bottom": 6}, + ElementType.H6: {"size": round(base_p_size * 0.67), "weight": FontWeight.BOLD, "margin_top": 10, "margin_bottom": 4}, + ElementType.P: {"size": base_p_size, "weight": FontWeight.NORMAL, "margin_top": 8, "margin_bottom": 12}, + ElementType.LI: {"size": base_p_size, "weight": FontWeight.NORMAL, "color": rl.Color(40, 40, 40, 255), "margin_top": 6, "margin_bottom": 6}, ElementType.BR: {"size": 0, "weight": FontWeight.NORMAL, "margin_top": 0, "margin_bottom": 12}, } @@ -179,8 +182,9 @@ class HtmlRenderer(Widget): wrapped_lines = wrap_text(font, element.content, element.font_size, int(content_width)) for line in wrapped_lines: - if current_y < rect.y - element.font_size: - current_y += element.font_size * element.line_height + # Use FONT_SCALE from wrapped raylib text functions to match what is drawn + if current_y < rect.y - element.font_size * FONT_SCALE: + current_y += element.font_size * FONT_SCALE * element.line_height continue if current_y > rect.y + rect.height: @@ -189,7 +193,7 @@ class HtmlRenderer(Widget): text_x = rect.x + (max(element.indent_level - 1, 0) * LIST_INDENT_PX) rl.draw_text_ex(font, line, rl.Vector2(text_x + padding, current_y), element.font_size, 0, self._text_color) - current_y += element.font_size * element.line_height + current_y += element.font_size * FONT_SCALE * element.line_height # Apply bottom margin current_y += element.margin_bottom @@ -213,7 +217,7 @@ class HtmlRenderer(Widget): wrapped_lines = wrap_text(font, element.content, element.font_size, int(usable_width)) for _ in wrapped_lines: - total_height += element.font_size * element.line_height + total_height += element.font_size * FONT_SCALE * element.line_height total_height += element.margin_bottom diff --git a/system/ui/widgets/inputbox.py b/system/ui/widgets/inputbox.py index 239d63037e..f53e3f0ebb 100644 --- a/system/ui/widgets/inputbox.py +++ b/system/ui/widgets/inputbox.py @@ -1,6 +1,6 @@ import pyray as rl import time -from openpilot.system.ui.lib.application import gui_app, MousePos +from openpilot.system.ui.lib.application import gui_app, MousePos, FONT_SCALE from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget @@ -130,7 +130,7 @@ class InputBox(Widget): rl.draw_text_ex( font, display_text, - rl.Vector2(int(rect.x + padding - self._text_offset), int(rect.y + rect.height / 2 - font_size / 2)), + rl.Vector2(int(rect.x + padding - self._text_offset), int(rect.y + rect.height / 2 - font_size * FONT_SCALE / 2)), font_size, 0, text_color, @@ -145,7 +145,7 @@ class InputBox(Widget): # Apply text offset to cursor position cursor_x -= self._text_offset - cursor_height = font_size + 4 + cursor_height = font_size * FONT_SCALE + 4 cursor_y = rect.y + rect.height / 2 - cursor_height / 2 rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.WHITE) diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 2da9a9f8df..33c4ef29d3 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -3,7 +3,7 @@ from itertools import zip_longest import pyray as rl -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_COLOR +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_COLOR, FONT_SCALE from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.utils import GuiStyleContext from openpilot.system.ui.lib.emoji import find_emoji, emoji_tex @@ -171,7 +171,7 @@ class Label(Widget): tex = emoji_tex(emoji) rl.draw_texture_ex(tex, line_pos, 0.0, self._font_size / tex.height, self._text_color) - line_pos.x += self._font_size + line_pos.x += self._font_size * FONT_SCALE prev_index = end rl.draw_text_ex(self._font, text[prev_index:], line_pos, self._font_size, 0, self._text_color) - text_pos.y += text_size.y or self._font_size + text_pos.y += text_size.y or self._font_size * FONT_SCALE From cc816043c10754e4a706031b9ef181dade290c26 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 03:36:47 -0700 Subject: [PATCH 144/341] raylib: add non-inline b tag (#36305) * debug * here too * clean up --- selfdrive/ui/layouts/settings/developer.py | 2 +- selfdrive/ui/layouts/settings/toggles.py | 4 ++-- system/ui/widgets/confirm_dialog.py | 2 +- system/ui/widgets/html_render.py | 13 +++++++++++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index bc8fc953c4..ec4d1fe989 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -154,7 +154,7 @@ class DeveloperLayout(Widget): self._alpha_long_toggle.action_item.set_state(False) # show confirmation dialog - content = (f"

{self._alpha_long_toggle.title}


" + + content = (f"

{self._alpha_long_toggle.title}


" + f"

{self._alpha_long_toggle.description}

") dlg = ConfirmDialog(content, "Enable", rich=True) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index c75cc7574d..01195142e3 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -151,7 +151,7 @@ class TogglesLayout(Widget): ui_state.update_params() e2e_description = ( - "openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " + + "openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " + "Experimental features are listed below:
" + "

End-to-End Longitudinal Control


" + "Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. " + @@ -219,7 +219,7 @@ class TogglesLayout(Widget): self._update_experimental_mode_icon() # show confirmation dialog - content = (f"

{self._toggles['ExperimentalMode'].title}


" + + content = (f"

{self._toggles['ExperimentalMode'].title}


" + f"

{self._toggles['ExperimentalMode'].description}

") dlg = ConfirmDialog(content, "Enable", rich=True) gui_app.set_modal_overlay(dlg, callback=confirm_callback) diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index 6d3e143b53..274307395c 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -19,7 +19,7 @@ class ConfirmDialog(Widget): def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel", rich: bool = False): super().__init__() self._label = Label(text, 70, FontWeight.BOLD, text_color=rl.Color(201, 201, 201, 255)) - self._html_renderer = HtmlRenderer(text=text, text_size={ElementType.P: 50}) + self._html_renderer = HtmlRenderer(text=text, text_size={ElementType.P: 50}, center_text=True) self._cancel_button = Button(cancel_text, self._cancel_button_callback) self._confirm_button = Button(confirm_text, self._confirm_button_callback, button_style=ButtonStyle.PRIMARY) self._rich = rich diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index 7ca62409d8..f90f78fe1b 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -8,6 +8,7 @@ from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle +from openpilot.system.ui.lib.text_measure import measure_text_cached LIST_INDENT_PX = 40 @@ -20,6 +21,7 @@ class ElementType(Enum): H5 = "h5" H6 = "h6" P = "p" + B = "b" UL = "ul" LI = "li" BR = "br" @@ -51,9 +53,10 @@ class HtmlElement: class HtmlRenderer(Widget): def __init__(self, file_path: str | None = None, text: str | None = None, - text_size: dict | None = None, text_color: rl.Color = rl.WHITE): + text_size: dict | None = None, text_color: rl.Color = rl.WHITE, center_text: bool = False): super().__init__() self._text_color = text_color + self._center_text = center_text self._normal_font = gui_app.font(FontWeight.NORMAL) self._bold_font = gui_app.font(FontWeight.BOLD) self._indent_level = 0 @@ -73,6 +76,7 @@ class HtmlRenderer(Widget): ElementType.H5: {"size": round(base_p_size * 0.83), "weight": FontWeight.BOLD, "margin_top": 12, "margin_bottom": 6}, ElementType.H6: {"size": round(base_p_size * 0.67), "weight": FontWeight.BOLD, "margin_top": 10, "margin_bottom": 4}, ElementType.P: {"size": base_p_size, "weight": FontWeight.NORMAL, "margin_top": 8, "margin_bottom": 12}, + ElementType.B: {"size": base_p_size, "weight": FontWeight.BOLD, "margin_top": 8, "margin_bottom": 12}, ElementType.LI: {"size": base_p_size, "weight": FontWeight.NORMAL, "color": rl.Color(40, 40, 40, 255), "margin_top": 6, "margin_bottom": 6}, ElementType.BR: {"size": 0, "weight": FontWeight.NORMAL, "margin_top": 0, "margin_bottom": 12}, } @@ -190,7 +194,12 @@ class HtmlRenderer(Widget): if current_y > rect.y + rect.height: break - text_x = rect.x + (max(element.indent_level - 1, 0) * LIST_INDENT_PX) + if self._center_text: + text_width = measure_text_cached(font, line, element.font_size).x + text_x = rect.x + (rect.width - text_width) / 2 + else: # left align + text_x = rect.x + (max(element.indent_level - 1, 0) * LIST_INDENT_PX) + rl.draw_text_ex(font, line, rl.Vector2(text_x + padding, current_y), element.font_size, 0, self._text_color) current_y += element.font_size * FONT_SCALE * element.line_height From 2305fb59a2b7e39495927361e09d6fe8a45c14f3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 03:40:40 -0700 Subject: [PATCH 145/341] raylib: fix mic indicator (#36309) * fix mic * move out --- selfdrive/ui/layouts/sidebar.py | 2 +- selfdrive/ui/ui_state.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index c499d35d4d..4ba7f2aa13 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -107,7 +107,7 @@ class Sidebar(Widget): device_state = sm['deviceState'] - self._recording_audio = sm.alive['rawAudioData'] + self._recording_audio = ui_state.recording_audio self._update_network_status(device_state) self._update_temperature_status(device_state) self._update_connection_status(device_state) diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index f4b9e1a9be..3ac9dbea17 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -67,6 +67,7 @@ class UIState: self.is_metric: bool = self.params.get_bool("IsMetric") self.started: bool = False self.ignition: bool = False + self.recording_audio: bool = False self.panda_type: log.PandaState.PandaType = log.PandaState.PandaType.unknown self.personality: log.LongitudinalPersonality = log.LongitudinalPersonality.standard self.has_longitudinal_control: bool = False @@ -128,6 +129,11 @@ class UIState: # Update started state self.started = self.sm["deviceState"].started and self.ignition + # Update recording audio state + self.recording_audio = self.params.get_bool("RecordAudio") and self.started + + self.is_metric = self.params.get_bool("IsMetric") + def _update_status(self) -> None: if self.started and self.sm.updated["selfdriveState"]: ss = self.sm["selfdriveState"] @@ -157,8 +163,7 @@ class UIState: self._started_prev = self.started def update_params(self) -> None: - self.is_metric = self.params.get_bool("IsMetric") - + # For slower operations # Update longitudinal control state CP_bytes = self.params.get("CarParamsPersistent") if CP_bytes is not None: From 0e6f78a656f5292f7234608011099855c6e0964e Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 11 Oct 2025 04:23:16 -0700 Subject: [PATCH 146/341] raylib is now a core dependancy --- pyproject.toml | 2 +- uv.lock | 68 +++++++++++++++++++++++++------------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9a8de2b746..7248caa4f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,6 +72,7 @@ dependencies = [ "zstandard", # ui + "raylib < 5.5.0.3", # TODO: unpin when they fix https://github.com/electronstudio/raylib-python-cffi/issues/186 "qrcode", ] @@ -119,7 +120,6 @@ dev = [ "tabulate", "types-requests", "types-tabulate", - "raylib < 5.5.0.3", # TODO: unpin when they fix https://github.com/electronstudio/raylib-python-cffi/issues/186 ] tools = [ diff --git a/uv.lock b/uv.lock index 3246221299..476945120c 100644 --- a/uv.lock +++ b/uv.lock @@ -639,10 +639,10 @@ name = "gymnasium" version = "1.2.0" 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/fd/17/c2a0e15c2cd5a8e788389b280996db927b923410de676ec5c7b2695e9261/gymnasium-1.2.0.tar.gz", hash = "sha256:344e87561012558f603880baf264ebc97f8a5c997a957b0c9f910281145534b0", size = 821142, upload-time = "2025-06-27T08:21:20.262Z" } wheels = [ @@ -931,22 +931,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:d0afaf3b005e35e14b929d5491d2d5b64562d0c1cd5093ba969fb63908670dd4" }, @@ -1281,6 +1281,7 @@ dependencies = [ { name = "pyserial" }, { name = "pyzmq" }, { name = "qrcode" }, + { name = "raylib" }, { name = "requests" }, { name = "scons" }, { name = "sentry-sdk" }, @@ -1313,7 +1314,6 @@ dev = [ { name = "pyprof2calltree" }, { name = "pytools", marker = "platform_machine != 'aarch64'" }, { name = "pywinctl" }, - { name = "raylib" }, { name = "tabulate" }, { name = "types-requests" }, { name = "types-tabulate" }, @@ -1401,7 +1401,7 @@ requires-dist = [ { name = "pywinctl", marker = "extra == 'dev'" }, { name = "pyzmq" }, { name = "qrcode" }, - { name = "raylib", marker = "extra == 'dev'", specifier = "<5.5.0.3" }, + { name = "raylib", specifier = "<5.5.0.3" }, { name = "requests" }, { name = "ruff", marker = "extra == 'testing'" }, { name = "scons" }, @@ -1454,8 +1454,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, upload-time = "2021-05-21T05:46:32.738Z" } wheels = [ @@ -1467,8 +1467,8 @@ name = "panda3d-simplepbr" version = "0.13.1" 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/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload-time = "2025-03-30T16:57:41.087Z" } wheels = [ @@ -4205,9 +4205,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, upload-time = "2025-01-22T00:16:58.421Z" } wheels = [ @@ -4429,9 +4429,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, upload-time = "2024-07-17T18:47:38.287Z" } wheels = [ @@ -4754,7 +4754,7 @@ name = "shapely" version = "2.1.1" 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/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } wheels = [ @@ -4983,7 +4983,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, upload-time = "2024-11-14T00:11:41.584Z" } wheels = [ From a6e28ac2ee4480594544a4a456fdcbf23add719d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 04:27:01 -0700 Subject: [PATCH 147/341] raylib: disable mouse thread lag print (#36312) every other process disables this --- system/ui/lib/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 755a335e4d..d3bc3d494c 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -76,7 +76,7 @@ class MouseState: self._events: deque[MouseEvent] = deque(maxlen=MOUSE_THREAD_RATE) # bound event list self._prev_mouse_event: list[MouseEvent | None] = [None] * MAX_TOUCH_SLOTS - self._rk = Ratekeeper(MOUSE_THREAD_RATE) + self._rk = Ratekeeper(MOUSE_THREAD_RATE, print_delay_threshold=None) self._lock = threading.Lock() self._exit_event = threading.Event() self._thread = None From 348114e5bd0d4fd551854058a23d25ac40d28086 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 04:27:19 -0700 Subject: [PATCH 148/341] raylib: remove cut stuff (#36310) remove cut stuff --- selfdrive/ui/layouts/settings/device.py | 3 ++- selfdrive/ui/layouts/settings/software.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 286ba36f07..35021492e0 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -60,7 +60,8 @@ class DeviceLayout(Widget): self._reset_calib_btn, button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide), regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), - button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), + # TODO: implement multilang + # button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt), ] regulatory_btn.set_visible(TICI) diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index a337b9034d..0c17a54fbe 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -64,7 +64,8 @@ class SoftwareLayout(Widget): self._version_item, self._download_btn, self._install_btn, - button_item("Target Branch", "SELECT", callback=self._on_select_branch), + # TODO: implement branch switching + # button_item("Target Branch", "SELECT", callback=self._on_select_branch), button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall), ] return items From c2af5a82ff9b7e741c53c2266a1d85716814c13b Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 11 Oct 2025 04:27:45 -0700 Subject: [PATCH 149/341] add earcut python package --- pyproject.toml | 1 + uv.lock | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7248caa4f5..78bf8c22ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,7 @@ dependencies = [ # ui "raylib < 5.5.0.3", # TODO: unpin when they fix https://github.com/electronstudio/raylib-python-cffi/issues/186 "qrcode", + "mapbox-earcut", ] [project.optional-dependencies] diff --git a/uv.lock b/uv.lock index 476945120c..c34a6d9b71 100644 --- a/uv.lock +++ b/uv.lock @@ -844,6 +844,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/77/d7f491cbc05303ac6801651aabeb262d43f319288c1ea96c66b1d2692ff3/lxml-6.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:27220da5be049e936c3aca06f174e8827ca6445a4353a1995584311487fc4e3e", size = 3518768, upload-time = "2025-09-22T04:04:57.097Z" }, ] +[[package]] +name = "mapbox-earcut" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/70/0a322197c1178f47941e5e6e13b0a4adeaaa7c465c18e3b4ead3eba49860/mapbox_earcut-1.0.3.tar.gz", hash = "sha256:b6bac5d519d9947a6321a699c15d58e0b5740da61b9210ed229e05ad207c1c04", size = 24029, upload-time = "2024-12-25T12:49:09.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/d7/b37a45c248100e7285a40de87a8b1808ca4ca10228e265f2d0c320702d96/mapbox_earcut-1.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbf24029e7447eb0351000f4fd3185327a00dac5ed756b07330b0bdaed6932db", size = 71057, upload-time = "2024-12-25T12:48:09.131Z" }, + { url = "https://files.pythonhosted.org/packages/1b/df/2b63eb0d3a24e14f67adc816de18c2e09f3eb0997c512ace84dd59c3ed96/mapbox_earcut-1.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:998e2f1e3769538f7656a34296d08a37cb71ce57aa8cf4387572bc00029b52ce", size = 65300, upload-time = "2024-12-25T12:48:11.677Z" }, + { url = "https://files.pythonhosted.org/packages/87/37/9dd9575f5c00e35d480e7150e5bb315a35d9cf5642bfb75ca628a31e1341/mapbox_earcut-1.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2382d84d6d168f73479673d297753e37440772f233cc03ebb54d150e37b174", size = 96965, upload-time = "2024-12-25T12:48:12.968Z" }, + { url = "https://files.pythonhosted.org/packages/3b/91/5708233941b5bf73149ba35f7aa32c6ee2cf4a33cd33069e7dba69d4129f/mapbox_earcut-1.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ccddb4bb04f11beab62943eb5a1bcd52c5a71d236bfce0ecc03e45e97fdb24b", size = 1070953, upload-time = "2024-12-25T12:48:15.495Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fe/b35b999ba786aa17ddc47bc04231de076665eb511e1cd58cf6fef3581172/mapbox_earcut-1.0.3-cp311-cp311-win32.whl", hash = "sha256:f19b2bcf6475bc591f48437d3214691a6730f39b1f6dfd7505b69c4345485b0c", size = 65245, upload-time = "2024-12-25T12:48:17.826Z" }, + { url = "https://files.pythonhosted.org/packages/11/81/18ac08b0bb0c22dd9028c7ecb31ae4086d31128b13fb3903e717331072ac/mapbox_earcut-1.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:811a64ad5e6ecf09b96af533e5c169299ba173e53eb4ff0209de1adcfae314be", size = 72356, upload-time = "2024-12-25T12:48:20.164Z" }, + { url = "https://files.pythonhosted.org/packages/96/7c/707a4ce96e078f7d382cc32b4a6c2326eca68d77ead5e990f5f940d16140/mapbox_earcut-1.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5be71b7ec2180a27ce1178d53933430a3292b6ac3f94f2144513ee51d9034007", size = 70333, upload-time = "2024-12-25T12:48:22.565Z" }, + { url = "https://files.pythonhosted.org/packages/fb/47/ba2a14732f6e197b0ed879a1992b4d85054294b23627ad681b4fb1251d16/mapbox_earcut-1.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eb874f7562a49ae0fb7bd47bcc9b4854cc53e3e4f7f26674f02f3cadb006ce16", size = 64697, upload-time = "2024-12-25T12:48:25.025Z" }, + { url = "https://files.pythonhosted.org/packages/e7/68/59a514811da76c3c801207bd6d7094ea5ba75648c2e7f15d4cb98b08216f/mapbox_earcut-1.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b9f06f2f8a795d835342aa80e021cfceda78fdca7bc07dc1a0b4aca90239f3", size = 96182, upload-time = "2024-12-25T12:48:26.316Z" }, + { url = "https://files.pythonhosted.org/packages/3f/79/97bf509ade0f9aeb5b5f94b1aff86393c2f584379a80e392fdfcbea434ae/mapbox_earcut-1.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdc55574ef7b613004874a459d2d59c07e1ef45cebb83f86c4958f7d3e2d6069", size = 1070584, upload-time = "2024-12-25T12:48:29.065Z" }, + { url = "https://files.pythonhosted.org/packages/de/7a/5a6e205bab9ff49d1dae392f6179a444f820880d8985f26080816fa6c7ba/mapbox_earcut-1.0.3-cp312-cp312-win32.whl", hash = "sha256:790f52c67a0bd81032eaf61ebc181b1825b8b6daf01cb69e9eaa38521dd07aeb", size = 65375, upload-time = "2024-12-25T12:48:30.618Z" }, + { url = "https://files.pythonhosted.org/packages/7a/59/674a67f92772563d5a943ce2c4ed834ed341e3a0fd77b8eb4b79057f5193/mapbox_earcut-1.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:cc1bbf35be0d9853dd448374330684ddbd0112497dee7d21b7417b0ab6236ac7", size = 72575, upload-time = "2024-12-25T12:48:33.544Z" }, +] + [[package]] name = "markdown" version = "3.9" @@ -1270,6 +1293,7 @@ dependencies = [ { name = "json-rpc" }, { name = "kaitaistruct" }, { name = "libusb1" }, + { name = "mapbox-earcut" }, { name = "numpy" }, { name = "onnx" }, { name = "psutil" }, @@ -1367,6 +1391,7 @@ requires-dist = [ { name = "json-rpc" }, { name = "kaitaistruct" }, { name = "libusb1" }, + { name = "mapbox-earcut" }, { name = "matplotlib", marker = "extra == 'dev'" }, { name = "metadrive-simulator", marker = "platform_machine != 'aarch64' and extra == 'tools'", url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl" }, { name = "mkdocs", marker = "extra == 'docs'" }, From aa7f6973c0a7427317e8916f11bfb01390379bbf Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 06:01:17 -0700 Subject: [PATCH 150/341] raylib: match Qt onroad button disabling (#36316) * fixxx * clean up * disable onroad: adb, joystick, alpha long --- selfdrive/ui/layouts/settings/developer.py | 3 +++ selfdrive/ui/layouts/settings/device.py | 13 ++++++++++--- system/ui/widgets/list_view.py | 7 +++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index ec4d1fe989..fac00b60a2 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -38,6 +38,7 @@ class DeveloperLayout(Widget): description=DESCRIPTIONS["enable_adb"], initial_state=self._params.get_bool("AdbEnabled"), callback=self._on_enable_adb, + enabled=ui_state.is_offroad, ) # SSH enable toggle + SSH key management @@ -54,6 +55,7 @@ class DeveloperLayout(Widget): description="", initial_state=self._params.get_bool("JoystickDebugMode"), callback=self._on_joystick_debug_mode, + enabled=ui_state.is_offroad, ) self._long_maneuver_toggle = toggle_item( @@ -68,6 +70,7 @@ class DeveloperLayout(Widget): description=DESCRIPTIONS["alpha_longitudinal"], initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), callback=self._on_alpha_long_enabled, + enabled=ui_state.is_offroad, ) items = [ diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 35021492e0..66341db37b 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -42,6 +42,8 @@ class DeviceLayout(Widget): items = self._initialize_items() self._scroller = Scroller(items, line_separator=True, spacing=0) + ui_state.add_offroad_transition_callback(self._offroad_transition) + def _initialize_items(self): dongle_id = self._params.get("DongleId") or "N/A" serial = self._params.get("HardwareSerial") or "N/A" @@ -52,21 +54,26 @@ class DeviceLayout(Widget): self._reset_calib_btn = button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt) self._reset_calib_btn.set_description_opened_callback(self._update_calib_description) + self._power_off_btn = dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt) + items = [ text_item("Dongle ID", dongle_id), text_item("Serial", serial), self._pair_device_btn, button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), self._reset_calib_btn, - button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide), - regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), + button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide, enabled=ui_state.is_offroad), + regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory, enabled=ui_state.is_offroad), # TODO: implement multilang # button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), - dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt), + self._power_off_btn, ] regulatory_btn.set_visible(TICI) return items + def _offroad_transition(self): + self._power_off_btn.action_item.right_button.set_visible(ui_state.is_offroad()) + def show_event(self): self._scroller.show_event() diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index b1c4b44df1..aaa67472dd 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -191,6 +191,13 @@ class DualButtonAction(ItemAction): left_rect = rl.Rectangle(rect.x, button_y, button_width, button_height) right_rect = rl.Rectangle(rect.x + button_width + button_spacing, button_y, button_width, button_height) + # expand one to full width if other is not visible + if not self.left_button.is_visible: + right_rect.x = rect.x + right_rect.width = rect.width + elif not self.right_button.is_visible: + left_rect.width = rect.width + # Render buttons self.left_button.render(left_rect) self.right_button.render(right_rect) From fb772122218b7ff7bf716bacdb420d819f598a46 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 06:02:10 -0700 Subject: [PATCH 151/341] raylib: add more spacing to network nav buttons --- system/ui/widgets/network.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 85b98f10ac..a50881c8cb 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -88,15 +88,15 @@ class NetworkUI(Widget): def _render(self, _): # subtract button - content_rect = rl.Rectangle(self._rect.x, self._rect.y + self._nav_button.rect.height + 20, - self._rect.width, self._rect.height - self._nav_button.rect.height - 20) + content_rect = rl.Rectangle(self._rect.x, self._rect.y + self._nav_button.rect.height + 40, + self._rect.width, self._rect.height - self._nav_button.rect.height - 40) if self._current_panel == PanelType.WIFI: self._nav_button.text = "Advanced" - self._nav_button.set_position(self._rect.x + self._rect.width - self._nav_button.rect.width, self._rect.y + 10) + self._nav_button.set_position(self._rect.x + self._rect.width - self._nav_button.rect.width, self._rect.y + 20) self._wifi_panel.render(content_rect) else: self._nav_button.text = "Back" - self._nav_button.set_position(self._rect.x, self._rect.y + 10) + self._nav_button.set_position(self._rect.x, self._rect.y + 20) self._advanced_panel.render(content_rect) self._nav_button.render() From e8a17b49639aa872ad9097d8ab71405f1cde53d7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 06:04:27 -0700 Subject: [PATCH 152/341] raylib: fix stale frames going onroad (#36314) * fix * try * flip * now flip * fix network nav button heights * revert --- selfdrive/ui/onroad/cameraview.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index ca031e2f17..c8ee9b140c 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -8,6 +8,7 @@ from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.egl import init_egl, create_egl_image, destroy_egl_image, bind_egl_image_to_texture, EGLImage from openpilot.system.ui.widgets import Widget +from openpilot.selfdrive.ui.ui_state import ui_state CONNECTION_RETRY_INTERVAL = 0.2 # seconds between connection attempts @@ -103,6 +104,18 @@ class CameraView(Widget): self.egl_texture = rl.load_texture_from_image(temp_image) rl.unload_image(temp_image) + ui_state.add_offroad_transition_callback(self._offroad_transition) + + def _offroad_transition(self): + if ui_state.is_onroad(): + # Prevent old frames from showing when going onroad. Qt has a separate thread + # which drains the VisionIpcClient SubSocket for us. Re-connecting is not enough + # and only clears internal buffers, not the message queue. + self.frame = None + if self.client: + del self.client + self.client = VisionIpcClient(self._name, self._stream_type, conflate=True) + def _set_placeholder_color(self, color: rl.Color): """Set a placeholder color to be drawn when no frame is available.""" self._placeholder_color = color From 14993f58e3224fb488e65fa7a16617510b49843b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 06:28:49 -0700 Subject: [PATCH 153/341] raylib: set speed fixes (#36317) * remove msaa artifacting by heavily reducing segments and match radius * always draw set speed with '-' like qt * clean up * match qt behavior for rivian --- selfdrive/ui/onroad/hud_renderer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index c813d852bc..98c3d686fa 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -60,7 +60,7 @@ class HudRenderer(Widget): super().__init__() """Initialize the HUD renderer.""" self.is_cruise_set: bool = False - self.is_cruise_available: bool = False + self.is_cruise_available: bool = True self.set_speed: float = SET_SPEED_NA self.speed: float = 0.0 self.v_ego_cluster_seen: bool = False @@ -130,8 +130,8 @@ class HudRenderer(Widget): y = rect.y + 45 set_speed_rect = rl.Rectangle(x, y, set_speed_width, UI_CONFIG.set_speed_height) - rl.draw_rectangle_rounded(set_speed_rect, 0.2, 30, COLORS.black_translucent) - rl.draw_rectangle_rounded_lines_ex(set_speed_rect, 0.2, 30, 6, COLORS.border_translucent) + rl.draw_rectangle_rounded(set_speed_rect, 0.35, 10, COLORS.black_translucent) + rl.draw_rectangle_rounded_lines_ex(set_speed_rect, 0.35, 10, 6, COLORS.border_translucent) max_color = COLORS.grey set_speed_color = COLORS.dark_grey From cec7a5dc989cb7a23475de2748354ed7b24dcc64 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 06:55:35 -0700 Subject: [PATCH 154/341] raylib: fix styling for fullscreen alerts (#36318) * fix that * fix styling * and this * revert * fix full * revert --- selfdrive/ui/onroad/alert_renderer.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 2802363f81..02b015315b 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -63,8 +63,8 @@ ALERT_CRITICAL_TIMEOUT = Alert( ALERT_CRITICAL_REBOOT = Alert( text1="System Unresponsive", text2="Reboot Device", - size=AlertSize.full, - status=AlertStatus.critical, + size=AlertSize.mid, + status=AlertStatus.normal, ) @@ -149,13 +149,17 @@ class AlertRenderer(Widget): else: is_long = len(alert.text1) > 15 font_size1 = 132 if is_long else 177 - align_ment = rl.GuiTextAlignment.TEXT_ALIGN_CENTER - vertical_align = rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE - text_rect = rl.Rectangle(rect.x, rect.y, rect.width, rect.height) - gui_text_box(text_rect, alert.text1, font_size1, alignment=align_ment, alignment_vertical=vertical_align, font_weight=FontWeight.BOLD) - text_rect.y = rect.y + rect.height // 2 - gui_text_box(text_rect, alert.text2, ALERT_FONT_BIG, alignment=align_ment) + align_center = rl.GuiTextAlignment.TEXT_ALIGN_CENTER + align_top = rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP + + top_offset = 240 if is_long else 270 + title_rect = rl.Rectangle(rect.x, rect.y + top_offset, rect.width, 600) + gui_text_box(title_rect, alert.text1, font_size1, alignment=align_center, alignment_vertical=align_top, font_weight=FontWeight.BOLD) + + bottom_offset = 361 if is_long else 420 + subtitle_rect = rl.Rectangle(rect.x, rect.y + rect.height - bottom_offset, rect.width, 300) + gui_text_box(subtitle_rect, alert.text2, ALERT_FONT_BIG, alignment=align_center, alignment_vertical=align_top) def _draw_centered(self, text, rect, font, font_size, center_y=True, color=rl.WHITE) -> None: text_size = measure_text_cached(font, text, font_size) From b3eba70b7ac406a8054a32d4f26015ff4b149d21 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 07:41:29 -0700 Subject: [PATCH 155/341] raylib: flip! (#36319) * flip! * add ui * ? * qt is extra * low node * add uiDebug * fix * fix dat * bump double increase for tol * it's ~11ms but double for tol * fix report * Update selfdrive/test/test_onroad.py --- selfdrive/SConscript | 3 ++- selfdrive/test/test_onroad.py | 6 +++--- selfdrive/ui/onroad/augmented_road_view.py | 12 +++++++++++- selfdrive/ui/tests/test_raylib_ui.py | 2 +- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- system/manager/build.py | 2 +- system/manager/process_config.py | 4 ++-- 7 files changed, 21 insertions(+), 10 deletions(-) diff --git a/selfdrive/SConscript b/selfdrive/SConscript index 0b49e69116..43bf7d0476 100644 --- a/selfdrive/SConscript +++ b/selfdrive/SConscript @@ -3,4 +3,5 @@ SConscript(['controls/lib/lateral_mpc_lib/SConscript']) SConscript(['controls/lib/longitudinal_mpc_lib/SConscript']) SConscript(['locationd/SConscript']) SConscript(['modeld/SConscript']) -SConscript(['ui/SConscript']) \ No newline at end of file +if GetOption('extras'): + SConscript(['ui/SConscript']) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index b4b9b9dbbe..9fc76489b8 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -32,7 +32,7 @@ CPU usage budget TEST_DURATION = 25 LOG_OFFSET = 8 -MAX_TOTAL_CPU = 300. # total for all 8 cores +MAX_TOTAL_CPU = 315. # total for all 8 cores PROCS = { # Baseline CPU usage by process "selfdrive.controls.controlsd": 16.0, @@ -42,7 +42,7 @@ PROCS = { "./encoderd": 13.0, "./camerad": 10.0, "selfdrive.controls.plannerd": 8.0, - "./ui": 18.0, + "selfdrive.ui.ui": 24.0, "system.sensord.sensord": 13.0, "selfdrive.controls.radard": 2.0, "selfdrive.modeld.modeld": 22.0, @@ -215,7 +215,7 @@ class TestOnroad: print(result) assert max(ts) < 250. - assert np.mean(ts) < 10. + assert np.mean(ts) < 20. # TODO: ~6-11ms, increase consistency #self.assertLess(np.std(ts), 5.) # some slow frames are expected since camerad/modeld can preempt ui diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 0a4c45163b..cac2f9c968 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -1,6 +1,7 @@ +import time import numpy as np import pyray as rl -from cereal import log +from cereal import log, messaging from msgq.visionipc import VisionStreamType from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus, UI_BORDER_SIZE from openpilot.selfdrive.ui.onroad.alert_renderer import AlertRenderer @@ -48,8 +49,12 @@ class AugmentedRoadView(CameraView): self.alert_renderer = AlertRenderer() self.driver_state_renderer = DriverStateRenderer() + # debug + self._pm = messaging.PubMaster(['uiDebug']) + def _render(self, rect): # Only render when system is started to avoid invalid data access + start_draw = time.monotonic() if not ui_state.started: return @@ -93,6 +98,11 @@ class AugmentedRoadView(CameraView): # End clipping region rl.end_scissor_mode() + # publish uiDebug + msg = messaging.new_message('uiDebug') + msg.uiDebug.drawTimeMillis = (time.monotonic() - start_draw) * 1000 + self._pm.send('uiDebug', msg) + def _handle_mouse_press(self, _): if not self._hud_renderer.user_interacting() and self._click_callback is not None: self._click_callback() diff --git a/selfdrive/ui/tests/test_raylib_ui.py b/selfdrive/ui/tests/test_raylib_ui.py index 3f23301972..69ba946dcd 100644 --- a/selfdrive/ui/tests/test_raylib_ui.py +++ b/selfdrive/ui/tests/test_raylib_ui.py @@ -2,7 +2,7 @@ import time from openpilot.selfdrive.test.helpers import with_processes -@with_processes(["raylib_ui"]) +@with_processes(["ui"]) def test_raylib_ui(): """Test initialization of the UI widgets is successful.""" time.sleep(1) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index fb42b94d6d..bd40b89624 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -160,7 +160,7 @@ class TestUI: time.sleep(0.01) pyautogui.mouseUp(self.ui.left + x, self.ui.top + y, *args, **kwargs) - @with_processes(["raylib_ui"]) + @with_processes(["ui"]) def test_ui(self, name, setup_case): self.setup() time.sleep(UI_DELAY) # wait for UI to start diff --git a/system/manager/build.py b/system/manager/build.py index b6153ee8a4..c88befd454 100755 --- a/system/manager/build.py +++ b/system/manager/build.py @@ -14,7 +14,7 @@ from openpilot.system.version import get_build_metadata MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9 CACHE_DIR = Path("/data/scons_cache" if AGNOS else "/tmp/scons_cache") -TOTAL_SCONS_NODES = 3275 +TOTAL_SCONS_NODES = 2280 MAX_BUILD_PROGRESS = 100 def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 22f159e891..b8a1e09889 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -80,8 +80,8 @@ procs = [ PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), PythonProcess("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)), - PythonProcess("raylib_ui", "selfdrive.ui.ui", always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), + # NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), + PythonProcess("ui", "selfdrive.ui.ui", always_run, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False), From 49d9b8bb001a4739cfd91d7b1039e7d8cd5bb8f2 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 11 Oct 2025 23:06:06 -0700 Subject: [PATCH 156/341] ui: fix cloudlog spam (#36321) * dark office * check * back * fix * remove * remove --- selfdrive/ui/ui_state.py | 1 - system/ui/lib/application.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 3ac9dbea17..dab01f9245 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -228,7 +228,6 @@ class Device: if brightness != self._last_brightness: if self._brightness_thread is None or not self._brightness_thread.is_alive(): - cloudlog.debug(f"setting display brightness {brightness}") self._brightness_thread = threading.Thread(target=HARDWARE.set_screen_brightness, args=(brightness,)) self._brightness_thread.start() self._last_brightness = brightness diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index d3bc3d494c..f0bcdcdc6d 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -160,7 +160,7 @@ class GuiApplication: HARDWARE.set_screen_brightness(65) self._set_log_callback() - rl.set_trace_log_level(rl.TraceLogLevel.LOG_ALL) + rl.set_trace_log_level(rl.TraceLogLevel.LOG_WARNING) flags = rl.ConfigFlags.FLAG_MSAA_4X_HINT if ENABLE_VSYNC: From 32f65bae55bd75e94a33bc85eba3dde54ebf9b89 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 23:29:08 -0700 Subject: [PATCH 157/341] alpha long: allow toggle while onroad + restart op it's alpha, and some cars don't fault (we allow other toggles which would fault, so why not enable) --- selfdrive/ui/layouts/settings/developer.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index fac00b60a2..21eb44efe7 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -70,9 +70,11 @@ class DeveloperLayout(Widget): description=DESCRIPTIONS["alpha_longitudinal"], initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), callback=self._on_alpha_long_enabled, - enabled=ui_state.is_offroad, + enabled=lambda: not ui_state.engaged, ) + self._alpha_long_toggle.set_description(self._alpha_long_toggle.description + " Changing this setting will restart openpilot if the car is powered on.") + items = [ self._adb_toggle, self._ssh_toggle, @@ -152,6 +154,7 @@ class DeveloperLayout(Widget): def confirm_callback(result: int): if result == DialogResult.CONFIRM: self._params.put_bool("AlphaLongitudinalEnabled", True) + self._params.put_bool("OnroadCycleRequested", True) self._update_toggles() else: self._alpha_long_toggle.action_item.set_state(False) @@ -162,7 +165,8 @@ class DeveloperLayout(Widget): dlg = ConfirmDialog(content, "Enable", rich=True) gui_app.set_modal_overlay(dlg, callback=confirm_callback) - return - self._params.put_bool("AlphaLongitudinalEnabled", state) - self._update_toggles() + else: + self._params.put_bool("AlphaLongitudinalEnabled", False) + self._params.put_bool("OnroadCycleRequested", True) + self._update_toggles() From 5f7b05e8084fe1d1057735e54f2aa906f4b12345 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 23:36:05 -0700 Subject: [PATCH 158/341] raylib: don't create vipc client twice first time --- selfdrive/ui/onroad/cameraview.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index c8ee9b140c..744fdbf135 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -107,7 +107,8 @@ class CameraView(Widget): ui_state.add_offroad_transition_callback(self._offroad_transition) def _offroad_transition(self): - if ui_state.is_onroad(): + # Reconnect if not first time going onroad + if ui_state.is_onroad() and self.frame is not None: # Prevent old frames from showing when going onroad. Qt has a separate thread # which drains the VisionIpcClient SubSocket for us. Re-connecting is not enough # and only clears internal buffers, not the message queue. From 13d0aefd7c14908cad6df33d09dce8ea623c3b02 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Oct 2025 23:40:42 -0700 Subject: [PATCH 159/341] raylib: don't get old onroad alert on startup --- selfdrive/ui/onroad/alert_renderer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 02b015315b..9754d488ed 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -79,8 +79,8 @@ class AlertRenderer(Widget): ss = sm['selfdriveState'] # Check if selfdriveState messages have stopped arriving + recv_frame = sm.recv_frame['selfdriveState'] if not sm.updated['selfdriveState']: - recv_frame = sm.recv_frame['selfdriveState'] time_since_onroad = time.monotonic() - ui_state.started_time # 1. Never received selfdriveState since going onroad @@ -100,6 +100,10 @@ class AlertRenderer(Widget): if ss.alertSize == 0: return None + # Don't get old alert + if recv_frame < ui_state.started_frame: + return None + # Return current alert return Alert(text1=ss.alertText1, text2=ss.alertText2, size=ss.alertSize.raw, status=ss.alertStatus.raw) From 129445cd1d2c86a3bd3c4dba8369a3506a656772 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 12 Oct 2025 00:37:36 -0700 Subject: [PATCH 160/341] setup: don't wait for so long (#36323) nice --- system/ui/setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index 4068af01e7..99a3569fbe 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -343,7 +343,7 @@ class Setup(Widget): shutil.copyfile(INSTALLER_SOURCE_PATH, INSTALLER_DESTINATION_PATH) # give time for installer UI to take over - time.sleep(1) + time.sleep(0.1) gui_app.request_close() else: self.state = SetupState.NETWORK_SETUP @@ -406,7 +406,7 @@ class Setup(Widget): f.write(self.download_url) # give time for installer UI to take over - time.sleep(5) + time.sleep(0.1) gui_app.request_close() except Exception: From de0a1e66d828f5723c4ce00957d5adae842e1a4b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 13 Oct 2025 13:54:50 -0700 Subject: [PATCH 161/341] software download screenshot (#36326) * software * clean up * Qt ButtonControl has 0 padding * clean up * clean up --- .../ui/tests/test_ui/raylib_screenshots.py | 22 +++++++++++++------ system/ui/widgets/button.py | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index bd40b89624..20d0f2ecba 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -64,6 +64,19 @@ def setup_settings_software(click, pm: PubMaster): click(278, 720) +def setup_settings_software_download(click, pm: PubMaster): + params = Params() + # setup_settings_software but with "DOWNLOAD" button to test long text + params.put("UpdaterState", "idle") + params.put_bool("UpdaterFetchAvailable", True) + setup_settings_software(click, pm) + + +def setup_settings_software_release_notes(click, pm: PubMaster): + setup_settings_software(click, pm) + click(588, 110) # expand description for current version + + def setup_settings_firehose(click, pm: PubMaster): setup_settings(click, pm) click(278, 845) @@ -106,18 +119,14 @@ def setup_homescreen_update_available(click, pm: PubMaster): close_settings(click, pm) -def setup_software_release_notes(click, pm: PubMaster): - setup_settings(click, pm) - setup_settings_software(click, pm) - click(588, 110) # expand description for current version - - CASES = { "homescreen": setup_homescreen, "settings_device": setup_settings, "settings_network": setup_settings_network, "settings_toggles": setup_settings_toggles, "settings_software": setup_settings_software, + "settings_software_download": setup_settings_software_download, + "settings_software_release_notes": setup_settings_software_release_notes, "settings_firehose": setup_settings_firehose, "settings_developer": setup_settings_developer, "keyboard": setup_keyboard, @@ -125,7 +134,6 @@ CASES = { "offroad_alert": setup_offroad_alert, "homescreen_update_available": setup_homescreen_update_available, "confirmation_dialog": setup_confirmation_dialog, - "software_release_notes": setup_software_release_notes, } diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 141f682db0..ef28df8112 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -86,7 +86,7 @@ class Button(Widget): button_style: ButtonStyle = ButtonStyle.NORMAL, border_radius: int = 10, text_alignment: TextAlignment = TextAlignment.CENTER, - text_padding: int = 20, + text_padding: int = 0, icon=None, multi_touch: bool = False, ): From 692f5fdd72b06119a7dd2989d86fdb125f9b72f6 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:56:47 -0500 Subject: [PATCH 162/341] Add screenshot for experimental mode description (raylib UI test) (#36327) * feat: add test case to expand experimental mode description * Update selfdrive/ui/tests/test_ui/raylib_screenshots.py --------- Co-authored-by: Shane Smiskol --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 20d0f2ecba..a65f198ad6 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -119,6 +119,11 @@ def setup_homescreen_update_available(click, pm: PubMaster): close_settings(click, pm) +def setup_experimental_mode_description(click, pm: PubMaster): + setup_settings_toggles(click, pm) + click(1200, 280) # expand description for experimental mode + + CASES = { "homescreen": setup_homescreen, "settings_device": setup_settings, @@ -134,6 +139,7 @@ CASES = { "offroad_alert": setup_offroad_alert, "homescreen_update_available": setup_homescreen_update_available, "confirmation_dialog": setup_confirmation_dialog, + "experimental_mode_description": setup_experimental_mode_description, } From 41fa0cdf8271b8cd83b15eb349e0b48e5c12866b Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:07:11 -0500 Subject: [PATCH 163/341] fix cycle offroad alerts (#36302) * fix: use parse_release_notes in cycle_offroad_alerts * fix: set update available param true * Update selfdrive/ui/tests/cycle_offroad_alerts.py --------- Co-authored-by: Shane Smiskol --- selfdrive/ui/tests/cycle_offroad_alerts.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/tests/cycle_offroad_alerts.py b/selfdrive/ui/tests/cycle_offroad_alerts.py index e468d88e0e..b577b74b00 100755 --- a/selfdrive/ui/tests/cycle_offroad_alerts.py +++ b/selfdrive/ui/tests/cycle_offroad_alerts.py @@ -7,6 +7,7 @@ import json from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert +from openpilot.system.updated.updated import parse_release_notes if __name__ == "__main__": params = Params() @@ -18,9 +19,7 @@ if __name__ == "__main__": while True: print("setting alert update") params.put_bool("UpdateAvailable", True) - r = open(os.path.join(BASEDIR, "RELEASES.md")).read() - r = r[:r.find('\n\n')] # Slice latest release notes - params.put("UpdaterNewReleaseNotes", r + "\n") + params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR)) time.sleep(t) params.put_bool("UpdateAvailable", False) From 8e3757ac875a244adb1cef4d029cb8c59b345982 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 13 Oct 2025 16:49:39 -0700 Subject: [PATCH 164/341] raylib: image dimensions are optional (#36332) * meas * now no resizing * clean up --- selfdrive/ui/layouts/onboarding.py | 2 +- system/ui/lib/application.py | 33 ++++++++++++++++-------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/selfdrive/ui/layouts/onboarding.py b/selfdrive/ui/layouts/onboarding.py index ea6fedebf7..bccb36712c 100644 --- a/selfdrive/ui/layouts/onboarding.py +++ b/selfdrive/ui/layouts/onboarding.py @@ -46,7 +46,7 @@ class TrainingGuide(Widget): paths = sorted(paths, key=lambda x: int(re.search(r'\d+', x).group())) for fn in paths: path = os.path.join(BASEDIR, "selfdrive/assets/training", fn) - self._images.append(gui_app.texture(path, gui_app.width, gui_app.height)) + self._images.append(gui_app.texture(path)) def _handle_mouse_release(self, mouse_pos): if rl.check_collision_point_rec(mouse_pos, STEP_RECTS[self._step]): diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index f0bcdcdc6d..1925122d0b 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -189,7 +189,8 @@ class GuiApplication: self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback) - def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): + def texture(self, asset_path: str, width: int | None = None, height: int | None = None, + alpha_premultiply=False, keep_aspect_ratio=True): cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" if cache_key in self._textures: return self._textures[cache_key] @@ -199,29 +200,31 @@ class GuiApplication: self._textures[cache_key] = texture_obj return texture_obj - def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): + def _load_texture_from_image(self, image_path: str, width: int | None = None, height: int | None = None, + alpha_premultiply=False, keep_aspect_ratio=True): """Load and resize a texture, storing it for later automatic unloading.""" image = rl.load_image(image_path) if alpha_premultiply: rl.image_alpha_premultiply(image) - # Resize with aspect ratio preservation if requested - if keep_aspect_ratio: - orig_width = image.width - orig_height = image.height + if width is not None and height is not None: + # Resize with aspect ratio preservation if requested + if keep_aspect_ratio: + orig_width = image.width + orig_height = image.height - scale_width = width / orig_width - scale_height = height / orig_height + scale_width = width / orig_width + scale_height = height / orig_height - # Calculate new dimensions - scale = min(scale_width, scale_height) - new_width = int(orig_width * scale) - new_height = int(orig_height * scale) + # Calculate new dimensions + scale = min(scale_width, scale_height) + new_width = int(orig_width * scale) + new_height = int(orig_height * scale) - rl.image_resize(image, new_width, new_height) - else: - rl.image_resize(image, width, height) + rl.image_resize(image, new_width, new_height) + else: + rl.image_resize(image, width, height) texture = rl.load_texture_from_image(image) # Set texture filtering to smooth the result From f4041dc1f0ffbdd261fdc342d312e6a0f96c7c1e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Oct 2025 00:33:38 -0700 Subject: [PATCH 165/341] raylib: black sidebar (#36347) black sidebar --- selfdrive/ui/layouts/sidebar.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 4ba7f2aa13..9337b3c239 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -23,7 +23,6 @@ NetworkType = log.DeviceState.NetworkType # Color scheme class Colors: - SIDEBAR_BG = rl.Color(57, 57, 57, 255) WHITE = rl.WHITE WHITE_DIM = rl.Color(255, 255, 255, 85) GRAY = rl.Color(84, 84, 84, 255) @@ -94,7 +93,7 @@ class Sidebar(Widget): def _render(self, rect: rl.Rectangle): # Background - rl.draw_rectangle_rec(rect, Colors.SIDEBAR_BG) + rl.draw_rectangle_rec(rect, rl.BLACK) self._draw_buttons(rect) self._draw_network_indicator(rect) From a2cce7f897dd6a1516fb17fd9f595e5af3f0e626 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Oct 2025 00:39:22 -0700 Subject: [PATCH 166/341] raylib: fix border radius (#36346) * colors * revert color * rev --- selfdrive/ui/widgets/exp_mode_button.py | 3 +-- selfdrive/ui/widgets/prime.py | 4 ++-- selfdrive/ui/widgets/setup.py | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/selfdrive/ui/widgets/exp_mode_button.py b/selfdrive/ui/widgets/exp_mode_button.py index 40a649899d..97c399cdab 100644 --- a/selfdrive/ui/widgets/exp_mode_button.py +++ b/selfdrive/ui/widgets/exp_mode_button.py @@ -32,10 +32,9 @@ class ExperimentalModeButton(Widget): start_color, end_color) def _render(self, rect): - rl.draw_rectangle_rounded(rect, 0.08, 20, rl.WHITE) - rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) self._draw_gradient_background(rect) + rl.draw_rectangle_rounded_lines_ex(self._rect, 0.19, 10, 5, rl.BLACK) rl.end_scissor_mode() # Draw vertical separator line diff --git a/selfdrive/ui/widgets/prime.py b/selfdrive/ui/widgets/prime.py index 1e31ac8a1a..e779ead334 100644 --- a/selfdrive/ui/widgets/prime.py +++ b/selfdrive/ui/widgets/prime.py @@ -22,7 +22,7 @@ class PrimeWidget(Widget): def _render_for_non_prime_users(self, rect: rl.Rectangle): """Renders the advertisement for non-Prime users.""" - rl.draw_rectangle_rounded(rect, 0.02, 10, self.PRIME_BG_COLOR) + rl.draw_rectangle_rounded(rect, 0.025, 10, self.PRIME_BG_COLOR) # Layout x, y = rect.x + 80, rect.y + 90 @@ -52,7 +52,7 @@ class PrimeWidget(Widget): def _render_for_prime_user(self, rect: rl.Rectangle): """Renders the prime user widget with subscription status.""" - rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 230), 0.05, 10, self.PRIME_BG_COLOR) + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 230), 0.1, 10, self.PRIME_BG_COLOR) x = rect.x + 56 y = rect.y + 40 diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index dfaa3e4008..f786d1ab2f 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -30,7 +30,7 @@ class SetupWidget(Widget): def _render_registration(self, rect: rl.Rectangle): """Render registration prompt.""" - rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, rect.height), 0.02, 20, rl.Color(51, 51, 51, 255)) + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, rect.height), 0.03, 20, rl.Color(51, 51, 51, 255)) x = rect.x + 64 y = rect.y + 48 @@ -55,7 +55,7 @@ class SetupWidget(Widget): def _render_firehose_prompt(self, rect: rl.Rectangle): """Render firehose prompt widget.""" - rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 500), 0.02, 20, rl.Color(51, 51, 51, 255)) + rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 500), 0.04, 20, rl.Color(51, 51, 51, 255)) # Content margins (56, 40, 56, 40) x = rect.x + 56 From 5f0e9fce612f408b5fcc9c952bdd143669a122af Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Oct 2025 00:40:41 -0700 Subject: [PATCH 167/341] raylib: update experimental mode homescreen button (#36344) * update homescreen exp mode button * and here * Apply suggestions from code review --- selfdrive/ui/layouts/home.py | 4 ++++ selfdrive/ui/widgets/exp_mode_button.py | 3 +++ 2 files changed, 7 insertions(+) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 70960ee22f..519e0e020a 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -62,6 +62,7 @@ class HomeLayout(Widget): self._setup_callbacks() def show_event(self): + self._exp_mode_button.show_event() self.last_refresh = time.monotonic() self._refresh() @@ -76,6 +77,9 @@ class HomeLayout(Widget): def _set_state(self, state: HomeLayoutState): # propagate show/hide events if state != self.current_state: + if state == HomeLayoutState.HOME: + self._exp_mode_button.show_event() + if state in self._layout_widgets: self._layout_widgets[state].show_event() if self.current_state in self._layout_widgets: diff --git a/selfdrive/ui/widgets/exp_mode_button.py b/selfdrive/ui/widgets/exp_mode_button.py index 97c399cdab..23031be306 100644 --- a/selfdrive/ui/widgets/exp_mode_button.py +++ b/selfdrive/ui/widgets/exp_mode_button.py @@ -18,6 +18,9 @@ class ExperimentalModeButton(Widget): self.chill_pixmap = gui_app.texture("icons/couch.png", self.img_width, self.img_width) self.experimental_pixmap = gui_app.texture("icons/experimental_grey.png", self.img_width, self.img_width) + def show_event(self): + self.experimental_mode = self.params.get_bool("ExperimentalMode") + def _get_gradient_colors(self): alpha = 0xCC if self.is_pressed else 0xFF From 87443cd34d8db902a5e665b9ef679f09899a1c1a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Oct 2025 00:45:03 -0700 Subject: [PATCH 168/341] raylib: background onboarding texture loading (#36343) * this seems best so far * better * clean up * debug * debug * clean up * final --- selfdrive/ui/layouts/onboarding.py | 40 +++++++++++++++++++++--------- system/ui/lib/application.py | 12 ++++++--- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/selfdrive/ui/layouts/onboarding.py b/selfdrive/ui/layouts/onboarding.py index bccb36712c..f4ab1a05c6 100644 --- a/selfdrive/ui/layouts/onboarding.py +++ b/selfdrive/ui/layouts/onboarding.py @@ -1,5 +1,6 @@ import os import re +import threading from enum import IntEnum import pyray as rl @@ -38,15 +39,24 @@ class TrainingGuide(Widget): self._completed_callback = completed_callback self._step = 0 - self._load_images() + self._load_image_paths() - def _load_images(self): - self._images = [] + # Load first image now so we show something immediately + self._textures = [gui_app.texture(self._image_paths[0])] + self._image_objs = [] + + threading.Thread(target=self._preload_thread, daemon=True).start() + + def _load_image_paths(self): paths = [fn for fn in os.listdir(os.path.join(BASEDIR, "selfdrive/assets/training")) if re.match(r'^step\d*\.png$', fn)] paths = sorted(paths, key=lambda x: int(re.search(r'\d+', x).group())) - for fn in paths: - path = os.path.join(BASEDIR, "selfdrive/assets/training", fn) - self._images.append(gui_app.texture(path)) + self._image_paths = [os.path.join(BASEDIR, "selfdrive/assets/training", fn) for fn in paths] + + def _preload_thread(self): + # PNG loading is slow in raylib, so we preload in a thread and upload to GPU in main thread + # We've already loaded the first image on init + for path in self._image_paths[1:]: + self._image_objs.append(gui_app._load_image_from_path(path)) def _handle_mouse_release(self, mouse_pos): if rl.check_collision_point_rec(mouse_pos, STEP_RECTS[self._step]): @@ -57,30 +67,36 @@ class TrainingGuide(Widget): ui_state.params.put_bool("RecordFront", yes) # Restart training? - elif self._step == len(self._images) - 1: + elif self._step == len(self._image_paths) - 1: if rl.check_collision_point_rec(mouse_pos, RESTART_TRAINING_RECT): self._step = -1 self._step += 1 # Finished? - if self._step >= len(self._images): + if self._step >= len(self._image_paths): self._step = 0 if self._completed_callback: self._completed_callback() + def _update_state(self): + if len(self._image_objs): + self._textures.append(gui_app._load_texture_from_image(self._image_objs.pop(0))) + def _render(self, _): - rl.draw_texture(self._images[self._step], 0, 0, rl.WHITE) + # Safeguard against fast tapping + step = min(self._step, len(self._textures) - 1) + rl.draw_texture(self._textures[step], 0, 0, rl.WHITE) # progress bar - if 0 < self._step < len(STEP_RECTS) - 1: + if 0 < step < len(STEP_RECTS) - 1: h = 20 - w = int((self._step / (len(STEP_RECTS) - 1)) * self._rect.width) + w = int((step / (len(STEP_RECTS) - 1)) * self._rect.width) rl.draw_rectangle(int(self._rect.x), int(self._rect.y + self._rect.height - h), w, h, rl.Color(70, 91, 234, 255)) if DEBUG: - rl.draw_rectangle_lines_ex(STEP_RECTS[self._step], 3, rl.RED) + rl.draw_rectangle_lines_ex(STEP_RECTS[step], 3, rl.RED) return -1 diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 1925122d0b..4a62c996c7 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -196,13 +196,14 @@ class GuiApplication: return self._textures[cache_key] with as_file(ASSETS_DIR.joinpath(asset_path)) as fspath: - texture_obj = self._load_texture_from_image(fspath.as_posix(), width, height, alpha_premultiply, keep_aspect_ratio) + image_obj = self._load_image_from_path(fspath.as_posix(), width, height, alpha_premultiply, keep_aspect_ratio) + texture_obj = self._load_texture_from_image(image_obj) self._textures[cache_key] = texture_obj return texture_obj - def _load_texture_from_image(self, image_path: str, width: int | None = None, height: int | None = None, - alpha_premultiply=False, keep_aspect_ratio=True): - """Load and resize a texture, storing it for later automatic unloading.""" + def _load_image_from_path(self, image_path: str, width: int | None = None, height: int | None = None, + alpha_premultiply: bool = False, keep_aspect_ratio: bool = True) -> rl.Image: + """Load and resize an image, storing it for later automatic unloading.""" image = rl.load_image(image_path) if alpha_premultiply: @@ -225,7 +226,10 @@ class GuiApplication: rl.image_resize(image, new_width, new_height) else: rl.image_resize(image, width, height) + return image + def _load_texture_from_image(self, image: rl.Image) -> rl.Texture: + """Send image to GPU and unload original image.""" texture = rl.load_texture_from_image(image) # Set texture filtering to smooth the result rl.set_texture_filter(texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) From 3546b625e7e37e5abfb187a679fd379c75eed96a Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:22:17 -0700 Subject: [PATCH 169/341] latcontrol_torque: change in kp should not affect effective low speed factor gain (#36335) --- selfdrive/controls/lib/latcontrol_torque.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 2a1b666efd..664e866636 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -77,7 +77,7 @@ class LatControlTorque(LatControl): low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2 setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel error = setpoint - measurement - error_lsf = error + low_speed_factor * error + error_lsf = error + low_speed_factor / self.torque_params.kp * error # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly pid_log.error = float(error_lsf) From 8a1fcd8991cde9c122bfe5d01b835dee85b1b681 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Oct 2025 21:17:36 -0700 Subject: [PATCH 170/341] raylib: fix possible DM crash (#36354) * fix * bruh * clean up * here * rm --- selfdrive/ui/onroad/alert_renderer.py | 5 ++--- selfdrive/ui/onroad/augmented_road_view.py | 4 ++-- selfdrive/ui/onroad/driver_state.py | 14 ++++++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 9754d488ed..362f49a51e 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -107,10 +107,10 @@ class AlertRenderer(Widget): # Return current alert return Alert(text1=ss.alertText1, text2=ss.alertText2, size=ss.alertSize.raw, status=ss.alertStatus.raw) - def _render(self, rect: rl.Rectangle) -> bool: + def _render(self, rect: rl.Rectangle): alert = self.get_alert(ui_state.sm) if not alert: - return False + return alert_rect = self._get_alert_rect(rect, alert.size) self._draw_background(alert_rect, alert) @@ -122,7 +122,6 @@ class AlertRenderer(Widget): alert_rect.height - 2 * ALERT_PADDING ) self._draw_text(text_rect, alert) - return True def _get_alert_rect(self, rect: rl.Rectangle, size: int) -> rl.Rectangle: if size == AlertSize.full: diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index cac2f9c968..bffa30ad22 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -89,8 +89,8 @@ class AugmentedRoadView(CameraView): # Draw all UI overlays self.model_renderer.render(self._content_rect) self._hud_renderer.render(self._content_rect) - if not self.alert_renderer.render(self._content_rect): - self.driver_state_renderer.render(self._content_rect) + self.alert_renderer.render(self._content_rect) + self.driver_state_renderer.render(self._content_rect) # Custom UI extension point - add custom overlays here # Use self._content_rect for positioning within camera bounds diff --git a/selfdrive/ui/onroad/driver_state.py b/selfdrive/ui/onroad/driver_state.py index 69ed5f458f..8aaef1bcfb 100644 --- a/selfdrive/ui/onroad/driver_state.py +++ b/selfdrive/ui/onroad/driver_state.py @@ -1,10 +1,13 @@ import numpy as np import pyray as rl +from cereal import log from dataclasses import dataclass from openpilot.selfdrive.ui.ui_state import ui_state, UI_BORDER_SIZE from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import Widget +AlertSize = log.SelfdriveState.AlertSize + # Default 3D coordinates for face keypoints as a NumPy array DEFAULT_FACE_KPTS_3D = np.array([ [-5.98, -51.20, 8.00], [-17.64, -49.14, 8.00], [-23.81, -46.40, 8.00], [-29.98, -40.91, 8.00], @@ -50,7 +53,6 @@ class DriverStateRenderer(Widget): self.is_active = False self.is_rhd = False self.dm_fade_state = 0.0 - self.last_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) self.driver_pose_vals = np.zeros(3, dtype=np.float32) self.driver_pose_diff = np.zeros(3, dtype=np.float32) self.driver_pose_sins = np.zeros(3, dtype=np.float32) @@ -75,8 +77,8 @@ class DriverStateRenderer(Widget): self.engaged_color = rl.Color(26, 242, 66, 255) self.disengaged_color = rl.Color(139, 139, 139, 255) - self.set_visible(lambda: (ui_state.sm.recv_frame['driverStateV2'] > ui_state.started_frame and - ui_state.sm.seen['driverMonitoringState'])) + self.set_visible(lambda: (ui_state.sm["selfdriveState"].alertSize == AlertSize.none and + ui_state.sm.recv_frame["driverStateV2"] > ui_state.started_frame)) def _render(self, rect): # Set opacity based on active state @@ -106,11 +108,7 @@ class DriverStateRenderer(Widget): def _update_state(self): """Update the driver monitoring state based on model data""" sm = ui_state.sm - if not sm.updated["driverMonitoringState"]: - if (self._rect.x != self.last_rect.x or self._rect.y != self.last_rect.y or - self._rect.width != self.last_rect.width or self._rect.height != self.last_rect.height): - self._pre_calculate_drawing_elements() - self.last_rect = self._rect + if not self.is_visible: return # Get monitoring state From 59bddfba8dca350d3fdc1d6fa389070d4ccc14a2 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Oct 2025 21:24:36 -0700 Subject: [PATCH 171/341] raylib: rounded onroad corners (#36348) * rounded corners * use scissor * 0.1 * middle * don't trust chatter * round * clean p * cleanup * rev --- selfdrive/ui/onroad/augmented_road_view.py | 31 +++++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index bffa30ad22..0e0adceaf6 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -20,9 +20,9 @@ WIDE_CAM = VisionStreamType.VISION_STREAM_WIDE_ROAD DEFAULT_DEVICE_CAMERA = DEVICE_CAMERAS["tici", "ar0231"] BORDER_COLORS = { - UIStatus.DISENGAGED: rl.Color(0x17, 0x33, 0x49, 0xC8), # Blue for disengaged state - UIStatus.OVERRIDE: rl.Color(0x91, 0x9B, 0x95, 0xF1), # Gray for override state - UIStatus.ENGAGED: rl.Color(0x17, 0x86, 0x44, 0xF1), # Green for engaged state + UIStatus.DISENGAGED: rl.Color(0x12, 0x28, 0x39, 0xFF), # Blue for disengaged state + UIStatus.OVERRIDE: rl.Color(0x89, 0x92, 0x8D, 0xFF), # Gray for override state + UIStatus.ENGAGED: rl.Color(0x16, 0x7F, 0x40, 0xFF), # Green for engaged state } WIDE_CAM_MAX_SPEED = 10.0 # m/s (22 mph) @@ -71,9 +71,6 @@ class AugmentedRoadView(CameraView): rect.height - 2 * UI_BORDER_SIZE, ) - # Draw colored border based on driving state - self._draw_border(rect) - # Enable scissor mode to clip all rendering within content rectangle boundaries # This creates a rendering viewport that prevents graphics from drawing outside the border rl.begin_scissor_mode( @@ -98,6 +95,9 @@ class AugmentedRoadView(CameraView): # End clipping region rl.end_scissor_mode() + # Draw colored border based on driving state + self._draw_border(rect) + # publish uiDebug msg = messaging.new_message('uiDebug') msg.uiDebug.drawTimeMillis = (time.monotonic() - start_draw) * 1000 @@ -112,8 +112,25 @@ class AugmentedRoadView(CameraView): pass def _draw_border(self, rect: rl.Rectangle): + rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) + border_roundness = 0.15 border_color = BORDER_COLORS.get(ui_state.status, BORDER_COLORS[UIStatus.DISENGAGED]) - rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, border_color) + border_rect = rl.Rectangle(rect.x + UI_BORDER_SIZE, rect.y + UI_BORDER_SIZE, + rect.width - 2 * UI_BORDER_SIZE, rect.height - 2 * UI_BORDER_SIZE) + rl.draw_rectangle_rounded_lines_ex(border_rect, border_roundness, 10, UI_BORDER_SIZE, border_color) + + # black bg around colored border + black_bg_thickness = UI_BORDER_SIZE + black_bg_rect = rl.Rectangle( + border_rect.x - UI_BORDER_SIZE, + border_rect.y - UI_BORDER_SIZE, + border_rect.width + 2 * UI_BORDER_SIZE, + border_rect.height + 2 * UI_BORDER_SIZE, + ) + edge_offset = (black_bg_rect.height - border_rect.height) / 2 # distance between rect edges + roundness_out = (border_roundness * border_rect.height + 2 * edge_offset) / max(1.0, black_bg_rect.height) + rl.draw_rectangle_rounded_lines_ex(black_bg_rect, roundness_out, 10, black_bg_thickness, rl.BLACK) + rl.end_scissor_mode() def _switch_stream_if_needed(self, sm): if sm['selfdriveState'].experimentalMode and WIDE_CAM in self.available_streams: From c44548ba0f0464aad0bd9b52a067829e95d4d37a Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Tue, 14 Oct 2025 21:27:52 -0700 Subject: [PATCH 172/341] camerad: make wide brightness more consistent with road (#36355) align --- system/camerad/cameras/camera_qcom2.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 85f358977c..f2a064c606 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -89,7 +89,7 @@ void CameraState::set_exposure_rect() { // set areas for each camera, shouldn't be changed std::vector> ae_targets = { // (Rect, F) - std::make_pair((Rect){96, 250, 1734, 524}, 567.0), // wide + std::make_pair((Rect){96, 400, 1734, 524}, 567.0), // wide std::make_pair((Rect){96, 160, 1734, 986}, 2648.0), // road std::make_pair((Rect){96, 242, 1736, 906}, 567.0) // driver }; From 0c64818f525fb297417f87a7ed4a4ce60351163e Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Tue, 14 Oct 2025 23:36:04 -0500 Subject: [PATCH 173/341] Add screenshot for advanced network settings (#36351) add screenshot for advanced network settings --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index a65f198ad6..ea3f9c4802 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -53,6 +53,11 @@ def setup_settings_network(click, pm: PubMaster): click(278, 450) +def setup_settings_network_advanced(click, pm: PubMaster): + setup_settings_network(click, pm) + click(1880, 100) + + def setup_settings_toggles(click, pm: PubMaster): setup_settings(click, pm) click(278, 600) @@ -128,6 +133,7 @@ CASES = { "homescreen": setup_homescreen, "settings_device": setup_settings, "settings_network": setup_settings_network, + "settings_network_advanced": setup_settings_network_advanced, "settings_toggles": setup_settings_toggles, "settings_software": setup_settings_software, "settings_software_download": setup_settings_software_download, From f290fb1e05f590f8d89f62c26b631b8e7d70a6be Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Wed, 15 Oct 2025 00:15:31 -0500 Subject: [PATCH 174/341] keyboard: fix double space (#36345) * support multiple characters added add cursor position * fix * remove double space * Revert "fix" This reverts commit c938a52995b6f5343b461f408af5838b78f453d2. * Revert "support multiple characters added add cursor position" This reverts commit d8225a768686a88f2bdaabae6d2a57c541ac7f77. --- system/ui/widgets/keyboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 70f06f6b9d..85bc477bf6 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -19,7 +19,7 @@ DELETE_REPEAT_INTERVAL = 0.07 CONTENT_MARGIN = 50 BACKSPACE_KEY = "<-" ENTER_KEY = "->" -SPACE_KEY = " " +SPACE_KEY = " " SHIFT_INACTIVE_KEY = "SHIFT_OFF" SHIFT_ACTIVE_KEY = "SHIFT_ON" CAPS_LOCK_KEY = "CAPS" From 3553a754a46c1b99c9f810545471c1421e44ab65 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Oct 2025 22:45:33 -0700 Subject: [PATCH 175/341] Fix vendored emoji font (#36357) * add font * use it * rm old one --- selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf | 3 --- selfdrive/assets/fonts/NotoColorEmoji.ttf | 3 +++ system/ui/lib/emoji.py | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) delete mode 100644 selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf create mode 100644 selfdrive/assets/fonts/NotoColorEmoji.ttf diff --git a/selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf b/selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf deleted file mode 100644 index 2579d30f65..0000000000 --- a/selfdrive/assets/fonts/NotoColorEmoji-Regular.ttf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:69f216a4ec672bb910d652678301ffe3094c44e5d03276e794ef793d936a1f1d -size 25096376 diff --git a/selfdrive/assets/fonts/NotoColorEmoji.ttf b/selfdrive/assets/fonts/NotoColorEmoji.ttf new file mode 100644 index 0000000000..778e821ce3 --- /dev/null +++ b/selfdrive/assets/fonts/NotoColorEmoji.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93cdc4ee9aa40e2afceecc63da0ca05ec7aab4bec991ece51a6b52389f48a477 +size 10788068 diff --git a/system/ui/lib/emoji.py b/system/ui/lib/emoji.py index 28139158a1..519f567d3f 100644 --- a/system/ui/lib/emoji.py +++ b/system/ui/lib/emoji.py @@ -4,6 +4,8 @@ import re from PIL import Image, ImageDraw, ImageFont import pyray as rl +from openpilot.system.ui.lib.application import FONT_DIR + _cache: dict[str, rl.Texture] = {} EMOJI_REGEX = re.compile( @@ -37,7 +39,7 @@ def emoji_tex(emoji): if emoji not in _cache: img = Image.new("RGBA", (128, 128), (0, 0, 0, 0)) draw = ImageDraw.Draw(img) - font = ImageFont.truetype("NotoColorEmoji", 109) + font = ImageFont.truetype(FONT_DIR.joinpath("NotoColorEmoji.ttf"), 109) draw.text((0, 0), emoji, font=font, embedded_color=True) buffer = io.BytesIO() img.save(buffer, format="PNG") From 57223958b533f4f507b1851c50001896f00ac085 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Oct 2025 23:56:15 -0700 Subject: [PATCH 176/341] raylib screenshots: add more homescreen states (#36356) * hmm can do yeidl approach * clean up * clean up * flip * and add paired + prime * sort and add update params * try * all should have branch name * test * clean up * add offroad alert to update screen --- .../ui/tests/test_ui/raylib_screenshots.py | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index ea3f9c4802..3f3958f9d3 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -31,15 +31,19 @@ OFFROAD_ALERTS = ['Offroad_IsTakingSnapshot'] def put_update_params(params: Params): params.put("UpdaterCurrentReleaseNotes", parse_release_notes(BASEDIR)) params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR)) - description = "0.10.1 / this-is-a-really-super-mega-long-branch-name / 7864838 / Oct 03" - params.put("UpdaterCurrentDescription", description) - params.put("UpdaterNewDescription", description) def setup_homescreen(click, pm: PubMaster): pass +def setup_homescreen_update_available(click, pm: PubMaster): + params = Params() + params.put_bool("UpdateAvailable", True) + put_update_params(params) + setup_offroad_alert(click, pm) + + def setup_settings(click, pm: PubMaster): click(100, 100) @@ -102,6 +106,7 @@ def setup_pair_device(click, pm: PubMaster): def setup_offroad_alert(click, pm: PubMaster): + put_update_params(Params()) set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C') set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal') for alert in OFFROAD_ALERTS: @@ -116,14 +121,6 @@ def setup_confirmation_dialog(click, pm: PubMaster): click(1985, 791) # reset calibration -def setup_homescreen_update_available(click, pm: PubMaster): - params = Params() - params.put_bool("UpdateAvailable", True) - put_update_params(params) - setup_settings(click, pm) - close_settings(click, pm) - - def setup_experimental_mode_description(click, pm: PubMaster): setup_settings_toggles(click, pm) click(1200, 280) # expand description for experimental mode @@ -131,6 +128,9 @@ def setup_experimental_mode_description(click, pm: PubMaster): CASES = { "homescreen": setup_homescreen, + "homescreen_paired": setup_homescreen, + "homescreen_prime": setup_homescreen, + "homescreen_update_available": setup_homescreen_update_available, "settings_device": setup_settings, "settings_network": setup_settings_network, "settings_network_advanced": setup_settings_network_advanced, @@ -143,7 +143,6 @@ CASES = { "keyboard": setup_keyboard, "pair_device": setup_pair_device, "offroad_alert": setup_offroad_alert, - "homescreen_update_available": setup_homescreen_update_available, "confirmation_dialog": setup_confirmation_dialog, "experimental_mode_description": setup_experimental_mode_description, } @@ -194,10 +193,21 @@ def create_screenshots(): SCREENSHOTS_DIR.mkdir(parents=True) t = TestUI() - with OpenpilotPrefix(): - params = Params() - params.put("DongleId", "123456789012345") - for name, setup in CASES.items(): + for name, setup in CASES.items(): + with OpenpilotPrefix(): + params = Params() + params.put("DongleId", "123456789012345") + + # Set branch name + description = "0.10.1 / this-is-a-really-super-mega-long-branch-name / 7864838 / Oct 03" + params.put("UpdaterCurrentDescription", description) + params.put("UpdaterNewDescription", description) + + if name == "homescreen_paired": + params.put("PrimeType", 0) # NONE + elif name == "homescreen_prime": + params.put("PrimeType", 2) # LITE + t.test_ui(name, setup) From 75858673c4215d3dfbb3f0190ec8dabb776bb948 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 00:01:52 -0700 Subject: [PATCH 177/341] less rounded border --- selfdrive/ui/onroad/augmented_road_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 0e0adceaf6..661496a032 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -113,7 +113,7 @@ class AugmentedRoadView(CameraView): def _draw_border(self, rect: rl.Rectangle): rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - border_roundness = 0.15 + border_roundness = 0.12 border_color = BORDER_COLORS.get(ui_state.status, BORDER_COLORS[UIStatus.DISENGAGED]) border_rect = rl.Rectangle(rect.x + UI_BORDER_SIZE, rect.y + UI_BORDER_SIZE, rect.width - 2 * UI_BORDER_SIZE, rect.height - 2 * UI_BORDER_SIZE) From 3e566129905216ae533a374e4b3a78c11b8a4515 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 00:06:04 -0700 Subject: [PATCH 178/341] raylib: fix emoji vertical centering (#36358) * space * font scale * fix centering --- selfdrive/ui/widgets/setup.py | 4 ++-- system/ui/widgets/label.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index f786d1ab2f..b0f122227e 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -16,7 +16,7 @@ class SetupWidget(Widget): self._pair_device_btn = Button("Pair device", self._show_pairing, button_style=ButtonStyle.PRIMARY) self._open_settings_btn = Button("Open", lambda: self._open_settings_callback() if self._open_settings_callback else None, button_style=ButtonStyle.PRIMARY) - self._firehose_label = Label("🔥Firehose Mode 🔥", font_weight=FontWeight.MEDIUM, font_size=64) + self._firehose_label = Label("🔥 Firehose Mode 🔥", font_weight=FontWeight.MEDIUM, font_size=64) def set_open_settings_callback(self, callback): self._open_settings_callback = callback @@ -65,7 +65,7 @@ class SetupWidget(Widget): # Title with fire emojis # TODO: fix Label centering with emojis - self._firehose_label.render(rl.Rectangle(x - 40, y, w, 64)) + self._firehose_label.render(rl.Rectangle(x - 48, y, w, 64)) y += 64 + spacing # Description diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 33c4ef29d3..65b579209e 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -170,7 +170,7 @@ class Label(Widget): line_pos.x += width_before.x tex = emoji_tex(emoji) - rl.draw_texture_ex(tex, line_pos, 0.0, self._font_size / tex.height, self._text_color) + rl.draw_texture_ex(tex, line_pos, 0.0, self._font_size / tex.height * FONT_SCALE, self._text_color) line_pos.x += self._font_size * FONT_SCALE prev_index = end rl.draw_text_ex(self._font, text[prev_index:], line_pos, self._font_size, 0, self._text_color) From a2c4fe1c90b17476e4b2caa77214ad2c4b193493 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 00:11:16 -0700 Subject: [PATCH 179/341] raylib: fix setup styles (#36322) * hardcoding is bad for you * do updater * reset * lint * duh! * fixup setup * fixup updater * unround --- system/ui/reset.py | 6 ++-- system/ui/setup.py | 59 +++++++++++++++++++++----------------- system/ui/updater.py | 6 ++-- system/ui/widgets/label.py | 4 +-- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/system/ui/reset.py b/system/ui/reset.py index 85ce4a858b..8f6466ce46 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -8,7 +8,7 @@ from enum import IntEnum import pyray as rl from openpilot.system.hardware import PC -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.label import gui_label, gui_text_box @@ -70,10 +70,10 @@ class Reset(Widget): exit(0) def _render(self, rect: rl.Rectangle): - label_rect = rl.Rectangle(rect.x + 140, rect.y, rect.width - 280, 100) + label_rect = rl.Rectangle(rect.x + 140, rect.y, rect.width - 280, 100 * FONT_SCALE) gui_label(label_rect, "System Reset", 100, font_weight=FontWeight.BOLD) - text_rect = rl.Rectangle(rect.x + 140, rect.y + 140, rect.width - 280, rect.height - 90 - 100) + text_rect = rl.Rectangle(rect.x + 140, rect.y + 140, rect.width - 280, rect.height - 90 - 100 * FONT_SCALE) gui_text_box(text_rect, self._get_body_text(), 90) button_height = 160 diff --git a/system/ui/setup.py b/system/ui/setup.py index 99a3569fbe..a7fa4369a9 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -14,7 +14,7 @@ from cereal import log from openpilot.common.run import run_cmd from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle, ButtonRadio from openpilot.system.ui.widgets.keyboard import Keyboard @@ -24,10 +24,10 @@ from openpilot.system.ui.widgets.network import WifiManagerUI, WifiManager NetworkType = log.DeviceState.NetworkType MARGIN = 50 -TITLE_FONT_SIZE = 116 +TITLE_FONT_SIZE = 90 TITLE_FONT_WEIGHT = FontWeight.MEDIUM NEXT_BUTTON_WIDTH = 310 -BODY_FONT_SIZE = 96 +BODY_FONT_SIZE = 80 BUTTON_HEIGHT = 160 BUTTON_SPACING = 50 @@ -48,6 +48,7 @@ cd /data/openpilot exec ./launch_openpilot.sh """ + class SetupState(IntEnum): LOW_VOLTAGE = 0 GETTING_STARTED = 1 @@ -100,7 +101,7 @@ class Setup(Widget): self._download_failed_reboot_button = Button("Reboot device", HARDWARE.reboot) self._download_failed_startover_button = Button("Start over", self._download_failed_startover_button_callback, button_style=ButtonStyle.PRIMARY) self._download_failed_title_label = Label("Download Failed", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) - self._download_failed_url_label = Label("", 64, FontWeight.NORMAL, TextAlignment.LEFT) + self._download_failed_url_label = Label("", 52, FontWeight.NORMAL, TextAlignment.LEFT) self._download_failed_body_label = Label("", BODY_FONT_SIZE, text_alignment=TextAlignment.LEFT) self._network_setup_back_button = Button("Back", self._network_setup_back_button_callback) @@ -113,15 +114,16 @@ class Setup(Widget): button_style=ButtonStyle.PRIMARY) self._custom_software_warning_continue_button.set_enabled(False) self._custom_software_warning_back_button = Button("Back", self._custom_software_warning_back_button_callback) - self._custom_software_warning_title_label = Label("WARNING: Custom Software", 100, FontWeight.BOLD, TextAlignment.LEFT, text_color=rl.Color(255,89,79,255), + self._custom_software_warning_title_label = Label("WARNING: Custom Software", 81, FontWeight.BOLD, TextAlignment.LEFT, + text_color=rl.Color(255, 89, 79, 255), text_padding=60) self._custom_software_warning_body_label = Label("Use caution when installing third-party software.\n\n" - + "⚠️ It has not been tested by comma.\n\n" - + "⚠️ It may not comply with relevant safety standards.\n\n" - + "⚠️ It may cause damage to your device and/or vehicle.\n\n" - + "If you'd like to proceed, use https://flash.comma.ai " - + "to restore your device to a factory state later.", - 85, text_alignment=TextAlignment.LEFT, text_padding=60) + + "⚠️ It has not been tested by comma.\n\n" + + "⚠️ It may not comply with relevant safety standards.\n\n" + + "⚠️ It may cause damage to your device and/or vehicle.\n\n" + + "If you'd like to proceed, use https://flash.comma.ai " + + "to restore your device to a factory state later.", + 68, text_alignment=TextAlignment.LEFT, text_padding=60) self._custom_software_warning_body_scroll_panel = GuiScrollPanel() self._downloading_body_label = Label("Downloading...", TITLE_FONT_SIZE, FontWeight.MEDIUM) @@ -191,8 +193,8 @@ class Setup(Widget): def render_low_voltage(self, rect: rl.Rectangle): rl.draw_texture(self.warning, int(rect.x + 150), int(rect.y + 110), rl.WHITE) - self._low_voltage_title_label.render(rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 100, rect.width - 500 - 150, TITLE_FONT_SIZE)) - self._low_voltage_body_label.render(rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 150, rect.width - 500, BODY_FONT_SIZE * 3)) + self._low_voltage_title_label.render(rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 100, rect.width - 500 - 150, TITLE_FONT_SIZE * FONT_SCALE)) + self._low_voltage_body_label.render(rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 150, rect.width - 500, BODY_FONT_SIZE * FONT_SCALE * 3)) button_width = (rect.width - MARGIN * 3) / 2 button_y = rect.height - MARGIN - BUTTON_HEIGHT @@ -200,8 +202,9 @@ class Setup(Widget): self._low_voltage_continue_button.render(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT)) def render_getting_started(self, rect: rl.Rectangle): - self._getting_started_title_label.render(rl.Rectangle(rect.x + 165, rect.y + 280, rect.width - 265, TITLE_FONT_SIZE)) - self._getting_started_body_label.render(rl.Rectangle(rect.x + 165, rect.y + 280 + TITLE_FONT_SIZE, rect.width - 500, BODY_FONT_SIZE * 3)) + self._getting_started_title_label.render(rl.Rectangle(rect.x + 165, rect.y + 280, rect.width - 265, TITLE_FONT_SIZE * FONT_SCALE)) + self._getting_started_body_label.render(rl.Rectangle(rect.x + 165, rect.y + 280 + TITLE_FONT_SIZE * FONT_SCALE, rect.width - 500, + BODY_FONT_SIZE * FONT_SCALE * 3)) btn_rect = rl.Rectangle(rect.width - NEXT_BUTTON_WIDTH, 0, NEXT_BUTTON_WIDTH, rect.height) self._getting_started_button.render(btn_rect) @@ -233,10 +236,10 @@ class Setup(Widget): self.network_check_thread.join() def render_network_setup(self, rect: rl.Rectangle): - self._network_setup_title_label.render(rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE)) + self._network_setup_title_label.render(rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE * FONT_SCALE)) - wifi_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN + 25, rect.width - MARGIN * 2, - rect.height - TITLE_FONT_SIZE - 25 - BUTTON_HEIGHT - MARGIN * 3) + wifi_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE * FONT_SCALE + MARGIN + 25, rect.width - MARGIN * 2, + rect.height - TITLE_FONT_SIZE * FONT_SCALE - 25 - BUTTON_HEIGHT - MARGIN * 3) rl.draw_rectangle_rounded(wifi_rect, 0.05, 10, rl.Color(51, 51, 51, 255)) wifi_content_rect = rl.Rectangle(wifi_rect.x + MARGIN, wifi_rect.y, wifi_rect.width - MARGIN * 2, wifi_rect.height) self.wifi_ui.render(wifi_content_rect) @@ -254,21 +257,22 @@ class Setup(Widget): self._network_setup_continue_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_software_selection(self, rect: rl.Rectangle): - self._software_selection_title_label.render(rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE)) + self._software_selection_title_label.render(rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE * FONT_SCALE)) radio_height = 230 radio_spacing = 30 self._software_selection_continue_button.set_enabled(False) - openpilot_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN * 2, rect.width - MARGIN * 2, radio_height) + openpilot_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE * FONT_SCALE + MARGIN * 2, rect.width - MARGIN * 2, radio_height) self._software_selection_openpilot_button.render(openpilot_rect) if self._software_selection_openpilot_button.selected: self._software_selection_continue_button.set_enabled(True) self._software_selection_custom_software_button.selected = False - custom_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN * 2 + radio_height + radio_spacing, rect.width - MARGIN * 2, radio_height) + custom_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE * FONT_SCALE + MARGIN * 2 + radio_height + radio_spacing, rect.width - MARGIN * 2, + radio_height) self._software_selection_custom_software_button.render(custom_rect) if self._software_selection_custom_software_button.selected: @@ -282,12 +286,13 @@ class Setup(Widget): self._software_selection_continue_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_downloading(self, rect: rl.Rectangle): - self._downloading_body_label.render(rl.Rectangle(rect.x, rect.y + rect.height / 2 - TITLE_FONT_SIZE / 2, rect.width, TITLE_FONT_SIZE)) + self._downloading_body_label.render(rl.Rectangle(rect.x, rect.y + rect.height / 2 - TITLE_FONT_SIZE * FONT_SCALE / 2, rect.width, + TITLE_FONT_SIZE * FONT_SCALE)) def render_download_failed(self, rect: rl.Rectangle): - self._download_failed_title_label.render(rl.Rectangle(rect.x + 117, rect.y + 185, rect.width - 117, TITLE_FONT_SIZE)) + self._download_failed_title_label.render(rl.Rectangle(rect.x + 117, rect.y + 185, rect.width - 117, TITLE_FONT_SIZE * FONT_SCALE)) self._download_failed_url_label.set_text(self.failed_url) - self._download_failed_url_label.render(rl.Rectangle(rect.x + 117, rect.y + 185 + TITLE_FONT_SIZE + 67, rect.width - 117 - 100, 64)) + self._download_failed_url_label.render(rl.Rectangle(rect.x + 117, rect.y + 185 + TITLE_FONT_SIZE * FONT_SCALE + 67, rect.width - 117 - 100, 64)) self._download_failed_body_label.set_text(self.failed_reason) self._download_failed_body_label.render(rl.Rectangle(rect.x + 117, rect.y, rect.width - 117 - 100, rect.height)) @@ -304,10 +309,10 @@ class Setup(Widget): button_width = (rect.width - MARGIN * 3) / 2 button_y = rect.height - MARGIN - BUTTON_HEIGHT - rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(button_y - BODY_FONT_SIZE)) + rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(button_y - BODY_FONT_SIZE * FONT_SCALE)) y_offset = rect.y + offset - self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, y_offset + 150, rect.width - 265, TITLE_FONT_SIZE)) - self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, y_offset + 200 , rect.width - 50, BODY_FONT_SIZE * 3)) + self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, y_offset + 150, rect.width - 265, TITLE_FONT_SIZE * FONT_SCALE)) + self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, y_offset + 200, rect.width - 50, BODY_FONT_SIZE * FONT_SCALE * 3)) rl.end_scissor_mode() self._custom_software_warning_back_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) diff --git a/system/ui/updater.py b/system/ui/updater.py index 38a93687e0..5dd5a69c69 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -6,7 +6,7 @@ import pyray as rl from enum import IntEnum from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.lib.wifi_manager import WifiManager from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle @@ -89,14 +89,14 @@ class Updater(Widget): def render_prompt_screen(self, rect: rl.Rectangle): # Title - title_rect = rl.Rectangle(MARGIN + 50, 250, rect.width - MARGIN * 2 - 100, TITLE_FONT_SIZE) + title_rect = rl.Rectangle(MARGIN + 50, 250, rect.width - MARGIN * 2 - 100, TITLE_FONT_SIZE * FONT_SCALE) gui_label(title_rect, "Update Required", TITLE_FONT_SIZE, font_weight=FontWeight.BOLD) # Description desc_text = ("An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. " + "The download size is approximately 1GB.") - desc_rect = rl.Rectangle(MARGIN + 50, 250 + TITLE_FONT_SIZE + 75, rect.width - MARGIN * 2 - 100, BODY_FONT_SIZE * 3) + desc_rect = rl.Rectangle(MARGIN + 50, 250 + TITLE_FONT_SIZE * FONT_SCALE + 75, rect.width - MARGIN * 2 - 100, BODY_FONT_SIZE * FONT_SCALE * 4) gui_text_box(desc_rect, desc_text, BODY_FONT_SIZE) # Buttons at the bottom diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 65b579209e..d995ec1b52 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -76,8 +76,8 @@ def gui_text_box( ): styles = [ (rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_COLOR_NORMAL, rl.color_to_int(color)), - (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_SIZE, font_size), - (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_LINE_SPACING, font_size), + (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_SIZE, round(font_size * FONT_SCALE)), + (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_LINE_SPACING, round(font_size * FONT_SCALE)), (rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_ALIGNMENT, alignment), (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_ALIGNMENT_VERTICAL, alignment_vertical), (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_WRAP_MODE, rl.GuiTextWrapMode.TEXT_WRAP_WORD) From 2fd4b53aaf058bb730b46f1c22d18dfe7f5c067c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 00:48:05 -0700 Subject: [PATCH 180/341] raylib: smooth path distance (#36278) * smooth max distance * junk * clean up * final * you can read * Update selfdrive/ui/onroad/model_renderer.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * true * fix --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- selfdrive/ui/onroad/model_renderer.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 3877da81c5..125053f17e 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -169,12 +169,12 @@ class ModelRenderer(Widget): # Update lane lines using raw points for i, lane_line in enumerate(self._lane_lines): lane_line.projected_points = self._map_line_to_polygon( - lane_line.raw_points, 0.025 * self._lane_line_probs[i], 0.0, max_idx + lane_line.raw_points, 0.025 * self._lane_line_probs[i], 0.0, max_idx, max_distance ) # Update road edges using raw points for road_edge in self._road_edges: - road_edge.projected_points = self._map_line_to_polygon(road_edge.raw_points, 0.025, 0.0, max_idx) + road_edge.projected_points = self._map_line_to_polygon(road_edge.raw_points, 0.025, 0.0, max_idx, max_distance) # Update path using raw points if lead and lead.status: @@ -183,7 +183,7 @@ class ModelRenderer(Widget): max_idx = self._get_path_length_idx(path_x_array, max_distance) self._path.projected_points = self._map_line_to_polygon( - self._path.raw_points, 0.9, self._path_offset_z, max_idx, allow_invert=False + self._path.raw_points, 0.9, self._path_offset_z, max_idx, max_distance, allow_invert=False ) self._update_experimental_gradient() @@ -307,11 +307,11 @@ class ModelRenderer(Widget): rl.draw_triangle_fan(lead.chevron, len(lead.chevron), rl.Color(201, 34, 49, lead.fill_alpha)) @staticmethod - def _get_path_length_idx(pos_x_array: np.ndarray, path_height: float) -> int: - """Get the index corresponding to the given path height""" + def _get_path_length_idx(pos_x_array: np.ndarray, path_distance: float) -> int: + """Get the index corresponding to the given path distance""" if len(pos_x_array) == 0: return 0 - indices = np.where(pos_x_array <= path_height)[0] + indices = np.where(pos_x_array <= path_distance)[0] return indices[-1] if indices.size > 0 else 0 def _map_to_screen(self, in_x, in_y, in_z): @@ -330,13 +330,24 @@ class ModelRenderer(Widget): return (x, y) - def _map_line_to_polygon(self, line: np.ndarray, y_off: float, z_off: float, max_idx: int, allow_invert: bool = True) -> np.ndarray: + def _map_line_to_polygon(self, line: np.ndarray, y_off: float, z_off: float, max_idx: int, max_distance: float, allow_invert: bool = True) -> np.ndarray: """Convert 3D line to 2D polygon for rendering.""" if line.shape[0] == 0: return np.empty((0, 2), dtype=np.float32) # Slice points and filter non-negative x-coordinates points = line[:max_idx + 1] + + # Interpolate around max_idx so path end is smooth (max_distance is always >= p0.x) + if 0 < max_idx < line.shape[0] - 1: + p0 = line[max_idx] + p1 = line[max_idx + 1] + x0, x1 = p0[0], p1[0] + interp_y = np.interp(max_distance, [x0, x1], [p0[1], p1[1]]) + interp_z = np.interp(max_distance, [x0, x1], [p0[2], p1[2]]) + interp_point = np.array([max_distance, interp_y, interp_z], dtype=points.dtype) + points = np.concatenate((points, interp_point[None, :]), axis=0) + points = points[points[:, 0] >= 0] if points.shape[0] == 0: return np.empty((0, 2), dtype=np.float32) From ec33519dc749290d7b9d75af46708ee66ca255b7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 01:38:00 -0700 Subject: [PATCH 181/341] raylib: revert 0 button padding (#36360) * back to 20 * here only --- system/ui/widgets/button.py | 2 +- system/ui/widgets/list_view.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index ef28df8112..141f682db0 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -86,7 +86,7 @@ class Button(Widget): button_style: ButtonStyle = ButtonStyle.NORMAL, border_radius: int = 10, text_alignment: TextAlignment = TextAlignment.CENTER, - text_padding: int = 0, + text_padding: int = 20, icon=None, multi_touch: bool = False, ): diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index aaa67472dd..1f4f69a6bf 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -95,6 +95,7 @@ class ButtonAction(ItemAction): button_style=ButtonStyle.LIST_ACTION, border_radius=BUTTON_BORDER_RADIUS, click_callback=pressed, + text_padding=0, ) self.set_enabled(enabled) @@ -174,8 +175,8 @@ class DualButtonAction(ItemAction): super().__init__(width=0, enabled=enabled) # Width 0 means use full width self.left_text, self.right_text = left_text, right_text - self.left_button = Button(left_text, click_callback=left_callback, button_style=ButtonStyle.LIST_ACTION) - self.right_button = Button(right_text, click_callback=right_callback, button_style=ButtonStyle.DANGER) + self.left_button = Button(left_text, click_callback=left_callback, button_style=ButtonStyle.LIST_ACTION, text_padding=0) + self.right_button = Button(right_text, click_callback=right_callback, button_style=ButtonStyle.DANGER, text_padding=0) def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: super().set_touch_valid_callback(touch_callback) From 530ad2925df5f29cb213a2907ae5841ac6400042 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 15 Oct 2025 17:03:49 -0700 Subject: [PATCH 182/341] ui: clean raylib even on SIGINT (#36368) * fix * keep * fix --- system/ui/lib/application.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 4a62c996c7..ff7163793d 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -2,6 +2,8 @@ import atexit import cffi import os import time +import signal +import sys import pyray as rl import threading from collections.abc import Callable @@ -154,7 +156,11 @@ class GuiApplication: self._window_close_requested = True def init_window(self, title: str, fps: int = _DEFAULT_FPS): - atexit.register(self.close) # Automatically call close() on exit + def _close(sig, frame): + self.close() + sys.exit(0) + signal.signal(signal.SIGINT, _close) + atexit.register(self.close) HARDWARE.set_display_power(True) HARDWARE.set_screen_brightness(65) From 36d77debd04587ee3c9db838cd6c33540ba8a5ac Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 19:32:13 -0700 Subject: [PATCH 183/341] raylib: remove redundant text center enum (#36372) * rm * type * fix * fix --- selfdrive/ui/layouts/onboarding.py | 8 ++++---- system/ui/setup.py | 25 +++++++++++++------------ system/ui/widgets/button.py | 6 +++--- system/ui/widgets/keyboard.py | 6 +++--- system/ui/widgets/label.py | 23 +++++++++-------------- system/ui/widgets/network.py | 6 +++--- system/ui/widgets/option_dialog.py | 4 ++-- 7 files changed, 37 insertions(+), 41 deletions(-) diff --git a/selfdrive/ui/layouts/onboarding.py b/selfdrive/ui/layouts/onboarding.py index f4ab1a05c6..a817fc53ad 100644 --- a/selfdrive/ui/layouts/onboarding.py +++ b/selfdrive/ui/layouts/onboarding.py @@ -8,7 +8,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.application import FontWeight, gui_app from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle -from openpilot.system.ui.widgets.label import Label, TextAlignment +from openpilot.system.ui.widgets.label import Label from openpilot.selfdrive.ui.ui_state import ui_state DEBUG = False @@ -107,9 +107,9 @@ class TermsPage(Widget): self._on_accept = on_accept self._on_decline = on_decline - self._title = Label("Welcome to openpilot", font_size=90, font_weight=FontWeight.BOLD, text_alignment=TextAlignment.LEFT) + self._title = Label("Welcome to openpilot", font_size=90, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._desc = Label("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing.", - font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=TextAlignment.LEFT) + font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._decline_btn = Button("Decline", click_callback=on_decline) self._accept_btn = Button("Agree", button_style=ButtonStyle.PRIMARY, click_callback=on_accept) @@ -142,7 +142,7 @@ class DeclinePage(Widget): def __init__(self, back_callback=None): super().__init__() self._text = Label("You must accept the Terms and Conditions in order to use openpilot.", - font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=TextAlignment.LEFT) + font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._back_btn = Button("Back", click_callback=back_callback) self._uninstall_btn = Button("Decline, uninstall openpilot", button_style=ButtonStyle.DANGER, click_callback=self._on_uninstall_clicked) diff --git a/system/ui/setup.py b/system/ui/setup.py index a7fa4369a9..b41e4e7cd3 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -18,7 +18,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle, ButtonRadio from openpilot.system.ui.widgets.keyboard import Keyboard -from openpilot.system.ui.widgets.label import Label, TextAlignment +from openpilot.system.ui.widgets.label import Label from openpilot.system.ui.widgets.network import WifiManagerUI, WifiManager NetworkType = log.DeviceState.NetworkType @@ -79,16 +79,17 @@ class Setup(Widget): self.warning = gui_app.texture("icons/warning.png", 150, 150) self.checkmark = gui_app.texture("icons/circled_check.png", 100, 100) - self._low_voltage_title_label = Label("WARNING: Low Voltage", TITLE_FONT_SIZE, FontWeight.MEDIUM, TextAlignment.LEFT, text_color=rl.Color(255, 89, 79, 255)) + self._low_voltage_title_label = Label("WARNING: Low Voltage", TITLE_FONT_SIZE, FontWeight.MEDIUM, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, + text_color=rl.Color(255, 89, 79, 255)) self._low_voltage_body_label = Label("Power your device in a car with a harness or proceed at your own risk.", BODY_FONT_SIZE, - text_alignment=TextAlignment.LEFT) + text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._low_voltage_continue_button = Button("Continue", self._low_voltage_continue_button_callback) self._low_voltage_poweroff_button = Button("Power Off", HARDWARE.shutdown) self._getting_started_button = Button("", self._getting_started_button_callback, button_style=ButtonStyle.PRIMARY, border_radius=0) - self._getting_started_title_label = Label("Getting Started", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) + self._getting_started_title_label = Label("Getting Started", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._getting_started_body_label = Label("Before we get on the road, let's finish installation and cover some details.", - BODY_FONT_SIZE, text_alignment=TextAlignment.LEFT) + BODY_FONT_SIZE, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._software_selection_openpilot_button = ButtonRadio("openpilot", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) self._software_selection_custom_software_button = ButtonRadio("Custom Software", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) @@ -96,25 +97,25 @@ class Setup(Widget): button_style=ButtonStyle.PRIMARY) self._software_selection_continue_button.set_enabled(False) self._software_selection_back_button = Button("Back", self._software_selection_back_button_callback) - self._software_selection_title_label = Label("Choose Software to Use", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) + self._software_selection_title_label = Label("Choose Software to Use", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._download_failed_reboot_button = Button("Reboot device", HARDWARE.reboot) self._download_failed_startover_button = Button("Start over", self._download_failed_startover_button_callback, button_style=ButtonStyle.PRIMARY) - self._download_failed_title_label = Label("Download Failed", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) - self._download_failed_url_label = Label("", 52, FontWeight.NORMAL, TextAlignment.LEFT) - self._download_failed_body_label = Label("", BODY_FONT_SIZE, text_alignment=TextAlignment.LEFT) + self._download_failed_title_label = Label("Download Failed", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._download_failed_url_label = Label("", 52, FontWeight.NORMAL, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._download_failed_body_label = Label("", BODY_FONT_SIZE, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._network_setup_back_button = Button("Back", self._network_setup_back_button_callback) self._network_setup_continue_button = Button("Waiting for internet", self._network_setup_continue_button_callback, button_style=ButtonStyle.PRIMARY) self._network_setup_continue_button.set_enabled(False) - self._network_setup_title_label = Label("Connect to Wi-Fi", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) + self._network_setup_title_label = Label("Connect to Wi-Fi", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._custom_software_warning_continue_button = Button("Scroll to continue", self._custom_software_warning_continue_button_callback, button_style=ButtonStyle.PRIMARY) self._custom_software_warning_continue_button.set_enabled(False) self._custom_software_warning_back_button = Button("Back", self._custom_software_warning_back_button_callback) - self._custom_software_warning_title_label = Label("WARNING: Custom Software", 81, FontWeight.BOLD, TextAlignment.LEFT, + self._custom_software_warning_title_label = Label("WARNING: Custom Software", 81, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_color=rl.Color(255, 89, 79, 255), text_padding=60) self._custom_software_warning_body_label = Label("Use caution when installing third-party software.\n\n" @@ -123,7 +124,7 @@ class Setup(Widget): + "⚠️ It may cause damage to your device and/or vehicle.\n\n" + "If you'd like to proceed, use https://flash.comma.ai " + "to restore your device to a factory state later.", - 68, text_alignment=TextAlignment.LEFT, text_padding=60) + 68, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=60) self._custom_software_warning_body_scroll_panel = GuiScrollPanel() self._downloading_body_label = Label("Downloading...", TITLE_FONT_SIZE, FontWeight.MEDIUM) diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 141f682db0..fa6e5ed7dc 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -5,7 +5,7 @@ import pyray as rl from openpilot.system.ui.lib.application import FontWeight, MousePos from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.label import TextAlignment, Label +from openpilot.system.ui.widgets.label import Label class ButtonStyle(IntEnum): @@ -85,7 +85,7 @@ class Button(Widget): font_weight: FontWeight = FontWeight.MEDIUM, button_style: ButtonStyle = ButtonStyle.NORMAL, border_radius: int = 10, - text_alignment: TextAlignment = TextAlignment.CENTER, + text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_CENTER, text_padding: int = 20, icon=None, multi_touch: bool = False, @@ -137,7 +137,7 @@ class ButtonRadio(Button): icon, click_callback: Callable[[], None] | None = None, font_size: int = DEFAULT_BUTTON_FONT_SIZE, - text_alignment: TextAlignment = TextAlignment.LEFT, + text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_LEFT, border_radius: int = 10, text_padding: int = 20, ): diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 85bc477bf6..69aab81bc0 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -8,7 +8,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.inputbox import InputBox -from openpilot.system.ui.widgets.label import Label, TextAlignment +from openpilot.system.ui.widgets.label import Label KEY_FONT_SIZE = 96 DOUBLE_CLICK_THRESHOLD = 0.5 # seconds @@ -62,8 +62,8 @@ class Keyboard(Widget): self._layout_name: Literal["lowercase", "uppercase", "numbers", "specials"] = "lowercase" self._caps_lock = False self._last_shift_press_time = 0 - self._title = Label("", 90, FontWeight.BOLD, TextAlignment.LEFT) - self._sub_title = Label("", 55, FontWeight.NORMAL, TextAlignment.LEFT) + self._title = Label("", 90, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._sub_title = Label("", 55, FontWeight.NORMAL, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) self._max_text_size = max_text_size self._min_text_size = min_text_size diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index d995ec1b52..a3cabcac30 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -1,6 +1,5 @@ -from enum import IntEnum from itertools import zip_longest - +from typing import Union import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_COLOR, FONT_SCALE @@ -12,10 +11,6 @@ from openpilot.system.ui.widgets import Widget ICON_PADDING = 15 -class TextAlignment(IntEnum): - LEFT = 0 - CENTER = 1 - RIGHT = 2 # TODO: This should be a Widget class def gui_label( @@ -98,10 +93,10 @@ class Label(Widget): text: str, font_size: int = DEFAULT_TEXT_SIZE, font_weight: FontWeight = FontWeight.NORMAL, - text_alignment: TextAlignment = TextAlignment.CENTER, + text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_CENTER, text_padding: int = 20, text_color: rl.Color = DEFAULT_TEXT_COLOR, - icon = None, + icon: Union[rl.Texture, None] = None, # noqa: UP007 ): super().__init__() @@ -127,7 +122,7 @@ class Label(Widget): def _update_text(self, text): self._emojis = [] self._text_size = [] - self._text = wrap_text(self._font, text, self._font_size, self._rect.width - (self._text_padding*2)) + self._text = wrap_text(self._font, text, self._font_size, self._rect.width - (self._text_padding * 2)) for t in self._text: self._emojis.append(find_emoji(t)) self._text_size.append(measure_text_cached(self._font, t, self._font_size)) @@ -140,10 +135,10 @@ class Label(Widget): if self._icon: icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 if text: - if self._text_alignment == TextAlignment.LEFT: + if self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_LEFT: icon_x = self._rect.x + self._text_padding text_pos.x = self._icon.width + ICON_PADDING - elif self._text_alignment == TextAlignment.CENTER: + elif self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_CENTER: total_width = self._icon.width + ICON_PADDING + text_size.x icon_x = self._rect.x + (self._rect.width - total_width) / 2 text_pos.x = self._icon.width + ICON_PADDING @@ -155,11 +150,11 @@ class Label(Widget): for text, text_size, emojis in zip_longest(self._text, self._text_size, self._emojis, fillvalue=[]): line_pos = rl.Vector2(text_pos.x, text_pos.y) - if self._text_alignment == TextAlignment.LEFT: + if self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_LEFT: line_pos.x += self._rect.x + self._text_padding - elif self._text_alignment == TextAlignment.CENTER: + elif self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_CENTER: line_pos.x += self._rect.x + (self._rect.width - text_size.x) // 2 - elif self._text_alignment == TextAlignment.RIGHT: + elif self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_RIGHT: line_pos.x += self._rect.x + self._rect.width - text_size.x - self._text_padding prev_index = 0 diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index a50881c8cb..3bbf2e6402 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -10,7 +10,7 @@ from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.keyboard import Keyboard -from openpilot.system.ui.widgets.label import TextAlignment, gui_label +from openpilot.system.ui.widgets.label import gui_label from openpilot.system.ui.widgets.scroller import Scroller from openpilot.system.ui.widgets.list_view import ButtonAction, ListItem, MultipleButtonAction, ToggleAction, button_item, text_item @@ -442,8 +442,8 @@ class WifiManagerUI(Widget): def _on_network_updated(self, networks: list[Network]): self._networks = networks for n in self._networks: - self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT, - button_style=ButtonStyle.TRANSPARENT_WHITE_TEXT) + self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, + text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.TRANSPARENT_WHITE_TEXT) self._networks_buttons[n.ssid].set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid()) self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI, font_size=45) diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index 604cd59fd0..813e9cca8c 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -1,7 +1,7 @@ import pyray as rl from openpilot.system.ui.lib.application import FontWeight, gui_app from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import Button, ButtonStyle, TextAlignment +from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.label import gui_label from openpilot.system.ui.widgets.scroller import Scroller @@ -25,7 +25,7 @@ class MultiOptionDialog(Widget): # Create scroller with option buttons self.option_buttons = [Button(option, click_callback=lambda opt=option: self._on_option_clicked(opt), - text_alignment=TextAlignment.LEFT, button_style=ButtonStyle.NORMAL) for option in options] + text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.NORMAL) for option in options] self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING) self.cancel_button = Button("Cancel", click_callback=lambda: gui_app.set_modal_overlay(None)) From cb612a4b9063546ac206421e2266fdbb019d1c55 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 20:13:42 -0700 Subject: [PATCH 184/341] raylib: no Label padding (#36374) * none * try this * fix * stash * remove text padding from label, but keep for button * simpler is to default to 0 * fix --- system/ui/setup.py | 23 ++++++++++++----------- system/ui/widgets/keyboard.py | 4 ++-- system/ui/widgets/label.py | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index b41e4e7cd3..e362703cd7 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -80,16 +80,16 @@ class Setup(Widget): self.checkmark = gui_app.texture("icons/circled_check.png", 100, 100) self._low_voltage_title_label = Label("WARNING: Low Voltage", TITLE_FONT_SIZE, FontWeight.MEDIUM, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, - text_color=rl.Color(255, 89, 79, 255)) + text_color=rl.Color(255, 89, 79, 255), text_padding=20) self._low_voltage_body_label = Label("Power your device in a car with a harness or proceed at your own risk.", BODY_FONT_SIZE, - text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) self._low_voltage_continue_button = Button("Continue", self._low_voltage_continue_button_callback) self._low_voltage_poweroff_button = Button("Power Off", HARDWARE.shutdown) self._getting_started_button = Button("", self._getting_started_button_callback, button_style=ButtonStyle.PRIMARY, border_radius=0) - self._getting_started_title_label = Label("Getting Started", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._getting_started_title_label = Label("Getting Started", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) self._getting_started_body_label = Label("Before we get on the road, let's finish installation and cover some details.", - BODY_FONT_SIZE, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + BODY_FONT_SIZE, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) self._software_selection_openpilot_button = ButtonRadio("openpilot", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) self._software_selection_custom_software_button = ButtonRadio("Custom Software", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) @@ -97,19 +97,20 @@ class Setup(Widget): button_style=ButtonStyle.PRIMARY) self._software_selection_continue_button.set_enabled(False) self._software_selection_back_button = Button("Back", self._software_selection_back_button_callback) - self._software_selection_title_label = Label("Choose Software to Use", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._software_selection_title_label = Label("Choose Software to Use", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, + text_padding=20) self._download_failed_reboot_button = Button("Reboot device", HARDWARE.reboot) self._download_failed_startover_button = Button("Start over", self._download_failed_startover_button_callback, button_style=ButtonStyle.PRIMARY) - self._download_failed_title_label = Label("Download Failed", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) - self._download_failed_url_label = Label("", 52, FontWeight.NORMAL, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) - self._download_failed_body_label = Label("", BODY_FONT_SIZE, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._download_failed_title_label = Label("Download Failed", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) + self._download_failed_url_label = Label("", 52, FontWeight.NORMAL, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) + self._download_failed_body_label = Label("", BODY_FONT_SIZE, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) self._network_setup_back_button = Button("Back", self._network_setup_back_button_callback) self._network_setup_continue_button = Button("Waiting for internet", self._network_setup_continue_button_callback, button_style=ButtonStyle.PRIMARY) self._network_setup_continue_button.set_enabled(False) - self._network_setup_title_label = Label("Connect to Wi-Fi", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._network_setup_title_label = Label("Connect to Wi-Fi", TITLE_FONT_SIZE, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) self._custom_software_warning_continue_button = Button("Scroll to continue", self._custom_software_warning_continue_button_callback, button_style=ButtonStyle.PRIMARY) @@ -127,7 +128,7 @@ class Setup(Widget): 68, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=60) self._custom_software_warning_body_scroll_panel = GuiScrollPanel() - self._downloading_body_label = Label("Downloading...", TITLE_FONT_SIZE, FontWeight.MEDIUM) + self._downloading_body_label = Label("Downloading...", TITLE_FONT_SIZE, FontWeight.MEDIUM, text_padding=20) try: with open("/sys/class/hwmon/hwmon1/in1_input") as f: @@ -335,7 +336,7 @@ class Setup(Widget): elif result == 0: self.state = SetupState.SOFTWARE_SELECTION - self.keyboard.reset() + self.keyboard.reset(min_text_size=1) self.keyboard.set_title("Enter URL", "for Custom Software") gui_app.set_modal_overlay(self.keyboard, callback=handle_keyboard_result) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 69aab81bc0..6663c4e0ee 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -62,8 +62,8 @@ class Keyboard(Widget): self._layout_name: Literal["lowercase", "uppercase", "numbers", "specials"] = "lowercase" self._caps_lock = False self._last_shift_press_time = 0 - self._title = Label("", 90, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) - self._sub_title = Label("", 55, FontWeight.NORMAL, rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._title = Label("", 90, FontWeight.BOLD, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) + self._sub_title = Label("", 55, FontWeight.NORMAL, rl.GuiTextAlignment.TEXT_ALIGN_LEFT, text_padding=20) self._max_text_size = max_text_size self._min_text_size = min_text_size diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index a3cabcac30..6407d17d53 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -94,7 +94,7 @@ class Label(Widget): font_size: int = DEFAULT_TEXT_SIZE, font_weight: FontWeight = FontWeight.NORMAL, text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_CENTER, - text_padding: int = 20, + text_padding: int = 0, text_color: rl.Color = DEFAULT_TEXT_COLOR, icon: Union[rl.Texture, None] = None, # noqa: UP007 ): From d9fc6c0086b577490e4548d2c4cd3fc38aa02e50 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 21:11:00 -0700 Subject: [PATCH 185/341] raylib: small Label clean up (#36377) * do * clean up * text raw is the default! --- system/ui/widgets/label.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 6407d17d53..32cc7063d2 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -107,34 +107,35 @@ class Label(Widget): self._text_padding = text_padding self._text_color = text_color self._icon = icon + + self._text = text self.set_text(text) def set_text(self, text): - self._text_raw = text - self._update_text(self._text_raw) + self._text = text + self._update_text(self._text) def set_text_color(self, color): self._text_color = color def _update_layout_rects(self): - self._update_text(self._text_raw) + self._update_text(self._text) def _update_text(self, text): self._emojis = [] self._text_size = [] - self._text = wrap_text(self._font, text, self._font_size, self._rect.width - (self._text_padding * 2)) - for t in self._text: + self._text_wrapped = wrap_text(self._font, text, self._font_size, round(self._rect.width - (self._text_padding * 2))) + for t in self._text_wrapped: self._emojis.append(find_emoji(t)) self._text_size.append(measure_text_cached(self._font, t, self._font_size)) def _render(self, _): - text = self._text[0] if self._text else None text_size = self._text_size[0] if self._text_size else rl.Vector2(0.0, 0.0) - text_pos = rl.Vector2(0, (self._rect.y + (self._rect.height - (text_size.y)) // 2)) + text_pos = rl.Vector2(self._rect.x, (self._rect.y + (self._rect.height - text_size.y) // 2)) if self._icon: icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 - if text: + if len(self._text_wrapped) > 0: if self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_LEFT: icon_x = self._rect.x + self._text_padding text_pos.x = self._icon.width + ICON_PADDING @@ -148,14 +149,14 @@ class Label(Widget): icon_x = self._rect.x + (self._rect.width - self._icon.width) / 2 rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE) - for text, text_size, emojis in zip_longest(self._text, self._text_size, self._emojis, fillvalue=[]): + for text, text_size, emojis in zip_longest(self._text_wrapped, self._text_size, self._emojis, fillvalue=[]): line_pos = rl.Vector2(text_pos.x, text_pos.y) if self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_LEFT: - line_pos.x += self._rect.x + self._text_padding + line_pos.x += self._text_padding elif self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_CENTER: - line_pos.x += self._rect.x + (self._rect.width - text_size.x) // 2 + line_pos.x += (self._rect.width - text_size.x) // 2 elif self._text_alignment == rl.GuiTextAlignment.TEXT_ALIGN_RIGHT: - line_pos.x += self._rect.x + self._rect.width - text_size.x - self._text_padding + line_pos.x += self._rect.width - text_size.x - self._text_padding prev_index = 0 for start, end, emoji in emojis: From 80a8df06430eeaf11daa3e8d9a2c8ee3e4784bc1 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 21:53:52 -0700 Subject: [PATCH 186/341] raylib: fix emoji centering with Label (#36376) * kinda works * but spacing was off, so back to big emoji * rm debug * fixed! * fixed! * fix newline in emoji pattern * fix * fix dat --- selfdrive/ui/widgets/prime.py | 2 +- selfdrive/ui/widgets/setup.py | 3 +-- system/ui/lib/emoji.py | 2 +- system/ui/lib/text_measure.py | 20 +++++++++++++++++++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/widgets/prime.py b/selfdrive/ui/widgets/prime.py index e779ead334..bbbd52a8cd 100644 --- a/selfdrive/ui/widgets/prime.py +++ b/selfdrive/ui/widgets/prime.py @@ -46,7 +46,7 @@ class PrimeWidget(Widget): features = ["Remote access", "24/7 LTE connectivity", "1 year of drive storage", "Remote snapshots"] for i, feature in enumerate(features): item_y = features_y + 80 + i * 65 - gui_label(rl.Rectangle(x, item_y, 50, 60), "✓", 50, color=rl.Color(70, 91, 234, 255)) + gui_label(rl.Rectangle(x, item_y, 100, 60), "✓", 50, color=rl.Color(70, 91, 234, 255)) gui_label(rl.Rectangle(x + 60, item_y, w - 60, 60), feature, 50) def _render_for_prime_user(self, rect: rl.Rectangle): diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index b0f122227e..46a5877355 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -64,8 +64,7 @@ class SetupWidget(Widget): spacing = 42 # Title with fire emojis - # TODO: fix Label centering with emojis - self._firehose_label.render(rl.Rectangle(x - 48, y, w, 64)) + self._firehose_label.render(rl.Rectangle(rect.x, y, rect.width, 64)) y += 64 + spacing # Description diff --git a/system/ui/lib/emoji.py b/system/ui/lib/emoji.py index 519f567d3f..dc85c0fafa 100644 --- a/system/ui/lib/emoji.py +++ b/system/ui/lib/emoji.py @@ -28,7 +28,7 @@ EMOJI_REGEX = re.compile( \u231a \ufe0f \u3030 -]+""", +]+""".replace("\n", ""), flags=re.UNICODE ) diff --git a/system/ui/lib/text_measure.py b/system/ui/lib/text_measure.py index fcb7b25ccd..b91090e051 100644 --- a/system/ui/lib/text_measure.py +++ b/system/ui/lib/text_measure.py @@ -1,5 +1,6 @@ import pyray as rl from openpilot.system.ui.lib.application import FONT_SCALE +from openpilot.system.ui.lib.emoji import find_emoji _cache: dict[int, rl.Vector2] = {} @@ -10,6 +11,23 @@ def measure_text_cached(font: rl.Font, text: str, font_size: int, spacing: int = if key in _cache: return _cache[key] - result = rl.measure_text_ex(font, text, font_size * FONT_SCALE, spacing) # noqa: TID251 + # Measure normal characters without emojis, then add standard width for each found emoji + emoji = find_emoji(text) + if emoji: + non_emoji_text = "" + last_index = 0 + for start, end, _ in emoji: + non_emoji_text += text[last_index:start] + last_index = end + else: + non_emoji_text = text + + result = rl.measure_text_ex(font, non_emoji_text, font_size * FONT_SCALE, spacing) # noqa: TID251 + if emoji: + result.x += len(emoji) * font_size * FONT_SCALE + # If just emoji assume a single line height + if result.y == 0: + result.y = font_size * FONT_SCALE + _cache[key] = result return result From b29b1964ba232f407f164212ddfe84d96395dfb7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 22:10:55 -0700 Subject: [PATCH 187/341] raylib screenshots: test onroad (#36369) * test onroad * person * onroad alert * mid and full * all * can do this * tf * tf * clean up --- .../ui/tests/test_ui/raylib_screenshots.py | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 3f3958f9d3..013287aaeb 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -19,6 +19,9 @@ from openpilot.selfdrive.test.helpers import with_processes from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.system.updated.updated import parse_release_notes +AlertSize = log.SelfdriveState.AlertSize +AlertStatus = log.SelfdriveState.AlertStatus + TEST_DIR = pathlib.Path(__file__).parent TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" @@ -126,6 +129,71 @@ def setup_experimental_mode_description(click, pm: PubMaster): click(1200, 280) # expand description for experimental mode +def setup_onroad(click, pm: PubMaster): + ds = messaging.new_message('deviceState') + ds.deviceState.started = True + + ps = messaging.new_message('pandaStates', 1) + ps.pandaStates[0].pandaType = log.PandaState.PandaType.dos + ps.pandaStates[0].ignitionLine = True + + driverState = messaging.new_message('driverStateV2') + driverState.driverStateV2.leftDriverData.faceOrientation = [0, 0, 0] + + for _ in range(5): + pm.send('deviceState', ds) + pm.send('pandaStates', ps) + pm.send('driverStateV2', driverState) + ds.clear_write_flag() + ps.clear_write_flag() + driverState.clear_write_flag() + time.sleep(0.05) + + +def setup_onroad_sidebar(click, pm: PubMaster): + setup_onroad(click, pm) + click(100, 100) # open sidebar + + +def setup_onroad_small_alert(click, pm: PubMaster): + setup_onroad(click, pm) + alert = messaging.new_message('selfdriveState') + alert.selfdriveState.alertSize = AlertSize.small + alert.selfdriveState.alertText1 = "Small Alert" + alert.selfdriveState.alertText2 = "This is a small alert" + alert.selfdriveState.alertStatus = AlertStatus.normal + for _ in range(5): + pm.send('selfdriveState', alert) + alert.clear_write_flag() + time.sleep(0.05) + + +def setup_onroad_medium_alert(click, pm: PubMaster): + setup_onroad(click, pm) + alert = messaging.new_message('selfdriveState') + alert.selfdriveState.alertSize = AlertSize.mid + alert.selfdriveState.alertText1 = "Medium Alert" + alert.selfdriveState.alertText2 = "This is a medium alert" + alert.selfdriveState.alertStatus = AlertStatus.userPrompt + for _ in range(5): + pm.send('selfdriveState', alert) + alert.clear_write_flag() + time.sleep(0.05) + + +def setup_onroad_full_alert(click, pm: PubMaster): + setup_onroad(click, pm) + alert = messaging.new_message('selfdriveState') + alert.selfdriveState.alertSize = AlertSize.full + alert.selfdriveState.alertText1 = "TAKE CONTROL IMMEDIATELY" + alert.selfdriveState.alertText2 = "Calibration Invalid: Remount Device & Recalibrate" + alert.selfdriveState.alertStatus = AlertStatus.critical + for _ in range(5): + pm.send('selfdriveState', alert) + alert.clear_write_flag() + time.sleep(0.05) + + CASES = { "homescreen": setup_homescreen, "homescreen_paired": setup_homescreen, @@ -145,6 +213,11 @@ CASES = { "offroad_alert": setup_offroad_alert, "confirmation_dialog": setup_confirmation_dialog, "experimental_mode_description": setup_experimental_mode_description, + "onroad": setup_onroad, + "onroad_sidebar": setup_onroad_sidebar, + "onroad_small_alert": setup_onroad_small_alert, + "onroad_medium_alert": setup_onroad_medium_alert, + "onroad_full_alert": setup_onroad_full_alert, } @@ -155,7 +228,7 @@ class TestUI: def setup(self): # Seed minimal offroad state - self.pm = PubMaster(["deviceState"]) + self.pm = PubMaster(["deviceState", "pandaStates", "driverStateV2", "selfdriveState"]) ds = messaging.new_message('deviceState') ds.deviceState.networkType = log.DeviceState.NetworkType.wifi for _ in range(5): From 65e1fd299e2fc085c5e3d5b32a366736df85de54 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Oct 2025 22:53:37 -0700 Subject: [PATCH 188/341] raylib: fix full size alert text (#36379) * stash so far * try this * better * fast * rename * revert * clean up * yes * hack to make it work for now * actually fix * fix --- selfdrive/ui/onroad/alert_renderer.py | 20 ++++++++----- .../ui/tests/test_ui/raylib_screenshots.py | 29 ++++++++++++++++++- system/ui/widgets/button.py | 4 +-- system/ui/widgets/label.py | 11 ++++++- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 362f49a51e..0f944bac52 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -7,7 +7,7 @@ from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.label import gui_text_box +from openpilot.system.ui.widgets.label import Label AlertSize = log.SelfdriveState.AlertSize AlertStatus = log.SelfdriveState.AlertStatus @@ -74,6 +74,12 @@ class AlertRenderer(Widget): self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL) self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD) + # font size is set dynamically + self._full_text1_label = Label("", font_size=0, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + text_alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP) + self._full_text2_label = Label("", font_size=ALERT_FONT_BIG, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + text_alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP) + def get_alert(self, sm: messaging.SubMaster) -> Alert | None: """Generate the current alert based on selfdrive state.""" ss = sm['selfdriveState'] @@ -153,16 +159,16 @@ class AlertRenderer(Widget): is_long = len(alert.text1) > 15 font_size1 = 132 if is_long else 177 - align_center = rl.GuiTextAlignment.TEXT_ALIGN_CENTER - align_top = rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP - - top_offset = 240 if is_long else 270 + top_offset = 200 if is_long or '\n' in alert.text1 else 270 title_rect = rl.Rectangle(rect.x, rect.y + top_offset, rect.width, 600) - gui_text_box(title_rect, alert.text1, font_size1, alignment=align_center, alignment_vertical=align_top, font_weight=FontWeight.BOLD) + self._full_text1_label.set_font_size(font_size1) + self._full_text1_label.set_text(alert.text1) + self._full_text1_label.render(title_rect) bottom_offset = 361 if is_long else 420 subtitle_rect = rl.Rectangle(rect.x, rect.y + rect.height - bottom_offset, rect.width, 300) - gui_text_box(subtitle_rect, alert.text2, ALERT_FONT_BIG, alignment=align_center, alignment_vertical=align_top) + self._full_text2_label.set_text(alert.text2) + self._full_text2_label.render(subtitle_rect) def _draw_centered(self, text, rect, font, font_size, center_y=True, color=rl.WHITE) -> None: text_size = measure_text_cached(font, text, font_size) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 013287aaeb..6de774648a 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -182,12 +182,37 @@ def setup_onroad_medium_alert(click, pm: PubMaster): def setup_onroad_full_alert(click, pm: PubMaster): + setup_onroad(click, pm) + alert = messaging.new_message('selfdriveState') + alert.selfdriveState.alertSize = AlertSize.full + alert.selfdriveState.alertText1 = "DISENGAGE IMMEDIATELY" + alert.selfdriveState.alertText2 = "Driver Distracted" + alert.selfdriveState.alertStatus = AlertStatus.critical + for _ in range(5): + pm.send('selfdriveState', alert) + alert.clear_write_flag() + time.sleep(0.05) + + +def setup_onroad_full_alert_multiline(click, pm: PubMaster): + setup_onroad(click, pm) + alert = messaging.new_message('selfdriveState') + alert.selfdriveState.alertSize = AlertSize.full + alert.selfdriveState.alertText1 = "Reverse\nGear" + alert.selfdriveState.alertStatus = AlertStatus.normal + for _ in range(5): + pm.send('selfdriveState', alert) + alert.clear_write_flag() + time.sleep(0.05) + + +def setup_onroad_full_alert_long_text(click, pm: PubMaster): setup_onroad(click, pm) alert = messaging.new_message('selfdriveState') alert.selfdriveState.alertSize = AlertSize.full alert.selfdriveState.alertText1 = "TAKE CONTROL IMMEDIATELY" alert.selfdriveState.alertText2 = "Calibration Invalid: Remount Device & Recalibrate" - alert.selfdriveState.alertStatus = AlertStatus.critical + alert.selfdriveState.alertStatus = AlertStatus.userPrompt for _ in range(5): pm.send('selfdriveState', alert) alert.clear_write_flag() @@ -218,6 +243,8 @@ CASES = { "onroad_small_alert": setup_onroad_small_alert, "onroad_medium_alert": setup_onroad_medium_alert, "onroad_full_alert": setup_onroad_full_alert, + "onroad_full_alert_multiline": setup_onroad_full_alert_multiline, + "onroad_full_alert_long_text": setup_onroad_full_alert_long_text, } diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index fa6e5ed7dc..15d48b4e13 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -96,8 +96,8 @@ class Button(Widget): self._border_radius = border_radius self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] - self._label = Label(text, font_size, font_weight, text_alignment, text_padding, - BUTTON_TEXT_COLOR[self._button_style], icon=icon) + self._label = Label(text, font_size, font_weight, text_alignment, text_padding=text_padding, + text_color=BUTTON_TEXT_COLOR[self._button_style], icon=icon) self._click_callback = click_callback self._multi_touch = multi_touch diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 32cc7063d2..99aed529a7 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -94,6 +94,7 @@ class Label(Widget): font_size: int = DEFAULT_TEXT_SIZE, font_weight: FontWeight = FontWeight.NORMAL, text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + text_alignment_vertical: int = rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, text_padding: int = 0, text_color: rl.Color = DEFAULT_TEXT_COLOR, icon: Union[rl.Texture, None] = None, # noqa: UP007 @@ -104,6 +105,7 @@ class Label(Widget): self._font = gui_app.font(self._font_weight) self._font_size = font_size self._text_alignment = text_alignment + self._text_alignment_vertical = text_alignment_vertical self._text_padding = text_padding self._text_color = text_color self._icon = icon @@ -118,6 +120,10 @@ class Label(Widget): def set_text_color(self, color): self._text_color = color + def set_font_size(self, size): + self._font_size = size + self._update_text(self._text) + def _update_layout_rects(self): self._update_text(self._text) @@ -131,7 +137,10 @@ class Label(Widget): def _render(self, _): text_size = self._text_size[0] if self._text_size else rl.Vector2(0.0, 0.0) - text_pos = rl.Vector2(self._rect.x, (self._rect.y + (self._rect.height - text_size.y) // 2)) + if self._text_alignment_vertical == rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE: + text_pos = rl.Vector2(self._rect.x, (self._rect.y + (self._rect.height - text_size.y) // 2)) + else: + text_pos = rl.Vector2(self._rect.x, self._rect.y) if self._icon: icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 From 783b717af8e43b8c9671168c75fecdbae73d504c Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 16 Oct 2025 00:49:05 -0700 Subject: [PATCH 189/341] AGNOS 14 (#36313) * version * updater * this order * manifest * update * prod * logic * magic * new * bump * bump * new * b * bump * prod --- .gitattributes | 3 +- launch_env.sh | 2 +- selfdrive/test/test_onroad.py | 2 +- system/hardware/tici/agnos.json | 58 ++++++++--------- system/hardware/tici/all-partitions.json | 82 ++++++++++++------------ system/hardware/tici/updater | 20 +++++- system/hardware/tici/updater_magic | 3 + system/hardware/tici/updater_weston | 3 + third_party/raylib/build.sh | 2 +- third_party/raylib/larch64/libraylib.a | 4 +- 10 files changed, 100 insertions(+), 79 deletions(-) create mode 100755 system/hardware/tici/updater_magic create mode 100755 system/hardware/tici/updater_weston diff --git a/.gitattributes b/.gitattributes index cc1605a132..50ac49dd7c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,7 +10,8 @@ *.wav filter=lfs diff=lfs merge=lfs -text selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text -system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text +system/hardware/tici/updater_weston filter=lfs diff=lfs merge=lfs -text +system/hardware/tici/updater_magic filter=lfs diff=lfs merge=lfs -text third_party/**/*.a filter=lfs diff=lfs merge=lfs -text third_party/**/*.so filter=lfs diff=lfs merge=lfs -text third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text diff --git a/launch_env.sh b/launch_env.sh index 67dd5ee795..74bcf1bae6 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="13.1" + export AGNOS_VERSION="14" fi export STAGING_ROOT="/data/safe_staging" diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 9fc76489b8..40f13e11cb 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -285,7 +285,7 @@ class TestOnroad: # check for big leaks. note that memory usage is # expected to go up while the MSGQ buffers fill up - assert np.average(mems) <= 65, "Average memory usage above 65%" + assert np.average(mems) <= 85, "Average memory usage above 85%" assert np.max(np.diff(mems)) <= 4, "Max memory increase too high" assert np.average(np.diff(mems)) <= 1, "Average memory increase too high" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index d93963cf2c..bd731f5dd5 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -1,25 +1,25 @@ [ { "name": "xbl", - "url": "https://commadist.azureedge.net/agnosupdate/xbl-effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b.img.xz", - "hash": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b", - "hash_raw": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b", + "url": "https://commadist.azureedge.net/agnosupdate/xbl-98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723.img.xz", + "hash": "98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723", + "hash_raw": "98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723", "size": 3282256, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "ed61a650bea0c56652dd0fc68465d8fc722a4e6489dc8f257630c42c6adcdc89" + "ondevice_hash": "907c705f72ebcbd3030e03da9ef4c65a3d599e056a79aa9e7c369fdff8e54dc4" }, { "name": "xbl_config", - "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c.img.xz", - "hash": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c", - "hash_raw": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c", + "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f.img.xz", + "hash": "4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f", + "hash_raw": "4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f", "size": 98124, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "b12801ffaa81e58e3cef914488d3b447e35483ba549b28c6cd9deb4814c3265f" + "ondevice_hash": "46c472f52fb97a4836d08d0e790f5c8512651f520ce004bc3bbc6a143fc7a3c2" }, { "name": "abl", @@ -34,51 +34,51 @@ }, { "name": "aop", - "url": "https://commadist.azureedge.net/agnosupdate/aop-21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9.img.xz", - "hash": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9", - "hash_raw": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9", + "url": "https://commadist.azureedge.net/agnosupdate/aop-d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7.img.xz", + "hash": "d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7", + "hash_raw": "d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7", "size": 184364, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "c1be2f4aac5b3af49b904b027faec418d05efd7bd5144eb4fdfcba602bcf2180" + "ondevice_hash": "e320da0d3f73aa09277a8be740c59f9cc605d2098b46a842c93ea2ac0ac97cb0" }, { "name": "devcfg", - "url": "https://commadist.azureedge.net/agnosupdate/devcfg-d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620.img.xz", - "hash": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620", - "hash_raw": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620", + "url": "https://commadist.azureedge.net/agnosupdate/devcfg-7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8.img.xz", + "hash": "7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8", + "hash_raw": "7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8", "size": 40336, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "17b229668b20305ff8fa3cd5f94716a3aaa1e5bf9d1c24117eff7f2f81ae719f" + "ondevice_hash": "7e9412d154036216e56c2346d24455dd45f56d6de4c9e8837597f22d59c83d93" }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-b96882012ab6cddda04f440009c798a6cff65977f984b12072e89afa592d86cb.img.xz", - "hash": "b96882012ab6cddda04f440009c798a6cff65977f984b12072e89afa592d86cb", - "hash_raw": "b96882012ab6cddda04f440009c798a6cff65977f984b12072e89afa592d86cb", - "size": 17442816, + "url": "https://commadist.azureedge.net/agnosupdate/boot-08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9.img.xz", + "hash": "08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9", + "hash_raw": "08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9", + "size": 17868800, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "8ed6c2796be5c5b29d64e6413b8e878d5bd1a3981d15216d2b5e84140cc4ea2a" + "ondevice_hash": "18fab2e1eb2e43e5c39e20ee20e0d391586de528df6dbfdab6dabcdab835ee3e" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-2b1bb223bf2100376ad5d543bfa4a483f33327b3478ec20ab36048388472c4bc.img.xz", - "hash": "325414e5c9f7516b2bf0fedb6abe6682f717897a6d84ab70d5afe91a59f244e9", - "hash_raw": "2b1bb223bf2100376ad5d543bfa4a483f33327b3478ec20ab36048388472c4bc", - "size": 4718592000, + "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img.xz", + "hash": "eafa98c2dab3bc1bd799416070d724a0fca8138e6e730d391eea6002de9f3f44", + "hash_raw": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "79f4f6d0b5b4a416f0f31261b430943a78e37c26d0e226e0ef412fe0eae3c727", + "ondevice_hash": "169c9d230fc0806cb0f30844f38c543a568b1a159e380e33e5be8fce344b96bf", "alt": { - "hash": "2b1bb223bf2100376ad5d543bfa4a483f33327b3478ec20ab36048388472c4bc", - "url": "https://commadist.azureedge.net/agnosupdate/system-2b1bb223bf2100376ad5d543bfa4a483f33327b3478ec20ab36048388472c4bc.img", - "size": 4718592000 + "hash": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img", + "size": 5368709120 } } ] \ No newline at end of file diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index ebffc01dfd..38b5ebe7c1 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -130,25 +130,25 @@ }, { "name": "xbl", - "url": "https://commadist.azureedge.net/agnosupdate/xbl-effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b.img.xz", - "hash": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b", - "hash_raw": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b", + "url": "https://commadist.azureedge.net/agnosupdate/xbl-98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723.img.xz", + "hash": "98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723", + "hash_raw": "98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723", "size": 3282256, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "ed61a650bea0c56652dd0fc68465d8fc722a4e6489dc8f257630c42c6adcdc89" + "ondevice_hash": "907c705f72ebcbd3030e03da9ef4c65a3d599e056a79aa9e7c369fdff8e54dc4" }, { "name": "xbl_config", - "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c.img.xz", - "hash": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c", - "hash_raw": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c", + "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f.img.xz", + "hash": "4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f", + "hash_raw": "4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f", "size": 98124, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "b12801ffaa81e58e3cef914488d3b447e35483ba549b28c6cd9deb4814c3265f" + "ondevice_hash": "46c472f52fb97a4836d08d0e790f5c8512651f520ce004bc3bbc6a143fc7a3c2" }, { "name": "abl", @@ -163,14 +163,14 @@ }, { "name": "aop", - "url": "https://commadist.azureedge.net/agnosupdate/aop-21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9.img.xz", - "hash": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9", - "hash_raw": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9", + "url": "https://commadist.azureedge.net/agnosupdate/aop-d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7.img.xz", + "hash": "d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7", + "hash_raw": "d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7", "size": 184364, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "c1be2f4aac5b3af49b904b027faec418d05efd7bd5144eb4fdfcba602bcf2180" + "ondevice_hash": "e320da0d3f73aa09277a8be740c59f9cc605d2098b46a842c93ea2ac0ac97cb0" }, { "name": "bluetooth", @@ -207,14 +207,14 @@ }, { "name": "devcfg", - "url": "https://commadist.azureedge.net/agnosupdate/devcfg-d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620.img.xz", - "hash": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620", - "hash_raw": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620", + "url": "https://commadist.azureedge.net/agnosupdate/devcfg-7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8.img.xz", + "hash": "7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8", + "hash_raw": "7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8", "size": 40336, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "17b229668b20305ff8fa3cd5f94716a3aaa1e5bf9d1c24117eff7f2f81ae719f" + "ondevice_hash": "7e9412d154036216e56c2346d24455dd45f56d6de4c9e8837597f22d59c83d93" }, { "name": "devinfo", @@ -339,62 +339,62 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-b96882012ab6cddda04f440009c798a6cff65977f984b12072e89afa592d86cb.img.xz", - "hash": "b96882012ab6cddda04f440009c798a6cff65977f984b12072e89afa592d86cb", - "hash_raw": "b96882012ab6cddda04f440009c798a6cff65977f984b12072e89afa592d86cb", - "size": 17442816, + "url": "https://commadist.azureedge.net/agnosupdate/boot-08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9.img.xz", + "hash": "08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9", + "hash_raw": "08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9", + "size": 17868800, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "8ed6c2796be5c5b29d64e6413b8e878d5bd1a3981d15216d2b5e84140cc4ea2a" + "ondevice_hash": "18fab2e1eb2e43e5c39e20ee20e0d391586de528df6dbfdab6dabcdab835ee3e" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-2b1bb223bf2100376ad5d543bfa4a483f33327b3478ec20ab36048388472c4bc.img.xz", - "hash": "325414e5c9f7516b2bf0fedb6abe6682f717897a6d84ab70d5afe91a59f244e9", - "hash_raw": "2b1bb223bf2100376ad5d543bfa4a483f33327b3478ec20ab36048388472c4bc", - "size": 4718592000, + "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img.xz", + "hash": "eafa98c2dab3bc1bd799416070d724a0fca8138e6e730d391eea6002de9f3f44", + "hash_raw": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "79f4f6d0b5b4a416f0f31261b430943a78e37c26d0e226e0ef412fe0eae3c727", + "ondevice_hash": "169c9d230fc0806cb0f30844f38c543a568b1a159e380e33e5be8fce344b96bf", "alt": { - "hash": "2b1bb223bf2100376ad5d543bfa4a483f33327b3478ec20ab36048388472c4bc", - "url": "https://commadist.azureedge.net/agnosupdate/system-2b1bb223bf2100376ad5d543bfa4a483f33327b3478ec20ab36048388472c4bc.img", - "size": 4718592000 + "hash": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img", + "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-b3112984d2a8534a83d2ce43d35efdd10c7d163d9699f611f0f72ad9e9cb5af9.img.xz", - "hash": "bea163e6fb6ac6224c7f32619affb5afb834cd859971b0cab6d8297dd0098f0a", - "hash_raw": "b3112984d2a8534a83d2ce43d35efdd10c7d163d9699f611f0f72ad9e9cb5af9", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-dc81085a13dd7c19d94f10e53c6b5aefdefa83bb26a544674b3e768f09604da5.img.xz", + "hash": "085426cdb3a2ee8139817ffb622a3e25f0b95dd1b29faaa0ba99db8e27d4b7ed", + "hash_raw": "dc81085a13dd7c19d94f10e53c6b5aefdefa83bb26a544674b3e768f09604da5", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "f4841c6ae3207197886e5efbd50f44cc24822680d7b785fa2d2743c657f23287" + "ondevice_hash": "10e3b8d0f9a9eb1744368d31015b4397456e18a5fd94584a83669afed00bba92" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-3e63f670e4270474cec96f4da9250ee4e87e3106b0b043b7e82371e1c761e167.img.xz", - "hash": "b5458a29dd7d4a4c9b7ad77b8baa5f804142ac78d97c6668839bf2a650e32518", - "hash_raw": "3e63f670e4270474cec96f4da9250ee4e87e3106b0b043b7e82371e1c761e167", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-208d12e657cd343134df03ff1af2e2b4526ac03d1f7657bd53bfa76370681251.img.xz", + "hash": "796bd992e35f88cd7765a4e0273069b8cb8b2ba52be323a84716602a4443197b", + "hash_raw": "208d12e657cd343134df03ff1af2e2b4526ac03d1f7657bd53bfa76370681251", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "1dc10c542d3b019258fc08dc7dfdb49d9abad065e46d030b89bc1a2e0197f526" + "ondevice_hash": "c647752c040e56dc8d7b1cc23c1a40fa6e30ac5edc93fc4eed10704de1d2cbc7" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-1d3885d4370974e55f0c6f567fd0344fc5ee10db067aa5810fbaf402eadb032c.img.xz", - "hash": "687d178cfc91be5d7e8aa1333405b610fdce01775b8333bd0985b81642b94eea", - "hash_raw": "1d3885d4370974e55f0c6f567fd0344fc5ee10db067aa5810fbaf402eadb032c", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-3a4b4129a2d8ca6ceec090eeee73aaffd24c4f2160b280931e967bb83b1e811d.img.xz", + "hash": "e12f2bdd4365dbe4a84a71a6bc311ef995b47c2f7bff4b1691595466da2e017a", + "hash_raw": "3a4b4129a2d8ca6ceec090eeee73aaffd24c4f2160b280931e967bb83b1e811d", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "9ddbd1dae6ee7dc919f018364cf2f29dad138c9203c5a49aea0cbb9bf2e137e5" + "ondevice_hash": "4f2996a1ae3512e8e803f40028a18344877806f73d4ffd4ae29f66d96055671e" } ] \ No newline at end of file diff --git a/system/hardware/tici/updater b/system/hardware/tici/updater index 23cdc140f4..69ce323a10 100755 --- a/system/hardware/tici/updater +++ b/system/hardware/tici/updater @@ -1,3 +1,17 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eba5f44e6a763e1f74d1c718993218adcc72cba4caafe99b595fa701151a4c54 -size 10448792 +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" + +AGNOS_PY=$1 +MANIFEST=$2 + +if [[ ! -f "$AGNOS_PY" || ! -f "$MANIFEST" ]]; then + echo "invalid args" + exit 1 +fi + +if systemctl is-active --quiet weston-ready; then + $DIR/updater_weston $AGNOS_PY $MANIFEST +else + $DIR/updater_magic $AGNOS_PY $MANIFEST +fi diff --git a/system/hardware/tici/updater_magic b/system/hardware/tici/updater_magic new file mode 100755 index 0000000000..2487973507 --- /dev/null +++ b/system/hardware/tici/updater_magic @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffc9893e48b10096062f5fd1a14016addf7adb969f20f31ff26e68579992283c +size 20780744 diff --git a/system/hardware/tici/updater_weston b/system/hardware/tici/updater_weston new file mode 100755 index 0000000000..23cdc140f4 --- /dev/null +++ b/system/hardware/tici/updater_weston @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eba5f44e6a763e1f74d1c718993218adcc72cba4caafe99b595fa701151a4c54 +size 10448792 diff --git a/third_party/raylib/build.sh b/third_party/raylib/build.sh index f786213da7..870f4d5975 100755 --- a/third_party/raylib/build.sh +++ b/third_party/raylib/build.sh @@ -30,7 +30,7 @@ fi cd raylib_repo -COMMIT=${1:-63ea627abb4488adc9c7b5e2f8dcc77a7d6bfa3b} +COMMIT=${1:-4ee3b8ce4ee5a1604a7c055a417178c65fb795ef} git fetch origin $COMMIT git reset --hard $COMMIT git clean -xdff . diff --git a/third_party/raylib/larch64/libraylib.a b/third_party/raylib/larch64/libraylib.a index e1c5c19307..a1c5e903e1 100644 --- a/third_party/raylib/larch64/libraylib.a +++ b/third_party/raylib/larch64/libraylib.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c3125236db11e7bebcc6ad5868444ed0605c6343f98b212d39267c092b3b481 -size 3140628 +oid sha256:a73b6b91500a37d08bc2a3ee26951175ca0f95a7d0fa0ec97c823dcdf5a660dc +size 3155684 From e1ad4daf8d93c57b59b266d38157842fabd17032 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 16 Oct 2025 01:59:23 -0700 Subject: [PATCH 190/341] installer: branch migration (#36315) * mig * fix * fix * more * staging --- selfdrive/ui/installer/installer.cc | 34 ++++++++++++++++++++++++----- system/ui/setup.py | 4 +++- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/selfdrive/ui/installer/installer.cc b/selfdrive/ui/installer/installer.cc index a9b84b5c06..5cb0a38e0b 100644 --- a/selfdrive/ui/installer/installer.cc +++ b/selfdrive/ui/installer/installer.cc @@ -5,6 +5,7 @@ #include "common/swaglog.h" #include "common/util.h" +#include "system/hardware/hw.h" #include "third_party/raylib/include/raylib.h" int freshClone(); @@ -38,6 +39,27 @@ extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_a Font font; +std::vector tici_prebuilt_branches = {"release3", "release-tizi", "release3-staging", "nightly", "nightly-dev"}; +std::string migrated_branch; + +void branchMigration() { + migrated_branch = BRANCH_STR; + cereal::InitData::DeviceType device_type = Hardware::get_device_type(); + if (device_type == cereal::InitData::DeviceType::TICI) { + if (std::find(tici_prebuilt_branches.begin(), tici_prebuilt_branches.end(), BRANCH_STR) != tici_prebuilt_branches.end()) { + migrated_branch = "release-tici"; + } else if (BRANCH_STR == "master") { + migrated_branch = "master-tici"; + } + } else if (device_type == cereal::InitData::DeviceType::TIZI) { + if (BRANCH_STR == "release3") { + migrated_branch = "release-tizi"; + } else if (BRANCH_STR == "release3-staging") { + migrated_branch = "release-tizi-staging"; + } + } +} + void run(const char* cmd) { int err = std::system(cmd); assert(err == 0); @@ -87,7 +109,7 @@ int doInstall() { int freshClone() { LOGD("Doing fresh clone"); std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1", - GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH); + GIT_URL.c_str(), migrated_branch.c_str(), TMP_INSTALL_PATH); return executeGitCommand(cmd); } @@ -95,11 +117,11 @@ int cachedFetch(const std::string &cache) { LOGD("Fetching with cache: %s", cache.c_str()); run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str()); - run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str()); + run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, migrated_branch.c_str()).c_str()); renderProgress(10); - return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str())); + return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, migrated_branch.c_str())); } int executeGitCommand(const std::string &cmd) { @@ -142,8 +164,8 @@ void cloneFinished(int exitCode) { // ensure correct branch is checked out int err = chdir(TMP_INSTALL_PATH); assert(err == 0); - run(("git checkout " + BRANCH_STR).c_str()); - run(("git reset --hard origin/" + BRANCH_STR).c_str()); + run(("git checkout " + migrated_branch).c_str()); + run(("git reset --hard origin/" + migrated_branch).c_str()); run("git submodule update --init"); // move into place @@ -193,6 +215,8 @@ int main(int argc, char *argv[]) { font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, FONT_SIZE, NULL, 0); SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); + branchMigration(); + if (util::file_exists(CONTINUE_PATH)) { finishInstall(); } else { diff --git a/system/ui/setup.py b/system/ui/setup.py index e362703cd7..5dbf597484 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -376,7 +376,9 @@ class Setup(Widget): fd, tmpfile = tempfile.mkstemp(prefix="installer_") - headers = {"User-Agent": USER_AGENT, "X-openpilot-serial": HARDWARE.get_serial()} + headers = {"User-Agent": USER_AGENT, + "X-openpilot-serial": HARDWARE.get_serial(), + "X-openpilot-device-type": HARDWARE.get_device_type()} req = urllib.request.Request(self.download_url, headers=headers) with open(tmpfile, 'wb') as f, urllib.request.urlopen(req, timeout=30) as response: From 845f6ec8cfe6ae7541ef024fb49bd32ce633d572 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 16 Oct 2025 02:06:09 -0700 Subject: [PATCH 191/341] build new staging branch --- Jenkinsfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ad8e85136b..73fa74c1cd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -167,7 +167,7 @@ node { env.GIT_COMMIT = checkout(scm).GIT_COMMIT def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging', - 'release-tici', 'release-tizi', 'testing-closet*', 'hotfix-*'] + 'release-tici', 'release-tizi', 'release-tizi-staging', 'testing-closet*', 'hotfix-*'] def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*') if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) { @@ -178,8 +178,8 @@ node { try { if (env.BRANCH_NAME == 'devel-staging') { - deviceStage("build release3-staging", "tizi-needs-can", [], [ - step("build release3-staging", "RELEASE_BRANCH=release3-staging $SOURCE_DIR/release/build_release.sh"), + deviceStage("build release-tizi-staging", "tizi-needs-can", [], [ + step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging $SOURCE_DIR/release/build_release.sh"), ]) } From 25da8e9d44ee8422d00a56631ef3f9a6bcc7411d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 16 Oct 2025 02:22:02 -0700 Subject: [PATCH 192/341] raylib: fix crash from too many colors (#36382) * fix * bump --- selfdrive/ui/onroad/model_renderer.py | 8 ++++++-- system/ui/lib/shader_polygon.py | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 125053f17e..1e4fbbb783 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -226,8 +226,12 @@ class ModelRenderer(Widget): i += 1 + (1 if (i + 2) < max_len else 0) # Store the gradient in the path object - self._exp_gradient.colors = segment_colors - self._exp_gradient.stops = gradient_stops + self._exp_gradient = Gradient( + start=(0.0, 1.0), # Bottom of path + end=(0.0, 0.0), # Top of path + colors=segment_colors, + stops=gradient_stops, + ) def _update_lead_vehicle(self, d_rel, v_rel, point, rect): speed_buff, lead_buff = 10.0, 40.0 diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index 44c26c80e5..28585b08ba 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import Any, Optional, cast from openpilot.system.ui.lib.application import gui_app -MAX_GRADIENT_COLORS = 15 # includes stops as well +MAX_GRADIENT_COLORS = 20 # includes stops as well @dataclass @@ -48,8 +48,8 @@ uniform vec4 fillColor; uniform int useGradient; uniform vec2 gradientStart; // e.g. vec2(0, 0) uniform vec2 gradientEnd; // e.g. vec2(0, screenHeight) -uniform vec4 gradientColors[15]; -uniform float gradientStops[15]; +uniform vec4 gradientColors[20]; +uniform float gradientStops[20]; uniform int gradientColorCount; vec4 getGradientColor(vec2 p) { From 702bebf176dbfd5803c53cdae306eb4545b74d95 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 16 Oct 2025 02:22:49 -0700 Subject: [PATCH 193/341] raylib: fix temporarily untoggleable onroad experimental mode button (#36383) * gpt got it after 2 tries, but still not immed mergeable * bad bot --- selfdrive/ui/onroad/exp_button.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/onroad/exp_button.py b/selfdrive/ui/onroad/exp_button.py index 175233c5ba..e5d8171413 100644 --- a/selfdrive/ui/onroad/exp_button.py +++ b/selfdrive/ui/onroad/exp_button.py @@ -66,8 +66,5 @@ class ExpButton(Widget): if not self._params.get_bool("ExperimentalModeConfirmed"): return False - car_params = ui_state.sm["carParams"] - if car_params.alphaLongitudinalAvailable: - return self._params.get_bool("AlphaLongitudinalEnabled") - else: - return car_params.openpilotLongitudinalControl + # Mirror exp mode toggle using persistent car params + return ui_state.has_longitudinal_control From d71d2bd2d0a69977d71e58ea2b2ad589ae3d9f89 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 16 Oct 2025 03:45:50 -0700 Subject: [PATCH 194/341] test_onroad: ignore first few ui timing frames (#36385) clean up --- selfdrive/test/test_onroad.py | 3 ++- selfdrive/ui/onroad/cameraview.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 40f13e11cb..00abc525a0 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -206,7 +206,8 @@ class TestOnroad: result += "-------------- UI Draw Timing ------------------\n" result += "------------------------------------------------\n" - ts = self.ts['uiDebug']['drawTimeMillis'] + # skip first few frames -- connecting to vipc + ts = self.ts['uiDebug']['drawTimeMillis'][10:] result += f"min {min(ts):.2f}ms\n" result += f"max {max(ts):.2f}ms\n" result += f"std {np.std(ts):.2f}ms\n" diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index 744fdbf135..5098b6a06c 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -68,6 +68,7 @@ else: class CameraView(Widget): def __init__(self, name: str, stream_type: VisionStreamType): super().__init__() + # TODO: implement a receiver and connect thread self._name = name # Primary stream self.client = VisionIpcClient(name, stream_type, conflate=True) From 64f3759fd0717d9e6d53878aeb30fa91452e2b42 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 16 Oct 2025 15:07:45 -0700 Subject: [PATCH 195/341] cleanup release branches --- system/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/version.py b/system/version.py index 9c5a8348f9..f59509715f 100755 --- a/system/version.py +++ b/system/version.py @@ -10,8 +10,8 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.swaglog import cloudlog from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date -RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'release-tizi', 'nightly'] -TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] +RELEASE_BRANCHES = ['release-tizi-staging', 'release-tici', 'release-tizi', 'nightly'] +TESTED_BRANCHES = RELEASE_BRANCHES + ['devel-staging', 'nightly-dev'] BUILD_METADATA_FILENAME = "build.json" From ef988aca280f312d55f866e7e8b2aa8e57b673dd Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 16 Oct 2025 23:23:39 -0700 Subject: [PATCH 196/341] raylib: bump version --- third_party/raylib/build.sh | 2 +- third_party/raylib/larch64/libraylib.a | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/third_party/raylib/build.sh b/third_party/raylib/build.sh index 870f4d5975..0b50244482 100755 --- a/third_party/raylib/build.sh +++ b/third_party/raylib/build.sh @@ -30,7 +30,7 @@ fi cd raylib_repo -COMMIT=${1:-4ee3b8ce4ee5a1604a7c055a417178c65fb795ef} +COMMIT=${1:-aa6ade09ac4bfb2847a356535f2d9f87e49ab089} git fetch origin $COMMIT git reset --hard $COMMIT git clean -xdff . diff --git a/third_party/raylib/larch64/libraylib.a b/third_party/raylib/larch64/libraylib.a index a1c5e903e1..954aa0d486 100644 --- a/third_party/raylib/larch64/libraylib.a +++ b/third_party/raylib/larch64/libraylib.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a73b6b91500a37d08bc2a3ee26951175ca0f95a7d0fa0ec97c823dcdf5a660dc -size 3155684 +oid sha256:8bb734fb8733e1762081945f4f3ddf8d3f7379a0ea4790ee925803cd00129ccb +size 3156548 From 5dabb678ce5860fb0e7cf1041f09712308ccf1c6 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 16 Oct 2025 23:35:31 -0700 Subject: [PATCH 197/341] ci: just stop power_monitor on devices --- selfdrive/test/setup_device_ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/test/setup_device_ci.sh b/selfdrive/test/setup_device_ci.sh index 98909bfb52..d56f11bcc2 100755 --- a/selfdrive/test/setup_device_ci.sh +++ b/selfdrive/test/setup_device_ci.sh @@ -54,7 +54,7 @@ while true; do # /data/ciui.py & #fi - awk '{print \$1}' /proc/uptime > /var/tmp/power_watchdog + sudo systemctl stop power_monitor sleep 5s done From 727a750b3411773eb0873ecb5554a057af852f5f Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 16 Oct 2025 23:37:44 -0700 Subject: [PATCH 198/341] ci: stop power_monitor once --- selfdrive/test/setup_device_ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/test/setup_device_ci.sh b/selfdrive/test/setup_device_ci.sh index d56f11bcc2..2a1442a20c 100755 --- a/selfdrive/test/setup_device_ci.sh +++ b/selfdrive/test/setup_device_ci.sh @@ -42,6 +42,7 @@ sudo systemctl restart NetworkManager sudo systemctl disable ssh-param-watcher.path sudo systemctl disable ssh-param-watcher.service sudo mount -o ro,remount / +sudo systemctl stop power_monitor while true; do if ! sudo systemctl is-active -q ssh; then @@ -54,7 +55,6 @@ while true; do # /data/ciui.py & #fi - sudo systemctl stop power_monitor sleep 5s done From 92cd656c68432830e7e49348c613d496af4c9d5d Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 17 Oct 2025 00:26:29 -0700 Subject: [PATCH 199/341] ui: remove watchdog (#36388) out --- selfdrive/ui/ui.py | 3 --- system/manager/process_config.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index 5fb6fd5737..3eb72ec104 100755 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import pyray as rl -from openpilot.common.watchdog import kick_watchdog from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.main import MainLayout from openpilot.selfdrive.ui.ui_state import ui_state @@ -16,8 +15,6 @@ def main(): for showing_dialog in gui_app.render(): ui_state.update() - kick_watchdog() - if not showing_dialog: main_layout.render() diff --git a/system/manager/process_config.py b/system/manager/process_config.py index b8a1e09889..0c35a3d3c9 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -81,7 +81,7 @@ procs = [ PythonProcess("sensord", "system.sensord.sensord", only_onroad, enabled=not PC), # NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), - PythonProcess("ui", "selfdrive.ui.ui", always_run, watchdog_max_dt=(5 if not PC else None)), + PythonProcess("ui", "selfdrive.ui.ui", always_run), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False), From 13d98fd2d589b050a3be0a6f7d11e411f49c449e Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 17 Oct 2025 00:36:15 -0700 Subject: [PATCH 200/341] test_onroad: skip more frames for ui timings --- 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 00abc525a0..0c3bdea4c8 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -207,7 +207,7 @@ class TestOnroad: result += "------------------------------------------------\n" # skip first few frames -- connecting to vipc - ts = self.ts['uiDebug']['drawTimeMillis'][10:] + ts = self.ts['uiDebug']['drawTimeMillis'][15:] result += f"min {min(ts):.2f}ms\n" result += f"max {max(ts):.2f}ms\n" result += f"std {np.std(ts):.2f}ms\n" From 821e4da2c78ff25406c3b40fcf1b0b8867d241b9 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 17 Oct 2025 01:12:55 -0700 Subject: [PATCH 201/341] AGNOS 14.1 (#36389) * stag * prod --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++---- system/hardware/tici/all-partitions.json | 36 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 74bcf1bae6..5c14af6dcb 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="14" + export AGNOS_VERSION="14.1" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index bd731f5dd5..25cbe9f321 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img.xz", - "hash": "eafa98c2dab3bc1bd799416070d724a0fca8138e6e730d391eea6002de9f3f44", - "hash_raw": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "url": "https://commadist.azureedge.net/agnosupdate/system-26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3.img.xz", + "hash": "3f65337c978b659408567df57a4787241ffa6ce7d6dff857b9e29c22d010a931", + "hash_raw": "26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "169c9d230fc0806cb0f30844f38c543a568b1a159e380e33e5be8fce344b96bf", + "ondevice_hash": "91fe31b15ec64a5271093b9ae39dc9d59225ed4f9eb35d3c9dd099f293fb5f0d", "alt": { - "hash": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", - "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img", + "hash": "26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3", + "url": "https://commadist.azureedge.net/agnosupdate/system-26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 38b5ebe7c1..298571f31c 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img.xz", - "hash": "eafa98c2dab3bc1bd799416070d724a0fca8138e6e730d391eea6002de9f3f44", - "hash_raw": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "url": "https://commadist.azureedge.net/agnosupdate/system-26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3.img.xz", + "hash": "3f65337c978b659408567df57a4787241ffa6ce7d6dff857b9e29c22d010a931", + "hash_raw": "26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "169c9d230fc0806cb0f30844f38c543a568b1a159e380e33e5be8fce344b96bf", + "ondevice_hash": "91fe31b15ec64a5271093b9ae39dc9d59225ed4f9eb35d3c9dd099f293fb5f0d", "alt": { - "hash": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", - "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img", + "hash": "26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3", + "url": "https://commadist.azureedge.net/agnosupdate/system-26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-dc81085a13dd7c19d94f10e53c6b5aefdefa83bb26a544674b3e768f09604da5.img.xz", - "hash": "085426cdb3a2ee8139817ffb622a3e25f0b95dd1b29faaa0ba99db8e27d4b7ed", - "hash_raw": "dc81085a13dd7c19d94f10e53c6b5aefdefa83bb26a544674b3e768f09604da5", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-a154dec5ebad07f63ebef989a1f7e44c449b9fb94b1048157d426ff0e78feef8.img.xz", + "hash": "32ef650ba25cbf867eb4699096e33027aa0ab79e05de2d1dfee3601b00b4fdf6", + "hash_raw": "a154dec5ebad07f63ebef989a1f7e44c449b9fb94b1048157d426ff0e78feef8", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "10e3b8d0f9a9eb1744368d31015b4397456e18a5fd94584a83669afed00bba92" + "ondevice_hash": "9f21158f9055983c237d47a8eea8e27e978b5f25383756a7a9363a7bd9f7f72e" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-208d12e657cd343134df03ff1af2e2b4526ac03d1f7657bd53bfa76370681251.img.xz", - "hash": "796bd992e35f88cd7765a4e0273069b8cb8b2ba52be323a84716602a4443197b", - "hash_raw": "208d12e657cd343134df03ff1af2e2b4526ac03d1f7657bd53bfa76370681251", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-31ebdff72d44d3f60bdf0920e39171795494c275b8cff023cf23ec592af7a4b3.img.xz", + "hash": "a62837b235be14b257baf05ddc6bddd026c8859bbb4f154d0323c7efa58cb938", + "hash_raw": "31ebdff72d44d3f60bdf0920e39171795494c275b8cff023cf23ec592af7a4b3", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "c647752c040e56dc8d7b1cc23c1a40fa6e30ac5edc93fc4eed10704de1d2cbc7" + "ondevice_hash": "a5caa169c840de6d1804b4186a1d26486be95e1837c4df16ec45952665356942" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-3a4b4129a2d8ca6ceec090eeee73aaffd24c4f2160b280931e967bb83b1e811d.img.xz", - "hash": "e12f2bdd4365dbe4a84a71a6bc311ef995b47c2f7bff4b1691595466da2e017a", - "hash_raw": "3a4b4129a2d8ca6ceec090eeee73aaffd24c4f2160b280931e967bb83b1e811d", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-16518389a1ed7ad6277dbab75d18aa13833fb4ed4010f456438f2c2ac8c61140.img.xz", + "hash": "cb8c2fc2ae83cacb86af4ce96c6d61e4bd3cd2591e612e12878c27fa51030ffa", + "hash_raw": "16518389a1ed7ad6277dbab75d18aa13833fb4ed4010f456438f2c2ac8c61140", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "4f2996a1ae3512e8e803f40028a18344877806f73d4ffd4ae29f66d96055671e" + "ondevice_hash": "5dd8e1f87a3f985ece80f7a36da1cbdabd77bcc11d26fc7bb85540069eff8ead" } ] \ No newline at end of file From 18e8f648c2310327dc422b77d30ddde2f4e2a944 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 17 Oct 2025 01:48:08 -0700 Subject: [PATCH 202/341] Revert "AGNOS 14.1 (#36389)" This reverts commit 821e4da2c78ff25406c3b40fcf1b0b8867d241b9. --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++---- system/hardware/tici/all-partitions.json | 36 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 5c14af6dcb..74bcf1bae6 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="14.1" + export AGNOS_VERSION="14" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 25cbe9f321..bd731f5dd5 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3.img.xz", - "hash": "3f65337c978b659408567df57a4787241ffa6ce7d6dff857b9e29c22d010a931", - "hash_raw": "26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3", + "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img.xz", + "hash": "eafa98c2dab3bc1bd799416070d724a0fca8138e6e730d391eea6002de9f3f44", + "hash_raw": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "91fe31b15ec64a5271093b9ae39dc9d59225ed4f9eb35d3c9dd099f293fb5f0d", + "ondevice_hash": "169c9d230fc0806cb0f30844f38c543a568b1a159e380e33e5be8fce344b96bf", "alt": { - "hash": "26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3", - "url": "https://commadist.azureedge.net/agnosupdate/system-26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3.img", + "hash": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 298571f31c..38b5ebe7c1 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3.img.xz", - "hash": "3f65337c978b659408567df57a4787241ffa6ce7d6dff857b9e29c22d010a931", - "hash_raw": "26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3", + "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img.xz", + "hash": "eafa98c2dab3bc1bd799416070d724a0fca8138e6e730d391eea6002de9f3f44", + "hash_raw": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "91fe31b15ec64a5271093b9ae39dc9d59225ed4f9eb35d3c9dd099f293fb5f0d", + "ondevice_hash": "169c9d230fc0806cb0f30844f38c543a568b1a159e380e33e5be8fce344b96bf", "alt": { - "hash": "26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3", - "url": "https://commadist.azureedge.net/agnosupdate/system-26ddd23c2beea0382b784fdcf2fa86fd2ce1eff42828922a7e0b9d3f8ff77fa3.img", + "hash": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-a154dec5ebad07f63ebef989a1f7e44c449b9fb94b1048157d426ff0e78feef8.img.xz", - "hash": "32ef650ba25cbf867eb4699096e33027aa0ab79e05de2d1dfee3601b00b4fdf6", - "hash_raw": "a154dec5ebad07f63ebef989a1f7e44c449b9fb94b1048157d426ff0e78feef8", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-dc81085a13dd7c19d94f10e53c6b5aefdefa83bb26a544674b3e768f09604da5.img.xz", + "hash": "085426cdb3a2ee8139817ffb622a3e25f0b95dd1b29faaa0ba99db8e27d4b7ed", + "hash_raw": "dc81085a13dd7c19d94f10e53c6b5aefdefa83bb26a544674b3e768f09604da5", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "9f21158f9055983c237d47a8eea8e27e978b5f25383756a7a9363a7bd9f7f72e" + "ondevice_hash": "10e3b8d0f9a9eb1744368d31015b4397456e18a5fd94584a83669afed00bba92" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-31ebdff72d44d3f60bdf0920e39171795494c275b8cff023cf23ec592af7a4b3.img.xz", - "hash": "a62837b235be14b257baf05ddc6bddd026c8859bbb4f154d0323c7efa58cb938", - "hash_raw": "31ebdff72d44d3f60bdf0920e39171795494c275b8cff023cf23ec592af7a4b3", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-208d12e657cd343134df03ff1af2e2b4526ac03d1f7657bd53bfa76370681251.img.xz", + "hash": "796bd992e35f88cd7765a4e0273069b8cb8b2ba52be323a84716602a4443197b", + "hash_raw": "208d12e657cd343134df03ff1af2e2b4526ac03d1f7657bd53bfa76370681251", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "a5caa169c840de6d1804b4186a1d26486be95e1837c4df16ec45952665356942" + "ondevice_hash": "c647752c040e56dc8d7b1cc23c1a40fa6e30ac5edc93fc4eed10704de1d2cbc7" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-16518389a1ed7ad6277dbab75d18aa13833fb4ed4010f456438f2c2ac8c61140.img.xz", - "hash": "cb8c2fc2ae83cacb86af4ce96c6d61e4bd3cd2591e612e12878c27fa51030ffa", - "hash_raw": "16518389a1ed7ad6277dbab75d18aa13833fb4ed4010f456438f2c2ac8c61140", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-3a4b4129a2d8ca6ceec090eeee73aaffd24c4f2160b280931e967bb83b1e811d.img.xz", + "hash": "e12f2bdd4365dbe4a84a71a6bc311ef995b47c2f7bff4b1691595466da2e017a", + "hash_raw": "3a4b4129a2d8ca6ceec090eeee73aaffd24c4f2160b280931e967bb83b1e811d", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "5dd8e1f87a3f985ece80f7a36da1cbdabd77bcc11d26fc7bb85540069eff8ead" + "ondevice_hash": "4f2996a1ae3512e8e803f40028a18344877806f73d4ffd4ae29f66d96055671e" } ] \ No newline at end of file From cc683f20400cabb6c8090a7efbbe96905ab04952 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 17 Oct 2025 02:50:17 -0700 Subject: [PATCH 203/341] AGNOS 14.2 (#36390) * version * env --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++---- system/hardware/tici/all-partitions.json | 36 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 74bcf1bae6..07ec162f0b 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="14" + export AGNOS_VERSION="14.2" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index bd731f5dd5..035d8aa011 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img.xz", - "hash": "eafa98c2dab3bc1bd799416070d724a0fca8138e6e730d391eea6002de9f3f44", - "hash_raw": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "url": "https://commadist.azureedge.net/agnosupdate/system-1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b.img.xz", + "hash": "8d4b4dd80a8a537adf82faa07928066bec4568eae73bdcf4a5f0da94fb77b485", + "hash_raw": "1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "169c9d230fc0806cb0f30844f38c543a568b1a159e380e33e5be8fce344b96bf", + "ondevice_hash": "a9569b9286fba882be003f9710383ae6de229a72db936e80be08dbd2c23f320e", "alt": { - "hash": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", - "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img", + "hash": "1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b", + "url": "https://commadist.azureedge.net/agnosupdate/system-1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 38b5ebe7c1..6b99df1c38 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img.xz", - "hash": "eafa98c2dab3bc1bd799416070d724a0fca8138e6e730d391eea6002de9f3f44", - "hash_raw": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", + "url": "https://commadist.azureedge.net/agnosupdate/system-1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b.img.xz", + "hash": "8d4b4dd80a8a537adf82faa07928066bec4568eae73bdcf4a5f0da94fb77b485", + "hash_raw": "1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "169c9d230fc0806cb0f30844f38c543a568b1a159e380e33e5be8fce344b96bf", + "ondevice_hash": "a9569b9286fba882be003f9710383ae6de229a72db936e80be08dbd2c23f320e", "alt": { - "hash": "6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3", - "url": "https://commadist.azureedge.net/agnosupdate/system-6be13dfe085e03955d37fb317a1f2ee58d751eb0ef996c91bbb37c98a12a4ac3.img", + "hash": "1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b", + "url": "https://commadist.azureedge.net/agnosupdate/system-1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-dc81085a13dd7c19d94f10e53c6b5aefdefa83bb26a544674b3e768f09604da5.img.xz", - "hash": "085426cdb3a2ee8139817ffb622a3e25f0b95dd1b29faaa0ba99db8e27d4b7ed", - "hash_raw": "dc81085a13dd7c19d94f10e53c6b5aefdefa83bb26a544674b3e768f09604da5", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-a154dec5ebad07f63ebef989a1f7e44c449b9fb94b1048157d426ff0e78feef8.img.xz", + "hash": "32ef650ba25cbf867eb4699096e33027aa0ab79e05de2d1dfee3601b00b4fdf6", + "hash_raw": "a154dec5ebad07f63ebef989a1f7e44c449b9fb94b1048157d426ff0e78feef8", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "10e3b8d0f9a9eb1744368d31015b4397456e18a5fd94584a83669afed00bba92" + "ondevice_hash": "9f21158f9055983c237d47a8eea8e27e978b5f25383756a7a9363a7bd9f7f72e" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-208d12e657cd343134df03ff1af2e2b4526ac03d1f7657bd53bfa76370681251.img.xz", - "hash": "796bd992e35f88cd7765a4e0273069b8cb8b2ba52be323a84716602a4443197b", - "hash_raw": "208d12e657cd343134df03ff1af2e2b4526ac03d1f7657bd53bfa76370681251", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-31ebdff72d44d3f60bdf0920e39171795494c275b8cff023cf23ec592af7a4b3.img.xz", + "hash": "a62837b235be14b257baf05ddc6bddd026c8859bbb4f154d0323c7efa58cb938", + "hash_raw": "31ebdff72d44d3f60bdf0920e39171795494c275b8cff023cf23ec592af7a4b3", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "c647752c040e56dc8d7b1cc23c1a40fa6e30ac5edc93fc4eed10704de1d2cbc7" + "ondevice_hash": "a5caa169c840de6d1804b4186a1d26486be95e1837c4df16ec45952665356942" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-3a4b4129a2d8ca6ceec090eeee73aaffd24c4f2160b280931e967bb83b1e811d.img.xz", - "hash": "e12f2bdd4365dbe4a84a71a6bc311ef995b47c2f7bff4b1691595466da2e017a", - "hash_raw": "3a4b4129a2d8ca6ceec090eeee73aaffd24c4f2160b280931e967bb83b1e811d", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-16518389a1ed7ad6277dbab75d18aa13833fb4ed4010f456438f2c2ac8c61140.img.xz", + "hash": "cb8c2fc2ae83cacb86af4ce96c6d61e4bd3cd2591e612e12878c27fa51030ffa", + "hash_raw": "16518389a1ed7ad6277dbab75d18aa13833fb4ed4010f456438f2c2ac8c61140", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "4f2996a1ae3512e8e803f40028a18344877806f73d4ffd4ae29f66d96055671e" + "ondevice_hash": "5dd8e1f87a3f985ece80f7a36da1cbdabd77bcc11d26fc7bb85540069eff8ead" } ] \ No newline at end of file From 646f6a1006d69ba6c073f7048776fb86327c4288 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Fri, 17 Oct 2025 19:27:13 -0500 Subject: [PATCH 204/341] raylib screenshots: alpha long toggle confirmation dialog (#36386) add alpha long toggle confirmation test --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 6de774648a..2e96b3bd43 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -9,7 +9,7 @@ from collections import namedtuple import pyautogui import pywinctl -from cereal import log +from cereal import car, log from cereal import messaging from cereal.messaging import PubMaster from openpilot.common.basedir import BASEDIR @@ -95,6 +95,10 @@ def setup_settings_firehose(click, pm: PubMaster): def setup_settings_developer(click, pm: PubMaster): + CP = car.CarParams() + CP.alphaLongitudinalAvailable = True # show alpha long control toggle + Params().put("CarParamsPersistent", CP.to_bytes()) + setup_settings(click, pm) click(278, 950) @@ -129,6 +133,11 @@ def setup_experimental_mode_description(click, pm: PubMaster): click(1200, 280) # expand description for experimental mode +def setup_openpilot_long_confirmation_dialog(click, pm: PubMaster): + setup_settings_developer(click, pm) + click(2000, 960) # toggle openpilot longitudinal control + + def setup_onroad(click, pm: PubMaster): ds = messaging.new_message('deviceState') ds.deviceState.started = True @@ -238,6 +247,7 @@ CASES = { "offroad_alert": setup_offroad_alert, "confirmation_dialog": setup_confirmation_dialog, "experimental_mode_description": setup_experimental_mode_description, + "openpilot_long_confirmation_dialog": setup_openpilot_long_confirmation_dialog, "onroad": setup_onroad, "onroad_sidebar": setup_onroad_sidebar, "onroad_small_alert": setup_onroad_small_alert, From 1f5e0b6f682470e2308778d35b3995d44cc46c0e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 17 Oct 2025 19:00:06 -0700 Subject: [PATCH 205/341] raylib: show dialog when attempting to pair without internet (#36396) * match qt * clean up * bb * ofc * use alert_dialog --- selfdrive/ui/widgets/setup.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index 46a5877355..0538570e4a 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -1,9 +1,11 @@ import pyray as rl +from openpilot.common.time_helpers import system_time_valid from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.widgets.confirm_dialog import alert_dialog from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.label import Label @@ -84,6 +86,11 @@ class SetupWidget(Widget): self._open_settings_btn.render(button_rect) def _show_pairing(self): + if not system_time_valid(): + dlg = alert_dialog("Please connect to Wi-Fi to complete initial pairing") + gui_app.set_modal_overlay(dlg) + return + if not self._pairing_dialog: self._pairing_dialog = PairingDialog() gui_app.set_modal_overlay(self._pairing_dialog, lambda result: setattr(self, '_pairing_dialog', None)) From b28425b8c3261ac6e406f0f3a9b726e0a2b28c3c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 17 Oct 2025 19:11:12 -0700 Subject: [PATCH 206/341] raylib: fix broken pairing dialog first 5m after startup (#36397) * always try on dialog show * except logging * huge oof --- selfdrive/ui/widgets/pairing_dialog.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/widgets/pairing_dialog.py b/selfdrive/ui/widgets/pairing_dialog.py index 252a7dd94d..3778586f89 100644 --- a/selfdrive/ui/widgets/pairing_dialog.py +++ b/selfdrive/ui/widgets/pairing_dialog.py @@ -34,7 +34,7 @@ class PairingDialog(Widget): super().__init__() self.params = Params() self.qr_texture: rl.Texture | None = None - self.last_qr_generation = 0 + self.last_qr_generation = float('-inf') self._close_btn = IconButton(gui_app.texture("icons/close.png", 80, 80)) self._close_btn.set_click_callback(lambda: gui_app.set_modal_overlay(None)) @@ -42,8 +42,8 @@ class PairingDialog(Widget): try: dongle_id = self.params.get("DongleId") or "" token = Api(dongle_id).get_token({'pair': True}) - except Exception as e: - cloudlog.warning(f"Failed to get pairing token: {e}") + except Exception: + cloudlog.exception("Failed to get pairing token") token = "" return f"https://connect.comma.ai/?pair={token}" @@ -67,8 +67,8 @@ class PairingDialog(Widget): rl_image.format = rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 self.qr_texture = rl.load_texture_from_image(rl_image) - except Exception as e: - cloudlog.warning(f"QR code generation failed: {e}") + except Exception: + cloudlog.exception("QR code generation failed") self.qr_texture = None def _check_qr_refresh(self) -> None: From 7534b2a160faa683412c04c1254440e338931c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Sat, 18 Oct 2025 11:12:47 -0700 Subject: [PATCH 207/341] PID: no more ff gain (#36398) * No more ff gain * typo --- common/pid.py | 5 ++--- selfdrive/controls/lib/latcontrol_pid.py | 5 +++-- selfdrive/controls/lib/latcontrol_torque.py | 3 +-- selfdrive/controls/lib/longcontrol.py | 2 +- system/hardware/fan_controller.py | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/common/pid.py b/common/pid.py index ff5084781f..e3fa8afdf4 100644 --- a/common/pid.py +++ b/common/pid.py @@ -2,11 +2,10 @@ import numpy as np from numbers import Number class PIDController: - def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100): + def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100): self._k_p = k_p self._k_i = k_i self._k_d = k_d - self.k_f = k_f # feedforward gain if isinstance(self._k_p, Number): self._k_p = [[0], [self._k_p]] if isinstance(self._k_i, Number): @@ -48,7 +47,7 @@ class PIDController: self.speed = speed self.p = self.k_p * float(error) self.d = self.k_d * error_rate - self.f = self.k_f * feedforward + self.f = feedforward if not freeze_integrator: i = self.i + self.k_i * self.i_dt * error diff --git a/selfdrive/controls/lib/latcontrol_pid.py b/selfdrive/controls/lib/latcontrol_pid.py index 4cc1c47f61..14ab9f21b5 100644 --- a/selfdrive/controls/lib/latcontrol_pid.py +++ b/selfdrive/controls/lib/latcontrol_pid.py @@ -10,7 +10,8 @@ class LatControlPID(LatControl): super().__init__(CP, CI, dt) self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV), (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV), - k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max) + pos_limit=self.steer_max, neg_limit=-self.steer_max) + self.ff_factor = CP.lateralTuning.pid.kf self.get_steer_feedforward = CI.get_steer_feedforward_function() def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay): @@ -30,7 +31,7 @@ class LatControlPID(LatControl): else: # offset does not contribute to resistive torque - ff = self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo) + ff = self.ff_factor * self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo) freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 output_torque = self.pid.update(error, diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 664e866636..fbef5828e6 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -32,8 +32,7 @@ class LatControlTorque(LatControl): self.torque_params = CP.lateralTuning.torque.as_builder() self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.lateral_accel_from_torque = CI.lateral_accel_from_torque() - self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, - k_f=self.torque_params.kf, rate=1/self.dt) + self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, rate=1/self.dt) self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES = int(1 / self.dt) diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index 6d4f922461..62dbc842c5 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -50,7 +50,7 @@ class LongControl: self.long_control_state = LongCtrlState.off self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV), (CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV), - k_f=CP.longitudinalTuning.kf, rate=1 / DT_CTRL) + rate=1 / DT_CTRL) self.last_output_accel = 0.0 def reset(self): diff --git a/system/hardware/fan_controller.py b/system/hardware/fan_controller.py index 4c7adc0a3e..365688429a 100755 --- a/system/hardware/fan_controller.py +++ b/system/hardware/fan_controller.py @@ -18,7 +18,7 @@ class TiciFanController(BaseFanController): cloudlog.info("Setting up TICI fan handler") self.last_ignition = False - self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_HW)) + self.controller = PIDController(k_p=0, k_i=4e-3, rate=(1 / DT_HW)) def update(self, cur_temp: float, ignition: bool) -> int: self.controller.pos_limit = 100 if ignition else 30 From 3ef5037c1654e1d9142dff85db85a7e2a972d212 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Oct 2025 11:40:03 -0700 Subject: [PATCH 208/341] uploader: fix env var parsing --- system/loggerd/uploader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py index 38fc0e9209..bc19572507 100755 --- a/system/loggerd/uploader.py +++ b/system/loggerd/uploader.py @@ -29,7 +29,7 @@ MAX_UPLOAD_SIZES = { "qcam": 5*1e6, } -allow_sleep = bool(os.getenv("UPLOADER_SLEEP", "1")) +allow_sleep = bool(int(os.getenv("UPLOADER_SLEEP", "1"))) force_wifi = os.getenv("FORCEWIFI") is not None fake_upload = os.getenv("FAKEUPLOAD") is not None From 3c957c6e9d8f05138b8a80523d50db5b5ca2cb73 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Mon, 20 Oct 2025 14:09:42 -0700 Subject: [PATCH 209/341] =?UTF-8?q?The=20Cool=20People's=20model=20?= =?UTF-8?q?=F0=9F=98=8E=20(#36249)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * cb8f0d7e-6627-4d7f-ad97-10d0078f2d2c/400 * ci? * fd9a6816-8758-466b-bbde-3c1413b98f0a/400 --- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 89444c3ea7..1e764af9ba 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:792fca7c24faadc1183327eec7e04d06dd6ec0e53ac7bb43cb2b9823c6cfb32f -size 12343535 +oid sha256:c5a1f0655ddf266ed42ad1980389d96f47cc5e756da1fa3ca1477a920bb9b157 +size 13926324 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 0411ddb744..441c4a16af 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf6376aa9a090f0da26c280ef69eabf9bbdd51d1faac9ed392919c3db69be916 +oid sha256:8f16d548ea4eb5d01518a9e90d4527cd97c31a84bcaf6f695dead8f0015fecc4 size 46271942 From 8752093801d5c7212fe5c2cb1c728b3003a7ddce Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Oct 2025 15:45:44 -0700 Subject: [PATCH 210/341] raylib: fix option dialog (#36405) * fix dialog * rm --- system/ui/widgets/option_dialog.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index 813e9cca8c..b7e30b696b 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -1,6 +1,6 @@ import pyray as rl -from openpilot.system.ui.lib.application import FontWeight, gui_app -from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.lib.application import FontWeight +from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.label import gui_label from openpilot.system.ui.widgets.scroller import Scroller @@ -22,14 +22,19 @@ class MultiOptionDialog(Widget): self.options = options self.current = current self.selection = current + self._result: DialogResult = DialogResult.NO_ACTION # Create scroller with option buttons self.option_buttons = [Button(option, click_callback=lambda opt=option: self._on_option_clicked(opt), - text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.NORMAL) for option in options] + text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.NORMAL, + text_padding=50, elide_right=True) for option in options] self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING) - self.cancel_button = Button("Cancel", click_callback=lambda: gui_app.set_modal_overlay(None)) - self.select_button = Button("Select", click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY) + self.cancel_button = Button("Cancel", click_callback=lambda: self._set_result(DialogResult.CANCEL)) + self.select_button = Button("Select", click_callback=lambda: self._set_result(DialogResult.CONFIRM), button_style=ButtonStyle.PRIMARY) + + def _set_result(self, result: DialogResult): + self._result = result def _on_option_clicked(self, option): self.selection = option @@ -68,4 +73,4 @@ class MultiOptionDialog(Widget): self.select_button.set_enabled(self.selection != self.current) self.select_button.render(select_rect) - return -1 + return self._result From 8720e5d7128d98724ba1d2fb0bcc56577761a253 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 20 Oct 2025 16:14:02 -0700 Subject: [PATCH 211/341] tools: pass args to op adb --- tools/op.sh | 2 +- tools/scripts/adb_ssh.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/op.sh b/tools/op.sh index ae12809eb9..8b5062ad9b 100755 --- a/tools/op.sh +++ b/tools/op.sh @@ -284,7 +284,7 @@ function op_venv() { function op_adb() { op_before_cmd - op_run_command tools/scripts/adb_ssh.sh + op_run_command tools/scripts/adb_ssh.sh "$@" } function op_ssh() { diff --git a/tools/scripts/adb_ssh.sh b/tools/scripts/adb_ssh.sh index 43c8e07de6..2fe2873a3d 100755 --- a/tools/scripts/adb_ssh.sh +++ b/tools/scripts/adb_ssh.sh @@ -4,4 +4,4 @@ set -e # this is a little nicer than "adb shell" since # "adb shell" doesn't do full terminal emulation adb forward tcp:2222 tcp:22 -ssh comma@localhost -p 2222 +ssh comma@localhost -p 2222 "$@" From 01715f6f9a857c75d9eb9d049adfa5163e7beebf Mon Sep 17 00:00:00 2001 From: Bruce Wayne Date: Mon, 20 Oct 2025 16:26:22 -0700 Subject: [PATCH 212/341] test car model: use factor for torque --- selfdrive/car/tests/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 8996ad6460..94f5b33231 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -183,7 +183,7 @@ class TestCarModelBase(unittest.TestCase): if tuning == 'pid': self.assertTrue(len(self.CP.lateralTuning.pid.kpV)) elif tuning == 'torque': - self.assertTrue(self.CP.lateralTuning.torque.kf > 0) + self.assertTrue(self.CP.lateralTuning.torque.latAccelFactor > 0) else: raise Exception("unknown tuning") From b2e3dd17ea520c693976aa9db887ea24d566a18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Mon, 20 Oct 2025 17:16:03 -0700 Subject: [PATCH 213/341] torque gains not car specific (#36404) * torque gains not car specific * remove opendbc interfaces longitudinal control kf field assignment that makes hitl test fail * typo * another typo * bump * bump openbc * update ref --------- Co-authored-by: felsager --- opendbc_repo | 2 +- selfdrive/controls/lib/latcontrol_torque.py | 7 +++++-- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index dfa6807ebf..b59f8bdcca 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit dfa6807ebf9f0edb593189a703b857c834a88a75 +Subproject commit b59f8bdcca8d375b4a5a652d2f2d2ec9cd3503d3 diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index fbef5828e6..f2b1e0b669 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -24,6 +24,9 @@ from openpilot.common.pid import PIDController LOW_SPEED_X = [0, 10, 20, 30] LOW_SPEED_Y = [15, 13, 10, 5] +KP = 1.0 +KI = 0.3 +KD = 0.0 class LatControlTorque(LatControl): @@ -32,7 +35,7 @@ class LatControlTorque(LatControl): self.torque_params = CP.lateralTuning.torque.as_builder() self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.lateral_accel_from_torque = CI.lateral_accel_from_torque() - self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, rate=1/self.dt) + self.pid = PIDController(KP, KI, k_d=KD, rate=1/self.dt) self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES = int(1 / self.dt) @@ -76,7 +79,7 @@ class LatControlTorque(LatControl): low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2 setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel error = setpoint - measurement - error_lsf = error + low_speed_factor / self.torque_params.kp * error + error_lsf = error + low_speed_factor / KP * error # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly pid_log.error = float(error_lsf) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 538aa34f2a..dd73ed12f7 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -5342f5684c2498a33d6178f3b59efd5e83b73acc \ No newline at end of file +55e82ab6370865a1427ebc1d559921a5354d9cbf \ No newline at end of file From 338119229745c39a0a9d433d11a3f2057c9ef74f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Oct 2025 18:35:34 -0700 Subject: [PATCH 214/341] Multilang: remove main prefix (#36406) * rename * fix --- common/params_keys.h | 2 +- selfdrive/ui/.gitignore | 2 +- .../ui/tests/create_test_translations.sh | 4 ++-- selfdrive/ui/tests/test_runner.cc | 2 +- selfdrive/ui/tests/test_translations.py | 2 +- .../ui/translations/{main_ar.ts => ar.ts} | 0 selfdrive/ui/translations/auto_translate.py | 2 +- .../ui/translations/{main_de.ts => de.ts} | 0 .../ui/translations/{main_en.ts => en.ts} | 0 .../ui/translations/{main_es.ts => es.ts} | 0 .../ui/translations/{main_fr.ts => fr.ts} | 0 .../ui/translations/{main_ja.ts => ja.ts} | 0 .../ui/translations/{main_ko.ts => ko.ts} | 0 selfdrive/ui/translations/languages.json | 24 +++++++++---------- .../ui/translations/{main_nl.ts => nl.ts} | 0 .../ui/translations/{main_pl.ts => pl.ts} | 0 .../translations/{main_pt-BR.ts => pt-BR.ts} | 0 .../ui/translations/{main_th.ts => th.ts} | 0 .../ui/translations/{main_tr.ts => tr.ts} | 0 .../{main_zh-CHS.ts => zh-CHS.ts} | 0 .../{main_zh-CHT.ts => zh-CHT.ts} | 0 selfdrive/ui/update_translations.py | 2 +- 22 files changed, 20 insertions(+), 20 deletions(-) rename selfdrive/ui/translations/{main_ar.ts => ar.ts} (100%) rename selfdrive/ui/translations/{main_de.ts => de.ts} (100%) rename selfdrive/ui/translations/{main_en.ts => en.ts} (100%) rename selfdrive/ui/translations/{main_es.ts => es.ts} (100%) rename selfdrive/ui/translations/{main_fr.ts => fr.ts} (100%) rename selfdrive/ui/translations/{main_ja.ts => ja.ts} (100%) rename selfdrive/ui/translations/{main_ko.ts => ko.ts} (100%) rename selfdrive/ui/translations/{main_nl.ts => nl.ts} (100%) rename selfdrive/ui/translations/{main_pl.ts => pl.ts} (100%) rename selfdrive/ui/translations/{main_pt-BR.ts => pt-BR.ts} (100%) rename selfdrive/ui/translations/{main_th.ts => th.ts} (100%) rename selfdrive/ui/translations/{main_tr.ts => tr.ts} (100%) rename selfdrive/ui/translations/{main_zh-CHS.ts => zh-CHS.ts} (100%) rename selfdrive/ui/translations/{main_zh-CHT.ts => zh-CHT.ts} (100%) diff --git a/common/params_keys.h b/common/params_keys.h index 211b4d550b..f6e8d781c8 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -66,7 +66,7 @@ inline static std::unordered_map keys = { {"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}}, {"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}}, {"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}}, - {"LanguageSetting", {PERSISTENT, STRING, "main_en"}}, + {"LanguageSetting", {PERSISTENT, STRING, "en"}}, {"LastAthenaPingTime", {CLEAR_ON_MANAGER_START, INT}}, {"LastGPSPosition", {PERSISTENT, STRING}}, {"LastManagerExitReason", {CLEAR_ON_MANAGER_START, STRING}}, diff --git a/selfdrive/ui/.gitignore b/selfdrive/ui/.gitignore index 7e9eaf932f..5e7b02a1a3 100644 --- a/selfdrive/ui/.gitignore +++ b/selfdrive/ui/.gitignore @@ -1,7 +1,7 @@ moc_* *.moc -translations/main_test_en.* +translations/test_en.* ui mui diff --git a/selfdrive/ui/tests/create_test_translations.sh b/selfdrive/ui/tests/create_test_translations.sh index ed0890d946..1587a88205 100755 --- a/selfdrive/ui/tests/create_test_translations.sh +++ b/selfdrive/ui/tests/create_test_translations.sh @@ -4,8 +4,8 @@ set -e UI_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"/.. TEST_TEXT="(WRAPPED_SOURCE_TEXT)" -TEST_TS_FILE=$UI_DIR/translations/main_test_en.ts -TEST_QM_FILE=$UI_DIR/translations/main_test_en.qm +TEST_TS_FILE=$UI_DIR/translations/test_en.ts +TEST_QM_FILE=$UI_DIR/translations/test_en.qm # translation strings UNFINISHED="<\/translation>" diff --git a/selfdrive/ui/tests/test_runner.cc b/selfdrive/ui/tests/test_runner.cc index c8cc0d3e05..4bde921696 100644 --- a/selfdrive/ui/tests/test_runner.cc +++ b/selfdrive/ui/tests/test_runner.cc @@ -10,7 +10,7 @@ int main(int argc, char **argv) { // unit tests for Qt QApplication app(argc, argv); - QString language_file = "main_test_en"; + QString language_file = "test_en"; // FIXME: pytest-cpp considers this print as a test case qDebug() << "Loading language:" << language_file; diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py index 2ae3356bb8..edd9a30412 100644 --- a/selfdrive/ui/tests/test_translations.py +++ b/selfdrive/ui/tests/test_translations.py @@ -93,7 +93,7 @@ class TestTranslations: def test_bad_language(self): IGNORED_WORDS = {'pédale'} - match = re.search(r'_([a-zA-Z]{2,3})', self.file) + match = re.search(r'([a-zA-Z]{2,3})', self.file) assert match, f"{self.name} - could not parse language" try: diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/ar.ts similarity index 100% rename from selfdrive/ui/translations/main_ar.ts rename to selfdrive/ui/translations/ar.ts diff --git a/selfdrive/ui/translations/auto_translate.py b/selfdrive/ui/translations/auto_translate.py index c2e4bbc552..6251e03397 100755 --- a/selfdrive/ui/translations/auto_translate.py +++ b/selfdrive/ui/translations/auto_translate.py @@ -26,7 +26,7 @@ def get_language_files(languages: list[str] = None) -> dict[str, pathlib.Path]: for filename in language_dict.values(): path = TRANSLATIONS_DIR / f"{filename}.ts" - language = path.stem.split("main_")[1] + language = path.stem if languages is None or language in languages: files[language] = path diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/de.ts similarity index 100% rename from selfdrive/ui/translations/main_de.ts rename to selfdrive/ui/translations/de.ts diff --git a/selfdrive/ui/translations/main_en.ts b/selfdrive/ui/translations/en.ts similarity index 100% rename from selfdrive/ui/translations/main_en.ts rename to selfdrive/ui/translations/en.ts diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/es.ts similarity index 100% rename from selfdrive/ui/translations/main_es.ts rename to selfdrive/ui/translations/es.ts diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/fr.ts similarity index 100% rename from selfdrive/ui/translations/main_fr.ts rename to selfdrive/ui/translations/fr.ts diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/ja.ts similarity index 100% rename from selfdrive/ui/translations/main_ja.ts rename to selfdrive/ui/translations/ja.ts diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/ko.ts similarity index 100% rename from selfdrive/ui/translations/main_ko.ts rename to selfdrive/ui/translations/ko.ts diff --git a/selfdrive/ui/translations/languages.json b/selfdrive/ui/translations/languages.json index 132b5088d7..b0674dee82 100644 --- a/selfdrive/ui/translations/languages.json +++ b/selfdrive/ui/translations/languages.json @@ -1,14 +1,14 @@ { - "English": "main_en", - "Deutsch": "main_de", - "Français": "main_fr", - "Português": "main_pt-BR", - "Español": "main_es", - "Türkçe": "main_tr", - "العربية": "main_ar", - "ไทย": "main_th", - "中文(繁體)": "main_zh-CHT", - "中文(简体)": "main_zh-CHS", - "한국어": "main_ko", - "日本語": "main_ja" + "English": "en", + "Deutsch": "de", + "Français": "fr", + "Português": "pt-BR", + "Español": "es", + "Türkçe": "tr", + "العربية": "ar", + "ไทย": "th", + "中文(繁體)": "zh-CHT", + "中文(简体)": "zh-CHS", + "한국어": "ko", + "日本語": "ja" } diff --git a/selfdrive/ui/translations/main_nl.ts b/selfdrive/ui/translations/nl.ts similarity index 100% rename from selfdrive/ui/translations/main_nl.ts rename to selfdrive/ui/translations/nl.ts diff --git a/selfdrive/ui/translations/main_pl.ts b/selfdrive/ui/translations/pl.ts similarity index 100% rename from selfdrive/ui/translations/main_pl.ts rename to selfdrive/ui/translations/pl.ts diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/pt-BR.ts similarity index 100% rename from selfdrive/ui/translations/main_pt-BR.ts rename to selfdrive/ui/translations/pt-BR.ts diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/th.ts similarity index 100% rename from selfdrive/ui/translations/main_th.ts rename to selfdrive/ui/translations/th.ts diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/tr.ts similarity index 100% rename from selfdrive/ui/translations/main_tr.ts rename to selfdrive/ui/translations/tr.ts diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/zh-CHS.ts similarity index 100% rename from selfdrive/ui/translations/main_zh-CHS.ts rename to selfdrive/ui/translations/zh-CHS.ts diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/zh-CHT.ts similarity index 100% rename from selfdrive/ui/translations/main_zh-CHT.ts rename to selfdrive/ui/translations/zh-CHT.ts diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py index 65880bdad9..424b78851c 100755 --- a/selfdrive/ui/update_translations.py +++ b/selfdrive/ui/update_translations.py @@ -9,7 +9,7 @@ UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui") TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations") LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json") TRANSLATIONS_INCLUDE_FILE = os.path.join(TRANSLATIONS_DIR, "alerts_generated.h") -PLURAL_ONLY = ["main_en"] # base language, only create entries for strings with plural forms +PLURAL_ONLY = ["en"] # base language, only create entries for strings with plural forms def generate_translations_include(): From 9801e486d9898d0d89b6d22d9cd4686a4920e2a5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Oct 2025 18:36:13 -0700 Subject: [PATCH 215/341] fix incorrect Button argument --- system/ui/widgets/option_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index b7e30b696b..593a371fd9 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -27,7 +27,7 @@ class MultiOptionDialog(Widget): # Create scroller with option buttons self.option_buttons = [Button(option, click_callback=lambda opt=option: self._on_option_clicked(opt), text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.NORMAL, - text_padding=50, elide_right=True) for option in options] + text_padding=50) for option in options] self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING) self.cancel_button = Button("Cancel", click_callback=lambda: self._set_result(DialogResult.CANCEL)) From 650946cd2ace2f55631f13466b1906249afada59 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 21 Oct 2025 10:17:10 +0800 Subject: [PATCH 216/341] raylib:use context manager for BytesIO (#36407) use context manager for BytesIO --- system/ui/lib/emoji.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/system/ui/lib/emoji.py b/system/ui/lib/emoji.py index dc85c0fafa..54f742952e 100644 --- a/system/ui/lib/emoji.py +++ b/system/ui/lib/emoji.py @@ -41,9 +41,9 @@ def emoji_tex(emoji): draw = ImageDraw.Draw(img) font = ImageFont.truetype(FONT_DIR.joinpath("NotoColorEmoji.ttf"), 109) draw.text((0, 0), emoji, font=font, embedded_color=True) - buffer = io.BytesIO() - img.save(buffer, format="PNG") - l = buffer.tell() - buffer.seek(0) - _cache[emoji] = rl.load_texture_from_image(rl.load_image_from_memory(".png", buffer.getvalue(), l)) + with io.BytesIO() as buffer: + img.save(buffer, format="PNG") + l = buffer.tell() + buffer.seek(0) + _cache[emoji] = rl.load_texture_from_image(rl.load_image_from_memory(".png", buffer.getvalue(), l)) return _cache[emoji] From 9b2f7341d85fbdeaa743ebc83a79fa04e9074686 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Oct 2025 21:39:04 -0700 Subject: [PATCH 217/341] raylib: wrap text for multilang (#36410) * fix multilang dialog height * split to file * stash * Revert "stash" This reverts commit deb4239fe69f0260420fad03f2350e622e31542f. * add updater * add files * stuff * try rev * stash * works! * works! * this should be the flow? * cursor wrapping -- it missed entire sections, changed formatting, and didn't use trn properly!!!!!!!!!!!!!!!!! * update translations * learned my lesson * this should be the one thing it's good at * update trans * onroad wrap * spanish * rename * clean up * load all * Revert "load all" This reverts commit 6f2a45861c914ffb9d40a5edd15751afd798d614. * jp translations * try jp * Revert "try jp" This reverts commit d0524b10110104baafcdc1ec385c3d57bc5ef901. * remove languages we can't add rn * tr * pt and fr * ai cannot be trusted * ai cannot be trusted * missing trans * add fonts * Revert "remove languages we can't add rn" This reverts commit 73dc75fae2b9e347d867b6636dab6e2b5fe59da7. * painfully slow to startup * only load what we need * Reapply "remove languages we can't add rn" This reverts commit 52cb48f3b838520a421f9b90e5ea4409c27d4bd0. * stash! * rm * Revert "stash!" This reverts commit 31d7c361079a8e57039a0117c81d59bf84f191c7. * revert this * revert that * make this dynamic! * device * revert * firehose * stuff * revert application * back * full revert * clean up * network * more system * fix dat * fixy --- selfdrive/ui/layouts/home.py | 5 +- selfdrive/ui/layouts/onboarding.py | 15 +++--- selfdrive/ui/layouts/settings/developer.py | 19 +++---- selfdrive/ui/layouts/settings/device.py | 59 +++++++++++---------- selfdrive/ui/layouts/settings/firehose.py | 14 ++--- selfdrive/ui/layouts/settings/settings.py | 13 ++--- selfdrive/ui/layouts/settings/software.py | 41 +++++++------- selfdrive/ui/layouts/settings/toggles.py | 53 +++++++++--------- selfdrive/ui/layouts/sidebar.py | 39 +++++++------- selfdrive/ui/onroad/alert_renderer.py | 13 ++--- selfdrive/ui/onroad/driver_camera_dialog.py | 3 +- selfdrive/ui/onroad/hud_renderer.py | 5 +- selfdrive/ui/update_translations.py | 4 +- selfdrive/ui/widgets/exp_mode_button.py | 3 +- selfdrive/ui/widgets/offroad_alerts.py | 11 ++-- selfdrive/ui/widgets/pairing_dialog.py | 11 ++-- selfdrive/ui/widgets/prime.py | 13 ++--- selfdrive/ui/widgets/setup.py | 15 +++--- selfdrive/ui/widgets/ssh_key.py | 15 +++--- system/ui/lib/multilang.py | 10 ++++ system/ui/widgets/confirm_dialog.py | 9 +++- system/ui/widgets/html_render.py | 3 +- system/ui/widgets/keyboard.py | 5 +- system/ui/widgets/list_view.py | 5 +- system/ui/widgets/network.py | 49 ++++++++--------- system/ui/widgets/option_dialog.py | 5 +- 26 files changed, 238 insertions(+), 199 deletions(-) create mode 100644 system/ui/lib/multilang.py diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 519e0e020a..34a7558cdc 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -9,6 +9,7 @@ from openpilot.selfdrive.ui.widgets.prime import PrimeWidget from openpilot.selfdrive.ui.widgets.setup import SetupWidget from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos +from openpilot.system.ui.lib.multilang import tr, trn from openpilot.system.ui.widgets.label import gui_label from openpilot.system.ui.widgets import Widget @@ -151,7 +152,7 @@ class HomeLayout(Widget): highlight_color = rl.Color(75, 95, 255, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(54, 77, 239, 255) rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color) - text = "UPDATE" + text = tr("UPDATE") text_size = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE) text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_size.x) // 2 text_y = self.update_notif_rect.y + (self.update_notif_rect.height - text_size.y) // 2 @@ -165,7 +166,7 @@ class HomeLayout(Widget): highlight_color = rl.Color(255, 70, 70, 255) if self.current_state == HomeLayoutState.ALERTS else rl.Color(226, 44, 44, 255) rl.draw_rectangle_rounded(self.alert_notif_rect, 0.3, 10, highlight_color) - alert_text = f"{self.alert_count} ALERT{'S' if self.alert_count > 1 else ''}" + alert_text = trn("{} ALERT", "{} ALERTS", self.alert_count).format(self.alert_count) text_size = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE) text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_size.x) // 2 text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - text_size.y) // 2 diff --git a/selfdrive/ui/layouts/onboarding.py b/selfdrive/ui/layouts/onboarding.py index a817fc53ad..df259a8fb5 100644 --- a/selfdrive/ui/layouts/onboarding.py +++ b/selfdrive/ui/layouts/onboarding.py @@ -6,6 +6,7 @@ from enum import IntEnum import pyray as rl from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.application import FontWeight, gui_app +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.label import Label @@ -107,12 +108,12 @@ class TermsPage(Widget): self._on_accept = on_accept self._on_decline = on_decline - self._title = Label("Welcome to openpilot", font_size=90, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) - self._desc = Label("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing.", + self._title = Label(tr("Welcome to openpilot"), font_size=90, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) + self._desc = Label(tr("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing."), font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) - self._decline_btn = Button("Decline", click_callback=on_decline) - self._accept_btn = Button("Agree", button_style=ButtonStyle.PRIMARY, click_callback=on_accept) + self._decline_btn = Button(tr("Decline"), click_callback=on_decline) + self._accept_btn = Button(tr("Agree"), button_style=ButtonStyle.PRIMARY, click_callback=on_accept) def _render(self, _): welcome_x = self._rect.x + 165 @@ -141,10 +142,10 @@ class TermsPage(Widget): class DeclinePage(Widget): def __init__(self, back_callback=None): super().__init__() - self._text = Label("You must accept the Terms and Conditions in order to use openpilot.", + self._text = Label(tr("You must accept the Terms and Conditions in order to use openpilot."), font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT) - self._back_btn = Button("Back", click_callback=back_callback) - self._uninstall_btn = Button("Decline, uninstall openpilot", button_style=ButtonStyle.DANGER, + self._back_btn = Button(tr("Back"), click_callback=back_callback) + self._uninstall_btn = Button(tr("Decline, uninstall openpilot"), button_style=ButtonStyle.DANGER, click_callback=self._on_uninstall_clicked) def _on_uninstall_clicked(self): diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index 21eb44efe7..91268960cb 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -6,19 +6,20 @@ from openpilot.system.ui.widgets.list_view import toggle_item from openpilot.system.ui.widgets.scroller import Scroller from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import DialogResult # Description constants DESCRIPTIONS = { - 'enable_adb': ( + 'enable_adb': tr( "ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " + "See https://docs.comma.ai/how-to/connect-to-comma for more info." ), - 'ssh_key': ( + 'ssh_key': tr( "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " + "other than your own. A comma employee will NEVER ask you to add their GitHub username." ), - 'alpha_longitudinal': ( + 'alpha_longitudinal': tr( "WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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." @@ -34,7 +35,7 @@ class DeveloperLayout(Widget): # Build items and keep references for callbacks/state updates self._adb_toggle = toggle_item( - "Enable ADB", + tr("Enable ADB"), description=DESCRIPTIONS["enable_adb"], initial_state=self._params.get_bool("AdbEnabled"), callback=self._on_enable_adb, @@ -43,7 +44,7 @@ class DeveloperLayout(Widget): # SSH enable toggle + SSH key management self._ssh_toggle = toggle_item( - "Enable SSH", + tr("Enable SSH"), description="", initial_state=self._params.get_bool("SshEnabled"), callback=self._on_enable_ssh, @@ -51,7 +52,7 @@ class DeveloperLayout(Widget): self._ssh_keys = ssh_key_item("SSH Keys", description=DESCRIPTIONS["ssh_key"]) self._joystick_toggle = toggle_item( - "Joystick Debug Mode", + tr("Joystick Debug Mode"), description="", initial_state=self._params.get_bool("JoystickDebugMode"), callback=self._on_joystick_debug_mode, @@ -59,14 +60,14 @@ class DeveloperLayout(Widget): ) self._long_maneuver_toggle = toggle_item( - "Longitudinal Maneuver Mode", + tr("Longitudinal Maneuver Mode"), description="", initial_state=self._params.get_bool("LongitudinalManeuverMode"), callback=self._on_long_maneuver_mode, ) self._alpha_long_toggle = toggle_item( - "openpilot Longitudinal Control (Alpha)", + tr("openpilot Longitudinal Control (Alpha)"), description=DESCRIPTIONS["alpha_longitudinal"], initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), callback=self._on_alpha_long_enabled, @@ -163,7 +164,7 @@ class DeveloperLayout(Widget): content = (f"

{self._alpha_long_toggle.title}


" + f"

{self._alpha_long_toggle.description}

") - dlg = ConfirmDialog(content, "Enable", rich=True) + dlg = ConfirmDialog(content, tr("Enable"), rich=True) gui_app.set_modal_overlay(dlg, callback=confirm_callback) else: diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 66341db37b..e9bdf59e26 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -12,6 +12,7 @@ from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog from openpilot.system.ui.widgets.html_render import HtmlModal @@ -21,10 +22,10 @@ from openpilot.system.ui.widgets.scroller import Scroller # Description constants DESCRIPTIONS = { - 'pair_device': "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.", - 'driver_camera': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)", - 'reset_calibration': "openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down.", - 'review_guide': "Review the rules, features, and limitations of openpilot", + 'pair_device': tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."), + 'driver_camera': tr("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)"), + 'reset_calibration': tr("openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down."), + 'review_guide': tr("Review the rules, features, and limitations of openpilot"), } @@ -45,27 +46,27 @@ class DeviceLayout(Widget): ui_state.add_offroad_transition_callback(self._offroad_transition) def _initialize_items(self): - dongle_id = self._params.get("DongleId") or "N/A" - serial = self._params.get("HardwareSerial") or "N/A" + dongle_id = self._params.get("DongleId") or tr("N/A") + serial = self._params.get("HardwareSerial") or tr("N/A") - self._pair_device_btn = button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device) + self._pair_device_btn = button_item(tr("Pair Device"), tr("PAIR"), DESCRIPTIONS['pair_device'], callback=self._pair_device) self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired()) - self._reset_calib_btn = button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt) + self._reset_calib_btn = button_item(tr("Reset Calibration"), tr("RESET"), DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt) self._reset_calib_btn.set_description_opened_callback(self._update_calib_description) - self._power_off_btn = dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt) + self._power_off_btn = dual_button_item(tr("Reboot"), tr("Power Off"), left_callback=self._reboot_prompt, right_callback=self._power_off_prompt) items = [ - text_item("Dongle ID", dongle_id), - text_item("Serial", serial), + text_item(tr("Dongle ID"), dongle_id), + text_item(tr("Serial"), serial), self._pair_device_btn, - button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), + button_item(tr("Driver Camera"), tr("PREVIEW"), DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), self._reset_calib_btn, - button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide, enabled=ui_state.is_offroad), - regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory, enabled=ui_state.is_offroad), + button_item(tr("Review Training Guide"), tr("REVIEW"), DESCRIPTIONS['review_guide'], self._on_review_training_guide, enabled=ui_state.is_offroad), + regulatory_btn := button_item(tr("Regulatory"), tr("VIEW"), callback=self._on_regulatory, enabled=ui_state.is_offroad), # TODO: implement multilang - # button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), + # button_item(tr("Change Language"), tr("CHANGE"), callback=self._show_language_selection, enabled=ui_state.is_offroad), self._power_off_btn, ] regulatory_btn.set_visible(TICI) @@ -106,7 +107,7 @@ class DeviceLayout(Widget): def _reset_calibration_prompt(self): if ui_state.engaged: - gui_app.set_modal_overlay(alert_dialog("Disengage to Reset Calibration")) + gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reset Calibration"))) return def reset_calibration(result: int): @@ -122,7 +123,7 @@ class DeviceLayout(Widget): self._params.put_bool("OnroadCycleRequested", True) self._update_calib_description() - dialog = ConfirmDialog("Are you sure you want to reset calibration?", "Reset") + dialog = ConfirmDialog(tr("Are you sure you want to reset calibration?"), tr("Reset")) gui_app.set_modal_overlay(dialog, callback=reset_calibration) def _update_calib_description(self): @@ -136,7 +137,8 @@ class DeviceLayout(Widget): if calib.calStatus != log.LiveCalibrationData.Status.uncalibrated: pitch = math.degrees(calib.rpyCalib[1]) yaw = math.degrees(calib.rpyCalib[2]) - desc += f" Your device is pointed {abs(pitch):.1f}° {'down' if pitch > 0 else 'up'} and {abs(yaw):.1f}° {'left' if yaw > 0 else 'right'}." + desc += tr(" Your device is pointed {:.1f}° {} and {:.1f}° {}.").format(abs(pitch), tr("down") if pitch > 0 else tr("up"), + abs(yaw), tr("left") if yaw > 0 else tr("right")) except Exception: cloudlog.exception("invalid CalibrationParams") @@ -148,9 +150,9 @@ class DeviceLayout(Widget): except Exception: cloudlog.exception("invalid LiveDelay") if lag_perc < 100: - desc += f"

Steering lag calibration is {lag_perc}% complete." + desc += tr("

Steering lag calibration is {}% complete.").format(lag_perc) else: - desc += "

Steering lag calibration is complete." + desc += tr("

Steering lag calibration is complete.") torque_bytes = self._params.get("LiveTorqueParameters") if torque_bytes: @@ -160,24 +162,24 @@ class DeviceLayout(Widget): if torque.useParams: torque_perc = torque.calPerc if torque_perc < 100: - desc += f" Steering torque response calibration is {torque_perc}% complete." + desc += tr(" Steering torque response calibration is {}% complete.").format(torque_perc) else: - desc += " Steering torque response calibration is complete." + desc += tr(" Steering torque response calibration is complete.") except Exception: cloudlog.exception("invalid LiveTorqueParameters") desc += "

" - desc += ("openpilot is continuously calibrating, resetting is rarely required. " + - "Resetting calibration will restart openpilot if the car is powered on.") + desc += tr("openpilot is continuously calibrating, resetting is rarely required. " + + "Resetting calibration will restart openpilot if the car is powered on.") self._reset_calib_btn.set_description(desc) def _reboot_prompt(self): if ui_state.engaged: - gui_app.set_modal_overlay(alert_dialog("Disengage to Reboot")) + gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reboot"))) return - dialog = ConfirmDialog("Are you sure you want to reboot?", "Reboot") + dialog = ConfirmDialog(tr("Are you sure you want to reboot?"), tr("Reboot")) gui_app.set_modal_overlay(dialog, callback=self._perform_reboot) def _perform_reboot(self, result: int): @@ -186,10 +188,10 @@ class DeviceLayout(Widget): def _power_off_prompt(self): if ui_state.engaged: - gui_app.set_modal_overlay(alert_dialog("Disengage to Power Off")) + gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Power Off"))) return - dialog = ConfirmDialog("Are you sure you want to power off?", "Power Off") + dialog = ConfirmDialog(tr("Are you sure you want to power off?"), tr("Power Off")) gui_app.set_modal_overlay(dialog, callback=self._perform_power_off) def _perform_power_off(self, result: int): @@ -210,5 +212,6 @@ class DeviceLayout(Widget): if not self._training_guide: def completed_callback(): gui_app.set_modal_overlay(None) + self._training_guide = TrainingGuide(completed_callback=completed_callback) gui_app.set_modal_overlay(self._training_guide) diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py index e8eaaa44f2..f4d70eaa47 100644 --- a/selfdrive/ui/layouts/settings/firehose.py +++ b/selfdrive/ui/layouts/settings/firehose.py @@ -8,19 +8,20 @@ from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.multilang import tr, trn from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.selfdrive.ui.lib.api_helpers import get_token -TITLE = "Firehose Mode" -DESCRIPTION = ( +TITLE = tr("Firehose Mode") +DESCRIPTION = tr( "openpilot learns to drive by watching humans, like you, drive.\n\n" + "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." ) -INSTRUCTIONS = ( +INSTRUCTIONS = tr( "For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n" + "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n\n" + "Frequently Asked Questions\n\n" @@ -106,7 +107,8 @@ class FirehoseLayout(Widget): # Contribution count (if available) if self.segment_count > 0: - contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far." + contrib_text = trn("{} segment of your driving is in the training dataset so far.", + "{} segments of your driving is in the training dataset so far.", self.segment_count).format(self.segment_count) y = self._draw_wrapped_text(x, y, w, contrib_text, gui_app.font(FontWeight.BOLD), 52, rl.WHITE) y += 20 + 20 @@ -132,9 +134,9 @@ class FirehoseLayout(Widget): network_metered = ui_state.sm["deviceState"].networkMetered if not network_metered and network_type != 0: # Not metered and connected - return "ACTIVE", self.GREEN + return tr("ACTIVE"), self.GREEN else: - return "INACTIVE: connect to an unmetered network", self.RED + return tr("INACTIVE: connect to an unmetered network"), self.RED def _fetch_firehose_stats(self): try: diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index d43382f199..7f431d3a77 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -8,6 +8,7 @@ from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wifi_manager import WifiManager from openpilot.system.ui.widgets import Widget @@ -58,12 +59,12 @@ class SettingsLayout(Widget): wifi_manager.set_active(False) self._panels = { - PanelType.DEVICE: PanelInfo("Device", DeviceLayout()), - PanelType.NETWORK: PanelInfo("Network", NetworkUI(wifi_manager)), - PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout()), - PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout()), - PanelType.FIREHOSE: PanelInfo("Firehose", FirehoseLayout()), - PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout()), + PanelType.DEVICE: PanelInfo(tr("Device"), DeviceLayout()), + PanelType.NETWORK: PanelInfo(tr("Network"), NetworkUI(wifi_manager)), + PanelType.TOGGLES: PanelInfo(tr("Toggles"), TogglesLayout()), + PanelType.SOFTWARE: PanelInfo(tr("Software"), SoftwareLayout()), + PanelType.FIREHOSE: PanelInfo(tr("Firehose"), FirehoseLayout()), + PanelType.DEVELOPER: PanelInfo(tr("Developer"), DeveloperLayout()), } self._font_medium = gui_app.font(FontWeight.MEDIUM) diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 0c17a54fbe..8e0cfdbc3c 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -4,6 +4,7 @@ import datetime from openpilot.common.time_helpers import system_time_valid from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.multilang import tr, trn from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.list_view import button_item, text_item, ListItem @@ -15,7 +16,7 @@ UPDATED_TIMEOUT = 10 # seconds to wait for updated to respond def time_ago(date: datetime.datetime | None) -> str: if not date: - return "never" + return tr("never") if not system_time_valid(): return date.strftime("%a %b %d %Y") @@ -26,16 +27,16 @@ def time_ago(date: datetime.datetime | None) -> str: diff_seconds = int((now - date).total_seconds()) if diff_seconds < 60: - return "now" + return tr("now") if diff_seconds < 3600: m = diff_seconds // 60 - return f"{m} minute{'s' if m != 1 else ''} ago" + return trn("{} minute ago", "{} minutes ago", m).format(m) if diff_seconds < 86400: h = diff_seconds // 3600 - return f"{h} hour{'s' if h != 1 else ''} ago" + return trn("{} hour ago", "{} hours ago", h).format(h) if diff_seconds < 604800: d = diff_seconds // 86400 - return f"{d} day{'s' if d != 1 else ''} ago" + return trn("{} day ago", "{} days ago", d).format(d) return date.strftime("%a %b %d %Y") @@ -43,12 +44,12 @@ class SoftwareLayout(Widget): def __init__(self): super().__init__() - self._onroad_label = ListItem(title="Updates are only downloaded while the car is off.") - self._version_item = text_item("Current Version", ui_state.params.get("UpdaterCurrentDescription") or "") - self._download_btn = button_item("Download", "CHECK", callback=self._on_download_update) + self._onroad_label = ListItem(title=tr("Updates are only downloaded while the car is off.")) + self._version_item = text_item(tr("Current Version"), ui_state.params.get("UpdaterCurrentDescription") or "") + self._download_btn = button_item(tr("Download"), tr("CHECK"), callback=self._on_download_update) # Install button is initially hidden - self._install_btn = button_item("Install Update", "INSTALL", callback=self._on_install_update) + self._install_btn = button_item(tr("Install Update"), tr("INSTALL"), callback=self._on_install_update) self._install_btn.set_visible(False) # Track waiting-for-updater transition to avoid brief re-enable while still idle @@ -66,7 +67,7 @@ class SoftwareLayout(Widget): self._install_btn, # TODO: implement branch switching # button_item("Target Branch", "SELECT", callback=self._on_select_branch), - button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall), + button_item("Uninstall", tr("UNINSTALL"), callback=self._on_uninstall), ] return items @@ -101,19 +102,19 @@ class SoftwareLayout(Widget): self._download_btn.action_item.set_value(updater_state) else: if failed_count > 0: - self._download_btn.action_item.set_value("failed to check for update") - self._download_btn.action_item.set_text("CHECK") + self._download_btn.action_item.set_value(tr("failed to check for update")) + self._download_btn.action_item.set_text(tr("CHECK")) elif fetch_available: - self._download_btn.action_item.set_value("update available") - self._download_btn.action_item.set_text("DOWNLOAD") + self._download_btn.action_item.set_value(tr("update available")) + self._download_btn.action_item.set_text(tr("DOWNLOAD")) else: last_update = ui_state.params.get("LastUpdateTime") if last_update: formatted = time_ago(last_update) - self._download_btn.action_item.set_value(f"up to date, last checked {formatted}") + self._download_btn.action_item.set_value(tr("up to date, last checked {}").format(formatted)) else: - self._download_btn.action_item.set_value("up to date, last checked never") - self._download_btn.action_item.set_text("CHECK") + self._download_btn.action_item.set_value(tr("up to date, last checked never")) + self._download_btn.action_item.set_text(tr("CHECK")) # If we've been waiting too long without a state change, reset state if self._waiting_for_updater and (time.monotonic() - self._waiting_start_ts > UPDATED_TIMEOUT): @@ -127,7 +128,7 @@ class SoftwareLayout(Widget): if update_available: new_desc = ui_state.params.get("UpdaterNewDescription") or "" new_release_notes = (ui_state.params.get("UpdaterNewReleaseNotes") or b"").decode("utf-8", "replace") - self._install_btn.action_item.set_text("INSTALL") + self._install_btn.action_item.set_text(tr("INSTALL")) self._install_btn.action_item.set_value(new_desc) self._install_btn.set_description(new_release_notes) # Enable install button for testing (like Qt showEvent) @@ -138,7 +139,7 @@ class SoftwareLayout(Widget): def _on_download_update(self): # Check if we should start checking or start downloading self._download_btn.action_item.set_enabled(False) - if self._download_btn.action_item.text == "CHECK": + if self._download_btn.action_item.text == tr("CHECK"): # Start checking for updates self._waiting_for_updater = True self._waiting_start_ts = time.monotonic() @@ -154,7 +155,7 @@ class SoftwareLayout(Widget): if result == DialogResult.CONFIRM: ui_state.params.put_bool("DoUninstall", True) - dialog = ConfirmDialog("Are you sure you want to uninstall?", "Uninstall") + dialog = ConfirmDialog(tr("Are you sure you want to uninstall?"), tr("Uninstall")) gui_app.set_modal_overlay(dialog, callback=handle_uninstall_confirmation) def _on_install_update(self): diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 01195142e3..1e4d5c6c85 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -5,6 +5,7 @@ from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_i from openpilot.system.ui.widgets.scroller import Scroller from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import DialogResult from openpilot.selfdrive.ui.ui_state import ui_state @@ -12,24 +13,24 @@ PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants # Description constants DESCRIPTIONS = { - "OpenpilotEnabledToggle": ( + "OpenpilotEnabledToggle": tr( "Use the openpilot system for adaptive cruise control and lane keep driver assistance. " + "Your attention is required at all times to use this feature." ), - "DisengageOnAccelerator": "When enabled, pressing the accelerator pedal will disengage openpilot.", - "LongitudinalPersonality": ( + "DisengageOnAccelerator": tr("When enabled, pressing the accelerator pedal will disengage openpilot."), + "LongitudinalPersonality": tr( "Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " + "In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " + "your steering wheel distance button." ), - "IsLdwEnabled": ( + "IsLdwEnabled": tr( "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line " + "without a turn signal activated while driving over 31 mph (50 km/h)." ), - "AlwaysOnDM": "Enable driver monitoring even when openpilot is not engaged.", - 'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", - "IsMetric": "Display speed in km/h instead of mph.", - "RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.", + "AlwaysOnDM": tr("Enable driver monitoring even when openpilot is not engaged."), + 'RecordFront': tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), + "IsMetric": tr("Display speed in km/h instead of mph."), + "RecordAudio": tr("Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect."), } @@ -42,49 +43,49 @@ class TogglesLayout(Widget): # param, title, desc, icon, needs_restart self._toggle_defs = { "OpenpilotEnabledToggle": ( - "Enable openpilot", + tr("Enable openpilot"), DESCRIPTIONS["OpenpilotEnabledToggle"], "chffr_wheel.png", True, ), "ExperimentalMode": ( - "Experimental Mode", + tr("Experimental Mode"), "", "experimental_white.png", False, ), "DisengageOnAccelerator": ( - "Disengage on Accelerator Pedal", + tr("Disengage on Accelerator Pedal"), DESCRIPTIONS["DisengageOnAccelerator"], "disengage_on_accelerator.png", False, ), "IsLdwEnabled": ( - "Enable Lane Departure Warnings", + tr("Enable Lane Departure Warnings"), DESCRIPTIONS["IsLdwEnabled"], "warning.png", False, ), "AlwaysOnDM": ( - "Always-On Driver Monitoring", + tr("Always-On Driver Monitoring"), DESCRIPTIONS["AlwaysOnDM"], "monitoring.png", False, ), "RecordFront": ( - "Record and Upload Driver Camera", + tr("Record and Upload Driver Camera"), DESCRIPTIONS["RecordFront"], "monitoring.png", True, ), "RecordAudio": ( - "Record and Upload Microphone Audio", + tr("Record and Upload Microphone Audio"), DESCRIPTIONS["RecordAudio"], "microphone.png", True, ), "IsMetric": ( - "Use Metric System", + tr("Use Metric System"), DESCRIPTIONS["IsMetric"], "metric.png", False, @@ -92,9 +93,9 @@ class TogglesLayout(Widget): } self._long_personality_setting = multiple_button_item( - "Driving Personality", + tr("Driving Personality"), DESCRIPTIONS["LongitudinalPersonality"], - buttons=["Aggressive", "Standard", "Relaxed"], + buttons=[tr("Aggressive"), tr("Standard"), tr("Relaxed")], button_width=255, callback=self._set_longitudinal_personality, selected_index=self._params.get("LongitudinalPersonality", return_default=True), @@ -119,7 +120,7 @@ class TogglesLayout(Widget): toggle.action_item.set_enabled(not locked) if needs_restart and not locked: - toggle.set_description(toggle.description + " Changing this setting will restart openpilot if the car is powered on.") + toggle.set_description(toggle.description + tr(" Changing this setting will restart openpilot if the car is powered on.")) # track for engaged state updates if locked: @@ -150,7 +151,7 @@ class TogglesLayout(Widget): def _update_toggles(self): ui_state.update_params() - e2e_description = ( + e2e_description = tr( "openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " + "Experimental features are listed below:
" + "

End-to-End Longitudinal Control


" + @@ -174,15 +175,15 @@ class TogglesLayout(Widget): self._long_personality_setting.action_item.set_enabled(False) self._params.remove("ExperimentalMode") - unavailable = "Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control." + unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control.") - long_desc = unavailable + " openpilot longitudinal control may come in a future update." + long_desc = unavailable + " " + tr("openpilot longitudinal control may come in a future update.") if ui_state.CP.alphaLongitudinalAvailable: if self._is_release: - long_desc = unavailable + " " + ("An alpha version of openpilot longitudinal control can be tested, along with " + - "Experimental mode, on non-release branches.") + long_desc = unavailable + " " + tr("An alpha version of openpilot longitudinal control can be tested, along with " + + "Experimental mode, on non-release branches.") else: - long_desc = "Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode." + long_desc = tr("Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode.") self._toggles["ExperimentalMode"].set_description("" + long_desc + "

" + e2e_description) else: @@ -221,7 +222,7 @@ class TogglesLayout(Widget): # show confirmation dialog content = (f"

{self._toggles['ExperimentalMode'].title}


" + f"

{self._toggles['ExperimentalMode'].description}

") - dlg = ConfirmDialog(content, "Enable", rich=True) + dlg = ConfirmDialog(content, tr("Enable"), rich=True) gui_app.set_modal_overlay(dlg, callback=confirm_callback) else: self._update_experimental_mode_icon() diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 9337b3c239..48b577ea59 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -5,6 +5,7 @@ from collections.abc import Callable from cereal import log from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, FONT_SCALE +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget @@ -39,13 +40,13 @@ class Colors: NETWORK_TYPES = { - NetworkType.none: "--", - NetworkType.wifi: "Wi-Fi", - NetworkType.ethernet: "ETH", - NetworkType.cell2G: "2G", - NetworkType.cell3G: "3G", - NetworkType.cell4G: "LTE", - NetworkType.cell5G: "5G", + NetworkType.none: tr("--"), + NetworkType.wifi: tr("Wi-Fi"), + NetworkType.ethernet: tr("ETH"), + NetworkType.cell2G: tr("2G"), + NetworkType.cell3G: tr("3G"), + NetworkType.cell4G: tr("LTE"), + NetworkType.cell5G: tr("5G"), } @@ -67,9 +68,9 @@ class Sidebar(Widget): self._net_type = NETWORK_TYPES.get(NetworkType.none) self._net_strength = 0 - self._temp_status = MetricData("TEMP", "GOOD", Colors.GOOD) - self._panda_status = MetricData("VEHICLE", "ONLINE", Colors.GOOD) - self._connect_status = MetricData("CONNECT", "OFFLINE", Colors.WARNING) + self._temp_status = MetricData(tr("TEMP"), tr("GOOD"), Colors.GOOD) + self._panda_status = MetricData(tr("VEHICLE"), tr("ONLINE"), Colors.GOOD) + self._connect_status = MetricData(tr("CONNECT"), tr("OFFLINE"), Colors.WARNING) self._recording_audio = False self._home_img = gui_app.texture("images/button_home.png", HOME_BTN.width, HOME_BTN.height) @@ -113,7 +114,7 @@ class Sidebar(Widget): self._update_panda_status() def _update_network_status(self, device_state): - self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, "Unknown") + self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, tr("Unknown")) strength = device_state.networkStrength self._net_strength = max(0, min(5, strength.raw + 1)) if strength > 0 else 0 @@ -121,26 +122,26 @@ class Sidebar(Widget): thermal_status = device_state.thermalStatus if thermal_status == ThermalStatus.green: - self._temp_status.update("TEMP", "GOOD", Colors.GOOD) + self._temp_status.update(tr("TEMP"), tr("GOOD"), Colors.GOOD) elif thermal_status == ThermalStatus.yellow: - self._temp_status.update("TEMP", "OK", Colors.WARNING) + self._temp_status.update(tr("TEMP"), tr("OK"), Colors.WARNING) else: - self._temp_status.update("TEMP", "HIGH", Colors.DANGER) + self._temp_status.update(tr("TEMP"), tr("HIGH"), Colors.DANGER) def _update_connection_status(self, device_state): last_ping = device_state.lastAthenaPingTime if last_ping == 0: - self._connect_status.update("CONNECT", "OFFLINE", Colors.WARNING) + self._connect_status.update(tr("CONNECT"), tr("OFFLINE"), Colors.WARNING) elif time.monotonic_ns() - last_ping < 80_000_000_000: # 80 seconds in nanoseconds - self._connect_status.update("CONNECT", "ONLINE", Colors.GOOD) + self._connect_status.update(tr("CONNECT"), tr("ONLINE"), Colors.GOOD) else: - self._connect_status.update("CONNECT", "ERROR", Colors.DANGER) + self._connect_status.update(tr("CONNECT"), tr("ERROR"), Colors.DANGER) def _update_panda_status(self): if ui_state.panda_type == log.PandaState.PandaType.unknown: - self._panda_status.update("NO", "PANDA", Colors.DANGER) + self._panda_status.update(tr("NO"), tr("PANDA"), Colors.DANGER) else: - self._panda_status.update("VEHICLE", "ONLINE", Colors.GOOD) + self._panda_status.update(tr("VEHICLE"), tr("ONLINE"), Colors.GOOD) def _handle_mouse_release(self, mouse_pos: MousePos): if rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN): diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 0f944bac52..a81fbfc440 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -5,6 +5,7 @@ from cereal import messaging, log from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.label import Label @@ -47,22 +48,22 @@ class Alert: # Pre-defined alert instances ALERT_STARTUP_PENDING = Alert( - text1="openpilot Unavailable", - text2="Waiting to start", + text1=tr("openpilot Unavailable"), + text2=tr("Waiting to start"), size=AlertSize.mid, status=AlertStatus.normal, ) ALERT_CRITICAL_TIMEOUT = Alert( - text1="TAKE CONTROL IMMEDIATELY", - text2="System Unresponsive", + text1=tr("TAKE CONTROL IMMEDIATELY"), + text2=tr("System Unresponsive"), size=AlertSize.full, status=AlertStatus.critical, ) ALERT_CRITICAL_REBOOT = Alert( - text1="System Unresponsive", - text2="Reboot Device", + text1=tr("System Unresponsive"), + text2=tr("Reboot Device"), size=AlertSize.mid, status=AlertStatus.normal, ) diff --git a/selfdrive/ui/onroad/driver_camera_dialog.py b/selfdrive/ui/onroad/driver_camera_dialog.py index a3bd2e23e0..543ea35e81 100644 --- a/selfdrive/ui/onroad/driver_camera_dialog.py +++ b/selfdrive/ui/onroad/driver_camera_dialog.py @@ -5,6 +5,7 @@ from openpilot.selfdrive.ui.onroad.cameraview import CameraView from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer from openpilot.selfdrive.ui.ui_state import ui_state, device from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets.label import gui_label @@ -30,7 +31,7 @@ class DriverCameraDialog(CameraView): if not self.frame: gui_label( rect, - "camera starting", + tr("camera starting"), font_size=100, font_weight=FontWeight.BOLD, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index 98c3d686fa..a2459c27e2 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -4,6 +4,7 @@ from openpilot.common.constants import CV from openpilot.selfdrive.ui.onroad.exp_button import ExpButton from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget @@ -144,7 +145,7 @@ class HudRenderer(Widget): elif ui_state.status == UIStatus.OVERRIDE: max_color = COLORS.override - max_text = "MAX" + max_text = tr("MAX") max_text_width = measure_text_cached(self._font_semi_bold, max_text, FONT_SIZES.max_speed).x rl.draw_text_ex( self._font_semi_bold, @@ -173,7 +174,7 @@ class HudRenderer(Widget): speed_pos = rl.Vector2(rect.x + rect.width / 2 - speed_text_size.x / 2, 180 - speed_text_size.y / 2) rl.draw_text_ex(self._font_bold, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.white) - unit_text = "km/h" if ui_state.is_metric else "mph" + unit_text = tr("km/h") if ui_state.is_metric else tr("mph") unit_text_size = measure_text_cached(self._font_medium, unit_text, FONT_SIZES.speed_unit) unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2) rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.white_translucent) diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py index 424b78851c..643d246012 100755 --- a/selfdrive/ui/update_translations.py +++ b/selfdrive/ui/update_translations.py @@ -4,10 +4,8 @@ import json import os from openpilot.common.basedir import BASEDIR +from openpilot.system.ui.lib.multilang import UI_DIR, TRANSLATIONS_DIR, LANGUAGES_FILE -UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui") -TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations") -LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json") TRANSLATIONS_INCLUDE_FILE = os.path.join(TRANSLATIONS_DIR, "alerts_generated.h") PLURAL_ONLY = ["en"] # base language, only create entries for strings with plural forms diff --git a/selfdrive/ui/widgets/exp_mode_button.py b/selfdrive/ui/widgets/exp_mode_button.py index 23031be306..faa3bf877f 100644 --- a/selfdrive/ui/widgets/exp_mode_button.py +++ b/selfdrive/ui/widgets/exp_mode_button.py @@ -1,6 +1,7 @@ import pyray as rl from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import Widget @@ -46,7 +47,7 @@ class ExperimentalModeButton(Widget): rl.draw_line_ex(rl.Vector2(line_x, rect.y), rl.Vector2(line_x, rect.y + rect.height), 3, separator_color) # Draw text label (left aligned) - text = "EXPERIMENTAL MODE ON" if self.experimental_mode else "CHILL MODE ON" + text = tr("EXPERIMENTAL MODE ON") if self.experimental_mode else tr("CHILL MODE ON") text_x = rect.x + self.horizontal_padding text_y = rect.y + rect.height / 2 - 45 * FONT_SCALE // 2 # Center vertically diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 0b3ca8176f..0bd5b161b9 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -6,6 +6,7 @@ from dataclasses import dataclass from openpilot.common.params import Params from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text @@ -14,7 +15,7 @@ from openpilot.system.ui.widgets.html_render import HtmlRenderer from openpilot.selfdrive.selfdrived.alertmanager import OFFROAD_ALERTS -NO_RELEASE_NOTES = "

No release notes available.

" +NO_RELEASE_NOTES = tr("

No release notes available.

") class AlertColors: @@ -101,15 +102,15 @@ class AbstractAlert(Widget, ABC): if self.dismiss_callback: self.dismiss_callback() - self.dismiss_btn = ActionButton("Close") + self.dismiss_btn = ActionButton(tr("Close")) - self.snooze_btn = ActionButton("Snooze Update", style=ButtonStyle.DARK) + self.snooze_btn = ActionButton(tr("Snooze Update"), style=ButtonStyle.DARK) self.snooze_btn.set_click_callback(snooze_callback) - self.excessive_actuation_btn = ActionButton("Acknowledge Excessive Actuation", style=ButtonStyle.DARK, min_width=800) + self.excessive_actuation_btn = ActionButton(tr("Acknowledge Excessive Actuation"), style=ButtonStyle.DARK, min_width=800) self.excessive_actuation_btn.set_click_callback(excessive_actuation_callback) - self.reboot_btn = ActionButton("Reboot and Update", min_width=600) + self.reboot_btn = ActionButton(tr("Reboot and Update"), min_width=600) self.reboot_btn.set_click_callback(lambda: HARDWARE.reboot()) # TODO: just use a Scroller? diff --git a/selfdrive/ui/widgets/pairing_dialog.py b/selfdrive/ui/widgets/pairing_dialog.py index 3778586f89..85b42d1a7a 100644 --- a/selfdrive/ui/widgets/pairing_dialog.py +++ b/selfdrive/ui/widgets/pairing_dialog.py @@ -8,6 +8,7 @@ from openpilot.common.swaglog import cloudlog from openpilot.common.params import Params from openpilot.system.ui.widgets import Widget from openpilot.system.ui.lib.application import FontWeight, gui_app +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.selfdrive.ui.ui_state import ui_state @@ -99,7 +100,7 @@ class PairingDialog(Widget): y += close_size + 40 # Title - title = "Pair your device to your comma account" + title = tr("Pair your device to your comma account") title_font = gui_app.font(FontWeight.NORMAL) left_width = int(content_rect.width * 0.5 - 15) @@ -124,9 +125,9 @@ class PairingDialog(Widget): def _render_instructions(self, rect: rl.Rectangle) -> None: instructions = [ - "Go to https://connect.comma.ai on your phone", - "Click \"add new device\" and scan the QR code on the right", - "Bookmark connect.comma.ai to your home screen to use it like an app", + tr("Go to https://connect.comma.ai on your phone"), + tr("Click \"add new device\" and scan the QR code on the right"), + tr("Bookmark connect.comma.ai to your home screen to use it like an app"), ] font = gui_app.font(FontWeight.BOLD) @@ -157,7 +158,7 @@ class PairingDialog(Widget): rl.draw_rectangle_rounded(rect, 0.1, 20, rl.Color(240, 240, 240, 255)) error_font = gui_app.font(FontWeight.BOLD) rl.draw_text_ex( - error_font, "QR Code Error", rl.Vector2(rect.x + 20, rect.y + rect.height // 2 - 15), 30, 0.0, rl.RED + error_font, tr("QR Code Error"), rl.Vector2(rect.x + 20, rect.y + rect.height // 2 - 15), 30, 0.0, rl.RED ) return diff --git a/selfdrive/ui/widgets/prime.py b/selfdrive/ui/widgets/prime.py index bbbd52a8cd..49a0e56cdc 100644 --- a/selfdrive/ui/widgets/prime.py +++ b/selfdrive/ui/widgets/prime.py @@ -2,6 +2,7 @@ import pyray as rl from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget @@ -29,21 +30,21 @@ class PrimeWidget(Widget): w = rect.width - 160 # Title - gui_label(rl.Rectangle(x, y, w, 90), "Upgrade Now", 75, font_weight=FontWeight.BOLD) + gui_label(rl.Rectangle(x, y, w, 90), tr("Upgrade Now"), 75, font_weight=FontWeight.BOLD) # Description with wrapping desc_y = y + 140 font = gui_app.font(FontWeight.LIGHT) - wrapped_text = "\n".join(wrap_text(font, "Become a comma prime member at connect.comma.ai", 56, int(w))) + wrapped_text = "\n".join(wrap_text(font, tr("Become a comma prime member at connect.comma.ai"), 56, int(w))) text_size = measure_text_cached(font, wrapped_text, 56) rl.draw_text_ex(font, wrapped_text, rl.Vector2(x, desc_y), 56, 0, rl.WHITE) # Features section features_y = desc_y + text_size.y + 50 - gui_label(rl.Rectangle(x, features_y, w, 50), "PRIME FEATURES:", 41, font_weight=FontWeight.BOLD) + gui_label(rl.Rectangle(x, features_y, w, 50), tr("PRIME FEATURES:"), 41, font_weight=FontWeight.BOLD) # Feature list - features = ["Remote access", "24/7 LTE connectivity", "1 year of drive storage", "Remote snapshots"] + features = [tr("Remote access"), tr("24/7 LTE connectivity"), tr("1 year of drive storage"), tr("Remote snapshots")] for i, feature in enumerate(features): item_y = features_y + 80 + i * 65 gui_label(rl.Rectangle(x, item_y, 100, 60), "✓", 50, color=rl.Color(70, 91, 234, 255)) @@ -58,5 +59,5 @@ class PrimeWidget(Widget): y = rect.y + 40 font = gui_app.font(FontWeight.BOLD) - rl.draw_text_ex(font, "✓ SUBSCRIBED", rl.Vector2(x, y), 41, 0, rl.Color(134, 255, 78, 255)) - rl.draw_text_ex(font, "comma prime", rl.Vector2(x, y + 61), 75, 0, rl.WHITE) + rl.draw_text_ex(font, tr("✓ SUBSCRIBED"), rl.Vector2(x, y), 41, 0, rl.Color(134, 255, 78, 255)) + rl.draw_text_ex(font, tr("comma prime"), rl.Vector2(x, y + 61), 75, 0, rl.WHITE) diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index 0538570e4a..ea88180ef8 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -3,6 +3,7 @@ from openpilot.common.time_helpers import system_time_valid from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.confirm_dialog import alert_dialog @@ -15,10 +16,10 @@ class SetupWidget(Widget): super().__init__() self._open_settings_callback = None self._pairing_dialog: PairingDialog | None = None - self._pair_device_btn = Button("Pair device", self._show_pairing, button_style=ButtonStyle.PRIMARY) - self._open_settings_btn = Button("Open", lambda: self._open_settings_callback() if self._open_settings_callback else None, + self._pair_device_btn = Button(tr("Pair device"), self._show_pairing, button_style=ButtonStyle.PRIMARY) + self._open_settings_btn = Button(tr("Open"), lambda: self._open_settings_callback() if self._open_settings_callback else None, button_style=ButtonStyle.PRIMARY) - self._firehose_label = Label("🔥 Firehose Mode 🔥", font_weight=FontWeight.MEDIUM, font_size=64) + self._firehose_label = Label(tr("🔥 Firehose Mode 🔥"), font_weight=FontWeight.MEDIUM, font_size=64) def set_open_settings_callback(self, callback): self._open_settings_callback = callback @@ -40,11 +41,11 @@ class SetupWidget(Widget): # Title font = gui_app.font(FontWeight.BOLD) - rl.draw_text_ex(font, "Finish Setup", rl.Vector2(x, y), 75, 0, rl.WHITE) + rl.draw_text_ex(font, tr("Finish Setup"), rl.Vector2(x, y), 75, 0, rl.WHITE) y += 113 # 75 + 38 spacing # Description - desc = "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer." + desc = tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.") light_font = gui_app.font(FontWeight.LIGHT) wrapped = wrap_text(light_font, desc, 50, int(w)) for line in wrapped: @@ -71,7 +72,7 @@ class SetupWidget(Widget): # Description desc_font = gui_app.font(FontWeight.NORMAL) - desc_text = "Maximize your training data uploads to improve openpilot's driving models." + desc_text = tr("Maximize your training data uploads to improve openpilot's driving models.") wrapped_desc = wrap_text(desc_font, desc_text, 40, int(w)) for line in wrapped_desc: @@ -87,7 +88,7 @@ class SetupWidget(Widget): def _show_pairing(self): if not system_time_valid(): - dlg = alert_dialog("Please connect to Wi-Fi to complete initial pairing") + dlg = alert_dialog(tr("Please connect to Wi-Fi to complete initial pairing")) gui_app.set_modal_overlay(dlg) return diff --git a/selfdrive/ui/widgets/ssh_key.py b/selfdrive/ui/widgets/ssh_key.py index dc3c5a4a76..e44c4aad5d 100644 --- a/selfdrive/ui/widgets/ssh_key.py +++ b/selfdrive/ui/widgets/ssh_key.py @@ -7,6 +7,7 @@ from enum import Enum from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.button import Button, ButtonStyle @@ -25,9 +26,9 @@ VALUE_FONT_SIZE = 48 class SshKeyActionState(Enum): - LOADING = "LOADING" - ADD = "ADD" - REMOVE = "REMOVE" + LOADING = tr("LOADING") + ADD = tr("ADD") + REMOVE = tr("REMOVE") class SshKeyAction(ItemAction): @@ -85,7 +86,7 @@ class SshKeyAction(ItemAction): def _handle_button_click(self): if self._state == SshKeyActionState.ADD: self._keyboard.reset() - self._keyboard.set_title("Enter your GitHub username") + self._keyboard.set_title(tr("Enter your GitHub username")) gui_app.set_modal_overlay(self._keyboard, callback=self._on_username_submit) elif self._state == SshKeyActionState.REMOVE: self._params.remove("GithubUsername") @@ -110,7 +111,7 @@ class SshKeyAction(ItemAction): response.raise_for_status() keys = response.text.strip() if not keys: - raise requests.exceptions.HTTPError("No SSH keys found") + raise requests.exceptions.HTTPError(tr("No SSH keys found")) # Success - save keys self._params.put("GithubUsername", username) @@ -119,10 +120,10 @@ class SshKeyAction(ItemAction): self._username = username except requests.exceptions.Timeout: - self._error_message = "Request timed out" + self._error_message = tr("Request timed out") self._state = SshKeyActionState.ADD except Exception: - self._error_message = f"No SSH keys found for user '{username}'" + self._error_message = tr("No SSH keys found for user '{}'").format(username) self._state = SshKeyActionState.ADD diff --git a/system/ui/lib/multilang.py b/system/ui/lib/multilang.py new file mode 100644 index 0000000000..c020b33d5e --- /dev/null +++ b/system/ui/lib/multilang.py @@ -0,0 +1,10 @@ +import os +import gettext +from openpilot.common.basedir import BASEDIR + +UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui") +TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations") +LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json") + +tr = gettext.gettext +trn = gettext.ngettext diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index 274307395c..8c5ae0aa01 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -1,5 +1,6 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.label import Label @@ -16,8 +17,10 @@ BACKGROUND_COLOR = rl.Color(27, 27, 27, 255) class ConfirmDialog(Widget): - def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel", rich: bool = False): + def __init__(self, text: str, confirm_text: str, cancel_text: str | None = None, rich: bool = False): super().__init__() + if cancel_text is None: + cancel_text = tr("Cancel") self._label = Label(text, 70, FontWeight.BOLD, text_color=rl.Color(201, 201, 201, 255)) self._html_renderer = HtmlRenderer(text=text, text_size={ElementType.P: 50}, center_text=True) self._cancel_button = Button(cancel_text, self._cancel_button_callback) @@ -85,5 +88,7 @@ class ConfirmDialog(Widget): return self._dialog_result -def alert_dialog(message: str, button_text: str = "Ok"): +def alert_dialog(message: str, button_text: str | None = None): + if button_text is None: + button_text = tr("OK") return ConfirmDialog(message, button_text, cancel_text="") diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index f90f78fe1b..368d02cdfc 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -4,6 +4,7 @@ from dataclasses import dataclass from enum import Enum from typing import Any from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget @@ -243,7 +244,7 @@ class HtmlModal(Widget): super().__init__() self._content = HtmlRenderer(file_path=file_path, text=text) self._scroll_panel = GuiScrollPanel() - self._ok_button = Button("OK", click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY) + self._ok_button = Button(tr("OK"), click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY) def _render(self, rect: rl.Rectangle): margin = 50 diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 6663c4e0ee..ac006c2545 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -5,6 +5,7 @@ from typing import Literal import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.inputbox import InputBox @@ -77,7 +78,7 @@ class Keyboard(Widget): self._backspace_last_repeat: float = 0.0 self._render_return_status = -1 - self._cancel_button = Button("Cancel", self._cancel_button_callback) + self._cancel_button = Button(tr("Cancel"), self._cancel_button_callback) self._eye_button = Button("", self._eye_button_callback, button_style=ButtonStyle.TRANSPARENT) @@ -98,7 +99,7 @@ class Keyboard(Widget): if key in self._key_icons: texture = self._key_icons[key] self._all_keys[key] = Button("", partial(self._key_callback, key), icon=texture, - button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.KEYBOARD, multi_touch=True) + button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.KEYBOARD, multi_touch=True) else: self._all_keys[key] = Button(key, partial(self._key_callback, key), button_style=ButtonStyle.KEYBOARD, font_size=85, multi_touch=True) self._all_keys[CAPS_LOCK_KEY] = Button("", partial(self._key_callback, CAPS_LOCK_KEY), icon=self._key_icons[CAPS_LOCK_KEY], diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index 1f4f69a6bf..55abe02fe1 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -3,6 +3,7 @@ import pyray as rl from collections.abc import Callable from abc import ABC from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle @@ -111,7 +112,7 @@ class ButtonAction(ItemAction): @property def text(self): - return _resolve_value(self._text_source, "Error") + return _resolve_value(self._text_source, tr("Error")) @property def value(self): @@ -149,7 +150,7 @@ class TextAction(ItemAction): @property def text(self): - return _resolve_value(self._text_source, "Error") + return _resolve_value(self._text_source, tr("Error")) def get_width_hint(self) -> float: text_width = measure_text_cached(self._font, self.text, ITEM_TEXT_FONT_SIZE).x diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 3bbf2e6402..a59030363b 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -4,6 +4,7 @@ from typing import cast import pyray as rl from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType from openpilot.system.ui.widgets import Widget @@ -70,7 +71,7 @@ class NetworkUI(Widget): self._current_panel: PanelType = PanelType.WIFI self._wifi_panel = WifiManagerUI(wifi_manager) self._advanced_panel = AdvancedNetworkSettings(wifi_manager) - self._nav_button = NavButton("Advanced") + self._nav_button = NavButton(tr("Advanced")) self._nav_button.set_click_callback(self._cycle_panel) def show_event(self): @@ -91,11 +92,11 @@ class NetworkUI(Widget): content_rect = rl.Rectangle(self._rect.x, self._rect.y + self._nav_button.rect.height + 40, self._rect.width, self._rect.height - self._nav_button.rect.height - 40) if self._current_panel == PanelType.WIFI: - self._nav_button.text = "Advanced" + self._nav_button.text = tr("Advanced") self._nav_button.set_position(self._rect.x + self._rect.width - self._nav_button.rect.width, self._rect.y + 20) self._wifi_panel.render(content_rect) else: - self._nav_button.text = "Back" + self._nav_button.text = tr("Back") self._nav_button.set_position(self._rect.x, self._rect.y + 20) self._advanced_panel.render(content_rect) @@ -116,40 +117,40 @@ class AdvancedNetworkSettings(Widget): # Tethering self._tethering_action = ToggleAction(initial_state=False) - tethering_btn = ListItem(title="Enable Tethering", action_item=self._tethering_action, callback=self._toggle_tethering) + tethering_btn = ListItem(title=tr("Enable Tethering"), action_item=self._tethering_action, callback=self._toggle_tethering) # Edit tethering password - self._tethering_password_action = ButtonAction(text="EDIT") - tethering_password_btn = ListItem(title="Tethering Password", action_item=self._tethering_password_action, callback=self._edit_tethering_password) + self._tethering_password_action = ButtonAction(text=tr("EDIT")) + tethering_password_btn = ListItem(title=tr("Tethering Password"), action_item=self._tethering_password_action, callback=self._edit_tethering_password) # Roaming toggle roaming_enabled = self._params.get_bool("GsmRoaming") self._roaming_action = ToggleAction(initial_state=roaming_enabled) - self._roaming_btn = ListItem(title="Enable Roaming", action_item=self._roaming_action, callback=self._toggle_roaming) + self._roaming_btn = ListItem(title=tr("Enable Roaming"), action_item=self._roaming_action, callback=self._toggle_roaming) # Cellular metered toggle cellular_metered = self._params.get_bool("GsmMetered") self._cellular_metered_action = ToggleAction(initial_state=cellular_metered) - self._cellular_metered_btn = ListItem(title="Cellular Metered", description="Prevent large data uploads when on a metered cellular connection", + self._cellular_metered_btn = ListItem(title=tr("Cellular Metered"), description=tr("Prevent large data uploads when on a metered cellular connection"), action_item=self._cellular_metered_action, callback=self._toggle_cellular_metered) # APN setting - self._apn_btn = button_item("APN Setting", "EDIT", callback=self._edit_apn) + self._apn_btn = button_item(tr("APN Setting"), tr("EDIT"), callback=self._edit_apn) # Wi-Fi metered toggle - self._wifi_metered_action = MultipleButtonAction(["default", "metered", "unmetered"], 255, 0, callback=self._toggle_wifi_metered) - wifi_metered_btn = ListItem(title="Wi-Fi Network Metered", description="Prevent large data uploads when on a metered Wi-Fi connection", + self._wifi_metered_action = MultipleButtonAction([tr("default"), tr("metered"), tr("unmetered")], 255, 0, callback=self._toggle_wifi_metered) + wifi_metered_btn = ListItem(title=tr("Wi-Fi Network Metered"), description=tr("Prevent large data uploads when on a metered Wi-Fi connection"), action_item=self._wifi_metered_action) items: list[Widget] = [ tethering_btn, tethering_password_btn, - text_item("IP Address", lambda: self._wifi_manager.ipv4_address), + text_item(tr("IP Address"), lambda: self._wifi_manager.ipv4_address), self._roaming_btn, self._apn_btn, self._cellular_metered_btn, wifi_metered_btn, - button_item("Hidden Network", "CONNECT", callback=self._connect_to_hidden_network), + button_item(tr("Hidden Network"), tr("CONNECT"), callback=self._connect_to_hidden_network), ] self._scroller = Scroller(items, line_separator=True, spacing=0) @@ -198,7 +199,7 @@ class AdvancedNetworkSettings(Widget): current_apn = self._params.get("GsmApn") or "" self._keyboard.reset(min_text_size=0) - self._keyboard.set_title("Enter APN", "leave blank for automatic configuration") + self._keyboard.set_title(tr("Enter APN"), tr("leave blank for automatic configuration")) self._keyboard.set_text(current_apn) gui_app.set_modal_overlay(self._keyboard, update_apn) @@ -231,11 +232,11 @@ class AdvancedNetworkSettings(Widget): self._wifi_manager.connect_to_network(ssid, password, hidden=True) self._keyboard.reset(min_text_size=0) - self._keyboard.set_title("Enter password", f"for \"{ssid}\"") + self._keyboard.set_title(tr("Enter password"), tr("for \"{}\"").format(ssid)) gui_app.set_modal_overlay(self._keyboard, enter_password) self._keyboard.reset(min_text_size=1) - self._keyboard.set_title("Enter SSID", "") + self._keyboard.set_title(tr("Enter SSID"), "") gui_app.set_modal_overlay(self._keyboard, connect_hidden) def _edit_tethering_password(self): @@ -248,7 +249,7 @@ class AdvancedNetworkSettings(Widget): self._tethering_password_action.set_enabled(False) self._keyboard.reset(min_text_size=MIN_PASSWORD_LENGTH) - self._keyboard.set_title("Enter new tethering password", "") + self._keyboard.set_title(tr("Enter new tethering password"), "") self._keyboard.set_text(self._wifi_manager.tethering_password) gui_app.set_modal_overlay(self._keyboard, update_password) @@ -281,7 +282,7 @@ class WifiManagerUI(Widget): self._networks: list[Network] = [] self._networks_buttons: dict[str, Button] = {} self._forget_networks_buttons: dict[str, Button] = {} - self._confirm_dialog = ConfirmDialog("", "Forget", "Cancel") + self._confirm_dialog = ConfirmDialog("", tr("Forget"), tr("Cancel")) self._wifi_manager.set_callbacks(need_auth=self._on_need_auth, activated=self._on_activated, @@ -305,15 +306,15 @@ class WifiManagerUI(Widget): def _render(self, rect: rl.Rectangle): if not self._networks: - gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + gui_label(rect, tr("Scanning Wi-Fi networks..."), 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) return if self.state == UIState.NEEDS_AUTH and self._state_network: - self.keyboard.set_title("Wrong password" if self._password_retry else "Enter password", f"for {self._state_network.ssid}") + self.keyboard.set_title(tr("Wrong password") if self._password_retry else tr("Enter password"), tr("for \"{}\"").format(self._state_network.ssid)) self.keyboard.reset(min_text_size=MIN_PASSWORD_LENGTH) gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(cast(Network, self._state_network), result)) elif self.state == UIState.SHOW_FORGET_CONFIRM and self._state_network: - self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{self._state_network.ssid}"?') + self._confirm_dialog.set_text(tr("Forget Wi-Fi Network \"{}\"?").format(self._state_network.ssid)) self._confirm_dialog.reset() gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(self._state_network, result)) else: @@ -363,11 +364,11 @@ class WifiManagerUI(Widget): if self.state == UIState.CONNECTING and self._state_network: if self._state_network.ssid == network.ssid: self._networks_buttons[network.ssid].set_enabled(False) - status_text = "CONNECTING..." + status_text = tr("CONNECTING...") elif self.state == UIState.FORGETTING and self._state_network: if self._state_network.ssid == network.ssid: self._networks_buttons[network.ssid].set_enabled(False) - status_text = "FORGETTING..." + status_text = tr("FORGETTING...") elif network.security_type == SecurityType.UNSUPPORTED: self._networks_buttons[network.ssid].set_enabled(False) else: @@ -445,7 +446,7 @@ class WifiManagerUI(Widget): self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.TRANSPARENT_WHITE_TEXT) self._networks_buttons[n.ssid].set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid()) - self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI, + self._forget_networks_buttons[n.ssid] = Button(tr("Forget"), partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI, font_size=45) self._forget_networks_buttons[n.ssid].set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid()) diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index 593a371fd9..8c63ca3f9f 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -1,5 +1,6 @@ import pyray as rl from openpilot.system.ui.lib.application import FontWeight +from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.button import Button, ButtonStyle from openpilot.system.ui.widgets.label import gui_label @@ -30,8 +31,8 @@ class MultiOptionDialog(Widget): text_padding=50) for option in options] self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING) - self.cancel_button = Button("Cancel", click_callback=lambda: self._set_result(DialogResult.CANCEL)) - self.select_button = Button("Select", click_callback=lambda: self._set_result(DialogResult.CONFIRM), button_style=ButtonStyle.PRIMARY) + self.cancel_button = Button(tr("Cancel"), click_callback=lambda: self._set_result(DialogResult.CANCEL)) + self.select_button = Button(tr("Select"), click_callback=lambda: self._set_result(DialogResult.CONFIRM), button_style=ButtonStyle.PRIMARY) def _set_result(self, result: DialogResult): self._result = result From cc8f6eadfe0e80ab23b5c281779dc5df7868a319 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Tue, 21 Oct 2025 13:58:48 -0700 Subject: [PATCH 218/341] =?UTF-8?q?DM:=20Medium=20Fanta=20model=20?= =?UTF-8?q?=F0=9F=A5=A4=20(#36409)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M fanta: e456b6c5-2dd0-400e-bf0f-6bb5a908971a --- selfdrive/modeld/models/dmonitoring_model.onnx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/modeld/models/dmonitoring_model.onnx b/selfdrive/modeld/models/dmonitoring_model.onnx index 5ae91f67a3..1b6a8c3e93 100644 --- a/selfdrive/modeld/models/dmonitoring_model.onnx +++ b/selfdrive/modeld/models/dmonitoring_model.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b2117ee4907add59e3fbe6829cda74e0ad71c0835b0ebb9373ba9425de0d336 +oid sha256:3a53626ab84757813fb16a1441704f2ae7192bef88c331bdc2415be6981d204f size 7191776 From 5289b08bcf0b136ef1829f4951d48aa22bdb1945 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 21 Oct 2025 21:54:40 -0700 Subject: [PATCH 219/341] bump retry count for `micd` and `soundd` (#36415) retry --- selfdrive/ui/soundd.py | 2 +- system/micd.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index 13f3b22091..44c463b18c 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -124,7 +124,7 @@ class Soundd: volume = ((weighted_db - AMBIENT_DB) / DB_SCALE) * (MAX_VOLUME - MIN_VOLUME) + MIN_VOLUME return math.pow(10, (np.clip(volume, MIN_VOLUME, MAX_VOLUME) - 1)) - @retry(attempts=7, delay=3) + @retry(attempts=10, delay=3) def get_stream(self, sd): # reload sounddevice to reinitialize portaudio sd._terminate() diff --git a/system/micd.py b/system/micd.py index 02ef82390b..b3558a15a8 100755 --- a/system/micd.py +++ b/system/micd.py @@ -94,7 +94,7 @@ class Mic: self.measurements = self.measurements[FFT_SAMPLES:] - @retry(attempts=7, delay=3) + @retry(attempts=10, delay=3) def get_stream(self, sd): # reload sounddevice to reinitialize portaudio sd._terminate() From d2bb8fe537ccddbd4ce1067fc6ae25976d3f4be1 Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:44:14 -0700 Subject: [PATCH 220/341] latcontrol_torque: more descriptive variable names (#36422) --- selfdrive/controls/lib/latcontrol_torque.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index f2b1e0b669..31d982859b 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -4,7 +4,6 @@ from collections import deque from cereal import log from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction -from opendbc.car.tests.test_lateral_limits import MAX_LAT_JERK_UP from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY from openpilot.common.filter_simple import FirstOrderFilter from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED @@ -28,6 +27,8 @@ KP = 1.0 KI = 0.3 KD = 0.0 +LP_FILTER_CUTOFF_HZ = 1.2 +LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 class LatControlTorque(LatControl): def __init__(self, CP, CI, dt): @@ -35,13 +36,13 @@ class LatControlTorque(LatControl): self.torque_params = CP.lateralTuning.torque.as_builder() self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.lateral_accel_from_torque = CI.lateral_accel_from_torque() - self.pid = PIDController(KP, KI, k_d=KD, rate=1/self.dt) + self.pid = PIDController(KP, KI, KD, rate=1/self.dt) self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg - self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES = int(1 / self.dt) - self.requested_lateral_accel_buffer = deque([0.] * self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES , maxlen=self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES) + self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt) + self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len) self.previous_measurement = 0.0 - self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * (MAX_LAT_JERK_UP - 0.5)), self.dt) + self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt) def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction): self.torque_params.latAccelFactor = latAccelFactor @@ -64,11 +65,11 @@ class LatControlTorque(LatControl): curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0)) lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2 - delay_frames = int(np.clip(lat_delay / self.dt, 1, self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES)) - expected_lateral_accel = self.requested_lateral_accel_buffer[-delay_frames] + delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len)) + expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames] # TODO factor out lateral jerk from error to later replace it with delay independent alternative future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2 - self.requested_lateral_accel_buffer.append(future_desired_lateral_accel) + self.lat_accel_request_buffer.append(future_desired_lateral_accel) gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay From a2e7f3788f609c5691418baa8ffddec442770594 Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:56:34 -0700 Subject: [PATCH 221/341] LateralTorqueState: log controller version and desired lateral jerk (#36421) --- cereal/log.capnp | 2 ++ selfdrive/controls/lib/latcontrol_torque.py | 3 +++ selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cereal/log.capnp b/cereal/log.capnp index 019fbbe10b..6cd8196ae0 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -918,6 +918,8 @@ struct ControlsState @0x97ff69c53601abf1 { saturated @7 :Bool; actualLateralAccel @9 :Float32; desiredLateralAccel @10 :Float32; + desiredLateralJerk @11 :Float32; + version @12 :Int32; } struct LateralLQRState { diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 31d982859b..c46b0965c5 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -29,6 +29,7 @@ KD = 0.0 LP_FILTER_CUTOFF_HZ = 1.2 LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 +VERSION = 0 class LatControlTorque(LatControl): def __init__(self, CP, CI, dt): @@ -56,6 +57,7 @@ class LatControlTorque(LatControl): def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay): pid_log = log.ControlsState.LateralTorqueState.new_message() + pid_log.version = VERSION if not active: output_torque = 0.0 pid_log.active = False @@ -106,6 +108,7 @@ class LatControlTorque(LatControl): pid_log.output = float(-output_torque) # TODO: log lat accel? pid_log.actualLateralAccel = float(measurement) pid_log.desiredLateralAccel = float(setpoint) + pid_log.desiredLateralJerk= float(desired_lateral_jerk) pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited)) # TODO left is positive in this convention diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index dd73ed12f7..732b441da9 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -55e82ab6370865a1427ebc1d559921a5354d9cbf \ No newline at end of file +1841c3c365689310f7f337a2aa6966d79bc4a60c \ No newline at end of file From b1b7c505a1e1ed3cf3206da8ca925a71122479f6 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:28:37 -0500 Subject: [PATCH 222/341] raylib: add danger button pressed style (#36413) add danger hover style --- system/ui/widgets/button.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 15d48b4e13..baf45ce95a 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -61,7 +61,7 @@ BUTTON_BACKGROUND_COLORS = { BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.NORMAL: rl.Color(74, 74, 74, 255), ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255), - ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), + ButtonStyle.DANGER: rl.Color(204, 0, 0, 255), ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.BLANK, From 4489517eeb7d02d6ce82842b9ddcf740ead8e19b Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:35:58 -0500 Subject: [PATCH 223/341] keyboard: replace duplicate period key (#36361) switch between underscore and hypen instead of period --- system/ui/widgets/keyboard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index ac006c2545..80b87483de 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -45,13 +45,13 @@ KEYBOARD_LAYOUTS = { "numbers": [ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], ["-", "/", ":", ";", "(", ")", "$", "&", "@", "\""], - [SYMBOL_KEY, ".", ",", "?", "!", "`", BACKSPACE_KEY], + [SYMBOL_KEY, "_", ",", "?", "!", "`", BACKSPACE_KEY], [ABC_KEY, SPACE_KEY, ".", ENTER_KEY], ], "specials": [ ["[", "]", "{", "}", "#", "%", "^", "*", "+", "="], ["_", "\\", "|", "~", "<", ">", "€", "£", "¥", "•"], - [NUMERIC_KEY, ".", ",", "?", "!", "'", BACKSPACE_KEY], + [NUMERIC_KEY, "-", ",", "?", "!", "'", BACKSPACE_KEY], [ABC_KEY, SPACE_KEY, ".", ENTER_KEY], ], } From 936740201ced09e617d7249e92746849934de5b6 Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Wed, 22 Oct 2025 11:50:37 -0700 Subject: [PATCH 224/341] latcontrol_torque: refactor low speed factor into pid controller (#36364) --- selfdrive/controls/lib/latcontrol_torque.py | 23 +++++++++------------ selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index c46b0965c5..443fd1851c 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -6,7 +6,6 @@ from cereal import log from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY from openpilot.common.filter_simple import FirstOrderFilter -from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.common.pid import PIDController @@ -16,16 +15,16 @@ from openpilot.common.pid import PIDController # wheel slip, or to speed. # This controller applies torque to achieve desired lateral -# accelerations. To compensate for the low speed effects we -# use a LOW_SPEED_FACTOR in the error. Additionally, there is -# friction in the steering wheel that needs to be overcome to -# move it at all, this is compensated for too. +# accelerations. To compensate for the low speed effects the +# proportional gain is increased at low speeds by the PID controller. +# Additionally, there is friction in the steering wheel that needs +# to be overcome to move it at all, this is compensated for too. -LOW_SPEED_X = [0, 10, 20, 30] -LOW_SPEED_Y = [15, 13, 10, 5] KP = 1.0 KI = 0.3 KD = 0.0 +INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30] +KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP] LP_FILTER_CUTOFF_HZ = 1.2 LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 @@ -37,7 +36,7 @@ class LatControlTorque(LatControl): self.torque_params = CP.lateralTuning.torque.as_builder() self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.lateral_accel_from_torque = CI.lateral_accel_from_torque() - self.pid = PIDController(KP, KI, KD, rate=1/self.dt) + self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, KD, rate=1/self.dt) self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt) @@ -79,13 +78,11 @@ class LatControlTorque(LatControl): measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt) self.previous_measurement = measurement - low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2 setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel error = setpoint - measurement - error_lsf = error + low_speed_factor / KP * error # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly - pid_log.error = float(error_lsf) + pid_log.error = float(error) ff = gravity_adjusted_future_lateral_accel # latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll ff -= self.torque_params.latAccelOffset @@ -105,10 +102,10 @@ class LatControlTorque(LatControl): pid_log.i = float(self.pid.i) pid_log.d = float(self.pid.d) pid_log.f = float(self.pid.f) - pid_log.output = float(-output_torque) # TODO: log lat accel? + pid_log.output = float(-output_torque) # TODO: log lat accel? pid_log.actualLateralAccel = float(measurement) pid_log.desiredLateralAccel = float(setpoint) - pid_log.desiredLateralJerk= float(desired_lateral_jerk) + pid_log.desiredLateralJerk = float(desired_lateral_jerk) pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited)) # TODO left is positive in this convention diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 732b441da9..cdd4301fcf 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -1841c3c365689310f7f337a2aa6966d79bc4a60c \ No newline at end of file +b508f43fb0481bce0859c9b6ab4f45ee690b8dab \ No newline at end of file From f983df0c70194d772fa33c5698ceb6484c5b3e14 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Wed, 22 Oct 2025 15:35:58 -0700 Subject: [PATCH 225/341] camerad: faster exposure convergence at startup (#36424) * might converge faster * accept darker at start * accept darker at start * it was unreasonably lax --- system/camerad/cameras/camera_qcom2.cc | 2 +- system/camerad/test/test_exposure.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index f2a064c606..d741e13cf3 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -50,7 +50,7 @@ public: Rect ae_xywh = {}; float measured_grey_fraction = 0; - float target_grey_fraction = 0.3; + float target_grey_fraction = 0.125; float fl_pix = 0; std::unique_ptr pm; diff --git a/system/camerad/test/test_exposure.py b/system/camerad/test/test_exposure.py index b853b0f2f2..6f89e04800 100644 --- a/system/camerad/test/test_exposure.py +++ b/system/camerad/test/test_exposure.py @@ -20,7 +20,7 @@ class TestCamerad: def _is_exposure_okay(self, i, med_mean=None): if med_mean is None: - med_mean = np.array([[0.2,0.4],[0.2,0.6]]) + med_mean = np.array([[0.18,0.3],[0.18,0.3]]) h, w = i.shape[:2] i = i[h//10:9*h//10,w//10:9*w//10] med_ex, mean_ex = med_mean From 5af1099fbfe503bdc50dbfa734d3cd83bf754a39 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 22 Oct 2025 15:36:09 -0700 Subject: [PATCH 226/341] rm watchdog (#36425) --- common/SConscript | 1 - common/watchdog.cc | 12 --------- common/watchdog.h | 5 ---- common/watchdog.py | 22 ----------------- selfdrive/ui/qt/offroad/settings.cc | 2 -- selfdrive/ui/ui.cc | 4 --- system/manager/process.py | 38 ++--------------------------- system/manager/process_config.py | 2 +- 8 files changed, 3 insertions(+), 83 deletions(-) delete mode 100644 common/watchdog.cc delete mode 100644 common/watchdog.h delete mode 100644 common/watchdog.py diff --git a/common/SConscript b/common/SConscript index 0891b79039..c771ee78b7 100644 --- a/common/SConscript +++ b/common/SConscript @@ -4,7 +4,6 @@ common_libs = [ 'params.cc', 'swaglog.cc', 'util.cc', - 'watchdog.cc', 'ratekeeper.cc', 'clutil.cc', ] diff --git a/common/watchdog.cc b/common/watchdog.cc deleted file mode 100644 index 44e8c83e6d..0000000000 --- a/common/watchdog.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include "common/watchdog.h" -#include "common/util.h" -#include "system/hardware/hw.h" - -const std::string watchdog_fn_prefix = Path::shm_path() + "/wd_"; // + - -bool watchdog_kick(uint64_t ts) { - static std::string fn = watchdog_fn_prefix + std::to_string(getpid()); - return util::write_file(fn.c_str(), &ts, sizeof(ts), O_WRONLY | O_CREAT) > 0; -} diff --git a/common/watchdog.h b/common/watchdog.h deleted file mode 100644 index 12dd2ca035..0000000000 --- a/common/watchdog.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -bool watchdog_kick(uint64_t ts); diff --git a/common/watchdog.py b/common/watchdog.py deleted file mode 100644 index ddb6f744e9..0000000000 --- a/common/watchdog.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import time -import struct -from openpilot.system.hardware.hw import Paths - -WATCHDOG_FN = f"{Paths.shm_path()}/wd_" -_LAST_KICK = 0.0 - -def kick_watchdog(): - global _LAST_KICK - current_time = time.monotonic() - - if current_time - _LAST_KICK < 1.0: - return - - try: - with open(f"{WATCHDOG_FN}{os.getpid()}", 'wb') as f: - f.write(struct.pack(' -#include "common/watchdog.h" #include "common/util.h" #include "selfdrive/ui/qt/network/networking.h" #include "selfdrive/ui/qt/offroad/settings.h" @@ -270,7 +269,6 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { // put language setting, exit Qt UI, and trigger fast restart params.put("LanguageSetting", langs[selection].toStdString()); qApp->exit(18); - watchdog_kick(0); } }); addItem(translateBtn); diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 9ec61b9b81..ed851a41c8 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -8,7 +8,6 @@ #include "common/transformations/orientation.hpp" #include "common/swaglog.h" #include "common/util.h" -#include "common/watchdog.h" #include "system/hardware/hw.h" #define BACKLIGHT_DT 0.05 @@ -116,9 +115,6 @@ void UIState::update() { update_state(this); updateStatus(); - if (sm->frame % UI_FREQ == 0) { - watchdog_kick(nanos_since_boot()); - } emit uiUpdate(*this); } diff --git a/system/manager/process.py b/system/manager/process.py index 5e86e87c76..e6b6a44c40 100644 --- a/system/manager/process.py +++ b/system/manager/process.py @@ -1,7 +1,6 @@ import importlib import os import signal -import struct import time import subprocess from collections.abc import Callable, ValuesView @@ -16,9 +15,6 @@ import openpilot.system.sentry as sentry from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog -from openpilot.common.watchdog import WATCHDOG_FN - -ENABLE_WATCHDOG = os.getenv("NO_WATCHDOG") is None def launcher(proc: str, name: str) -> None: @@ -70,10 +66,6 @@ class ManagerProcess(ABC): proc: Process | None = None enabled = True name = "" - - last_watchdog_time = 0 - watchdog_max_dt: int | None = None - watchdog_seen = False shutting_down = False @abstractmethod @@ -88,26 +80,6 @@ class ManagerProcess(ABC): self.stop(sig=signal.SIGKILL) self.start() - def check_watchdog(self, started: bool) -> None: - if self.watchdog_max_dt is None or self.proc is None: - return - - try: - fn = WATCHDOG_FN + str(self.proc.pid) - with open(fn, "rb") as f: - self.last_watchdog_time = struct.unpack('Q', f.read())[0] - except Exception: - pass - - dt = time.monotonic() - self.last_watchdog_time / 1e9 - - if dt > self.watchdog_max_dt: - if self.watchdog_seen and ENABLE_WATCHDOG: - cloudlog.error(f"Watchdog timeout for {self.name} (exitcode {self.proc.exitcode}) restarting ({started=})") - self.restart() - else: - self.watchdog_seen = True - def stop(self, retry: bool = True, block: bool = True, sig: signal.Signals = None) -> int | None: if self.proc is None: return None @@ -167,14 +139,13 @@ class ManagerProcess(ABC): class NativeProcess(ManagerProcess): - def __init__(self, name, cwd, cmdline, should_run, enabled=True, sigkill=False, watchdog_max_dt=None): + def __init__(self, name, cwd, cmdline, should_run, enabled=True, sigkill=False): self.name = name self.cwd = cwd self.cmdline = cmdline self.should_run = should_run self.enabled = enabled self.sigkill = sigkill - self.watchdog_max_dt = watchdog_max_dt self.launcher = nativelauncher def prepare(self) -> None: @@ -192,18 +163,16 @@ class NativeProcess(ManagerProcess): cloudlog.info(f"starting process {self.name}") self.proc = Process(name=self.name, target=self.launcher, args=(self.cmdline, cwd, self.name)) self.proc.start() - self.watchdog_seen = False self.shutting_down = False class PythonProcess(ManagerProcess): - def __init__(self, name, module, should_run, enabled=True, sigkill=False, watchdog_max_dt=None): + def __init__(self, name, module, should_run, enabled=True, sigkill=False): self.name = name self.module = module self.should_run = should_run self.enabled = enabled self.sigkill = sigkill - self.watchdog_max_dt = watchdog_max_dt self.launcher = launcher def prepare(self) -> None: @@ -226,7 +195,6 @@ class PythonProcess(ManagerProcess): cloudlog.info(f"starting python {self.module}") self.proc = Process(name=name, target=self.launcher, args=(self.module, self.name)) self.proc.start() - self.watchdog_seen = False self.shutting_down = False @@ -288,8 +256,6 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None else: p.stop(block=False) - p.check_watchdog(started) - for p in running: p.start() diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 0c35a3d3c9..5c02227ca5 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -80,7 +80,7 @@ procs = [ PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), PythonProcess("sensord", "system.sensord.sensord", only_onroad, enabled=not PC), - # NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), + # NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False), PythonProcess("ui", "selfdrive.ui.ui", always_run), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), From 99fdd59042251c7271d41c7c0b4fcacdb01ab885 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 22 Oct 2025 16:11:37 -0700 Subject: [PATCH 227/341] agnos 14.3 (#36426) --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 26 ++++++------ system/hardware/tici/all-partitions.json | 50 ++++++++++++------------ 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 07ec162f0b..9bbe0916f7 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="14.2" + export AGNOS_VERSION="14.3" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 035d8aa011..61ba1d38ee 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -56,29 +56,29 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9.img.xz", - "hash": "08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9", - "hash_raw": "08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9", - "size": 17868800, + "url": "https://commadist.azureedge.net/agnosupdate/boot-273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925.img.xz", + "hash": "273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925", + "hash_raw": "273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925", + "size": 17496064, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "18fab2e1eb2e43e5c39e20ee20e0d391586de528df6dbfdab6dabcdab835ee3e" + "ondevice_hash": "562c9137843994995188f21f5ec3bac408ac7dee030627aef701853814a1d33e" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b.img.xz", - "hash": "8d4b4dd80a8a537adf82faa07928066bec4568eae73bdcf4a5f0da94fb77b485", - "hash_raw": "1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b", - "size": 5368709120, + "url": "https://commadist.azureedge.net/agnosupdate/system-71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c.img.xz", + "hash": "2e41ebf1cf7e3801fa447c1e133d6d1b928546c412c36a843f5bd344557b9908", + "hash_raw": "71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c", + "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "a9569b9286fba882be003f9710383ae6de229a72db936e80be08dbd2c23f320e", + "ondevice_hash": "1d65fd6d8bb4b0c6f9920e0b5c49c7743b5949f8057676511765f1900ab6a771", "alt": { - "hash": "1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b", - "url": "https://commadist.azureedge.net/agnosupdate/system-1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b.img", - "size": 5368709120 + "hash": "71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c", + "url": "https://commadist.azureedge.net/agnosupdate/system-71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c.img", + "size": 4718592000 } } ] \ No newline at end of file diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 6b99df1c38..434c0567d1 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-08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9.img.xz", - "hash": "08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9", - "hash_raw": "08291eb52a9d54b77e191ea1a78addf24aae28e15306ac3118d03ac9be29fbe9", - "size": 17868800, + "url": "https://commadist.azureedge.net/agnosupdate/boot-273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925.img.xz", + "hash": "273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925", + "hash_raw": "273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925", + "size": 17496064, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "18fab2e1eb2e43e5c39e20ee20e0d391586de528df6dbfdab6dabcdab835ee3e" + "ondevice_hash": "562c9137843994995188f21f5ec3bac408ac7dee030627aef701853814a1d33e" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b.img.xz", - "hash": "8d4b4dd80a8a537adf82faa07928066bec4568eae73bdcf4a5f0da94fb77b485", - "hash_raw": "1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b", - "size": 5368709120, + "url": "https://commadist.azureedge.net/agnosupdate/system-71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c.img.xz", + "hash": "2e41ebf1cf7e3801fa447c1e133d6d1b928546c412c36a843f5bd344557b9908", + "hash_raw": "71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c", + "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "a9569b9286fba882be003f9710383ae6de229a72db936e80be08dbd2c23f320e", + "ondevice_hash": "1d65fd6d8bb4b0c6f9920e0b5c49c7743b5949f8057676511765f1900ab6a771", "alt": { - "hash": "1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b", - "url": "https://commadist.azureedge.net/agnosupdate/system-1a653b2a2006eb19017b9f091928a51fbb0b91c1ab218971779936892c9bd71b.img", - "size": 5368709120 + "hash": "71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c", + "url": "https://commadist.azureedge.net/agnosupdate/system-71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c.img", + "size": 4718592000 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-a154dec5ebad07f63ebef989a1f7e44c449b9fb94b1048157d426ff0e78feef8.img.xz", - "hash": "32ef650ba25cbf867eb4699096e33027aa0ab79e05de2d1dfee3601b00b4fdf6", - "hash_raw": "a154dec5ebad07f63ebef989a1f7e44c449b9fb94b1048157d426ff0e78feef8", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-8ed6bf12f84d64770aca750daacbd1578d199cc470ecd81ace255cd9cf5065e8.img.xz", + "hash": "4b1aae460d6a81d27691e2aa4fd96634eca607cac65305b5147350f1578beebd", + "hash_raw": "8ed6bf12f84d64770aca750daacbd1578d199cc470ecd81ace255cd9cf5065e8", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "9f21158f9055983c237d47a8eea8e27e978b5f25383756a7a9363a7bd9f7f72e" + "ondevice_hash": "be4365d37920853fb05fbffa793f7818890a41e4869c36ab16439a5f18d8d945" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-31ebdff72d44d3f60bdf0920e39171795494c275b8cff023cf23ec592af7a4b3.img.xz", - "hash": "a62837b235be14b257baf05ddc6bddd026c8859bbb4f154d0323c7efa58cb938", - "hash_raw": "31ebdff72d44d3f60bdf0920e39171795494c275b8cff023cf23ec592af7a4b3", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-11fe1e0f066bb8639f6bbbf2c71f19472f1ae436b96c92fe2d21c456dce1bf88.img.xz", + "hash": "517273cd69a34ae523e6075baa79dd46fe5a940e376faba50f26ec0b74a27610", + "hash_raw": "11fe1e0f066bb8639f6bbbf2c71f19472f1ae436b96c92fe2d21c456dce1bf88", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "a5caa169c840de6d1804b4186a1d26486be95e1837c4df16ec45952665356942" + "ondevice_hash": "498a5bc3ed091663543d2882831b45e3f92743df458a901cf6f64e791e518cb2" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-16518389a1ed7ad6277dbab75d18aa13833fb4ed4010f456438f2c2ac8c61140.img.xz", - "hash": "cb8c2fc2ae83cacb86af4ce96c6d61e4bd3cd2591e612e12878c27fa51030ffa", - "hash_raw": "16518389a1ed7ad6277dbab75d18aa13833fb4ed4010f456438f2c2ac8c61140", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-a60a00504f28f72179c44dfa539ef447c8e12a614aa23cef29e4b04c394505f1.img.xz", + "hash": "c32098db26bf2aefd1fa6184e3ac5db304adb35bedc6693b8729046c40aad7fb", + "hash_raw": "a60a00504f28f72179c44dfa539ef447c8e12a614aa23cef29e4b04c394505f1", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "5dd8e1f87a3f985ece80f7a36da1cbdabd77bcc11d26fc7bb85540069eff8ead" + "ondevice_hash": "cf16e217c88d92bc99439cd3e8e24e6a1776bfc4b175044643bfc8b42968bd38" } ] \ No newline at end of file From c33c9ff22a7ef901529907d801787b408841e53e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 23 Oct 2025 07:17:57 +0800 Subject: [PATCH 228/341] raylib: optimize html renderer with height caching (#36418) optimize html renderer with height caching --- system/ui/widgets/html_render.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index 368d02cdfc..b7e28a8d52 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -65,6 +65,9 @@ class HtmlRenderer(Widget): if text_size is None: text_size = {} + self._cached_height: float | None = None + self._cached_width: int = -1 + # Base paragraph size (Qt stylesheet default is 48px in offroad alerts) base_p_size = int(text_size.get(ElementType.P, 48)) @@ -97,6 +100,8 @@ class HtmlRenderer(Widget): def parse_html_content(self, html_content: str) -> None: self.elements.clear() + self._cached_height = None + self._cached_width = -1 # Remove HTML comments html_content = re.sub(r'', '', html_content, flags=re.DOTALL) @@ -211,6 +216,9 @@ class HtmlRenderer(Widget): return current_y - rect.y def get_total_height(self, content_width: int) -> float: + if self._cached_height is not None and self._cached_width == content_width: + return self._cached_height + total_height = 0.0 padding = 20 usable_width = content_width - (padding * 2) @@ -231,6 +239,10 @@ class HtmlRenderer(Widget): total_height += element.margin_bottom + # Store result in cache + self._cached_height = total_height + self._cached_width = content_width + return total_height def _get_font(self, weight: FontWeight): From 215acefbb4094740698903a9f60bb2e57eabe5b0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 23 Oct 2025 07:18:11 +0800 Subject: [PATCH 229/341] raylib: precompile regex patterns for faster HTML parsing (#36417) precompiled regex --- system/ui/widgets/html_render.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/system/ui/widgets/html_render.py b/system/ui/widgets/html_render.py index b7e28a8d52..7d90d56925 100644 --- a/system/ui/widgets/html_render.py +++ b/system/ui/widgets/html_render.py @@ -31,6 +31,10 @@ class ElementType(Enum): TAG_NAMES = '|'.join([t.value for t in ElementType]) START_TAG_RE = re.compile(f'<({TAG_NAMES})>') END_TAG_RE = re.compile(f'') +COMMENT_RE = re.compile(r'', flags=re.DOTALL) +DOCTYPE_RE = re.compile(r']*>') +HTML_BODY_TAGS_RE = re.compile(r']*>') +TOKEN_RE = re.compile(r']+>|<[^>]+>|[^<\s]+') def is_tag(token: str) -> tuple[bool, bool, ElementType | None]: @@ -104,14 +108,14 @@ class HtmlRenderer(Widget): self._cached_width = -1 # Remove HTML comments - html_content = re.sub(r'', '', html_content, flags=re.DOTALL) + html_content = COMMENT_RE.sub('', html_content) # Remove DOCTYPE, html, head, body tags but keep their content - html_content = re.sub(r']*>', '', html_content) - html_content = re.sub(r']*>', '', html_content) + html_content = DOCTYPE_RE.sub('', html_content) + html_content = HTML_BODY_TAGS_RE.sub('', html_content) # Parse HTML - tokens = re.findall(r']+>|<[^>]+>|[^<\s]+', html_content) + tokens = TOKEN_RE.findall(html_content) def close_tag(): nonlocal current_content From 00e20f15242a2ea4abfca5ba794a537c8f8aa32f Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:19:37 -0500 Subject: [PATCH 230/341] raylib: fix "Reboot" button pressed style (#36412) use normal style for dual button action left button --- system/ui/widgets/list_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index 55abe02fe1..a02c7a1ebc 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -176,7 +176,7 @@ class DualButtonAction(ItemAction): super().__init__(width=0, enabled=enabled) # Width 0 means use full width self.left_text, self.right_text = left_text, right_text - self.left_button = Button(left_text, click_callback=left_callback, button_style=ButtonStyle.LIST_ACTION, text_padding=0) + self.left_button = Button(left_text, click_callback=left_callback, button_style=ButtonStyle.NORMAL, text_padding=0) self.right_button = Button(right_text, click_callback=right_callback, button_style=ButtonStyle.DANGER, text_padding=0) def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: From 856f8d3d47983a80bfdb08d2208bcef9278f5232 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 23 Oct 2025 07:22:51 +0800 Subject: [PATCH 231/341] raylib: add branch switcher (#36411) * add branch switcher * improve --- selfdrive/ui/layouts/settings/software.py | 32 ++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 8e0cfdbc3c..1b78b5c9e8 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -8,6 +8,7 @@ from openpilot.system.ui.lib.multilang import tr, trn from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.list_view import button_item, text_item, ListItem +from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog from openpilot.system.ui.widgets.scroller import Scroller # TODO: remove this. updater fails to respond on startup if time is not correct @@ -52,6 +53,9 @@ class SoftwareLayout(Widget): self._install_btn = button_item(tr("Install Update"), tr("INSTALL"), callback=self._on_install_update) self._install_btn.set_visible(False) + self._select_branch_dialog: MultiOptionDialog | None = None + self._target_branch_btn = button_item(tr("Target Branch"), tr("SELECT"), callback=self._on_select_branch) + # Track waiting-for-updater transition to avoid brief re-enable while still idle self._waiting_for_updater = False self._waiting_start_ts: float = 0.0 @@ -65,8 +69,7 @@ class SoftwareLayout(Widget): self._version_item, self._download_btn, self._install_btn, - # TODO: implement branch switching - # button_item("Target Branch", "SELECT", callback=self._on_select_branch), + self._target_branch_btn, button_item("Uninstall", tr("UNINSTALL"), callback=self._on_uninstall), ] return items @@ -87,6 +90,8 @@ class SoftwareLayout(Widget): self._version_item.action_item.set_text(current_desc) self._version_item.set_description(current_release_notes) + self._target_branch_btn.action_item.set_value(ui_state.params.get("UpdaterTargetBranch") or "") + # Update download button visibility and state self._download_btn.set_visible(ui_state.is_offroad()) @@ -163,4 +168,25 @@ class SoftwareLayout(Widget): self._install_btn.action_item.set_enabled(False) ui_state.params.put_bool("DoReboot", True) - def _on_select_branch(self): pass + def _on_select_branch(self): + current_branch = ui_state.params.get("GitBranch") or "" + branches_str = ui_state.params.get("UpdaterAvailableBranches") or "" + + available_branches = [b.strip() for b in branches_str.split(",") if b.strip()] + priority_branches = [current_branch, "devel-staging", "devel", "nightly", "nightly-dev", "master"] + for branch in priority_branches: + if branch in available_branches: + available_branches.remove(branch) + available_branches.insert(0, branch) + + self._select_branch_dialog = MultiOptionDialog(tr("Select a branch"), available_branches, current=current_branch) + gui_app.set_modal_overlay(self._select_branch_dialog, callback=self._handle_branch_selection) + + def _handle_branch_selection(self, result: int): + if result == 1 and self._select_branch_dialog: + selected = self._select_branch_dialog.selection + self._target_branch_btn.action_item.set_value(selected) + ui_state.params.put("UpdaterTargetBranch", selected) + os.system("pkill -SIGHUP -f system.updated.updated") + + self._select_branch_dialog = None From 4ccafff123c45ea644e20818381b9cde37b9e232 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 22 Oct 2025 16:28:28 -0700 Subject: [PATCH 232/341] raylib: multilang (#36195) * fix multilang dialog height * split to file * stash * Revert "stash" This reverts commit deb4239fe69f0260420fad03f2350e622e31542f. * add updater * add files * stuff * try rev * stash * works! * works! * this should be the flow? * cursor wrapping -- it missed entire sections, changed formatting, and didn't use trn properly!!!!!!!!!!!!!!!!! * update translations * learned my lesson * this should be the one thing it's good at * update trans * onroad wrap * spanish * rename * clean up * load all * Revert "load all" This reverts commit 6f2a45861c914ffb9d40a5edd15751afd798d614. * jp translations * try jp * Revert "try jp" This reverts commit d0524b10110104baafcdc1ec385c3d57bc5ef901. * remove languages we can't add rn * tr * pt and fr * ai cannot be trusted * ai cannot be trusted * missing trans * add fonts * Revert "remove languages we can't add rn" This reverts commit 73dc75fae2b9e347d867b6636dab6e2b5fe59da7. * painfully slow to startup * only load what we need * Reapply "remove languages we can't add rn" This reverts commit 52cb48f3b838520a421f9b90e5ea4409c27d4bd0. * add system * that's sick that this just works (dynamic) * fix description falling back to first str + support callable titles in list items * device is now live! * make firehose live * developer * network live * software live * and that * toggles live * regen * start to clean up gpt * revert op sans * bruh * update translations * rm old script * add noops for descriptions to fix translating away from non-english after startup * missing de * do filtering in multilang.py * clean up clean up * codespell: ignore po * fix update * should not depend * more live * sidebar and offroad alert panel live * fix issues with offroad alerts * fix firehose live * fix weird tr("") behavior * sh key live bugfix * setup.py live * update * update * no fuzzy matching -- breaks dynamic translations * rm this * fix calib desc live trans * change onroad * rm dfonts * clean up device * missing live * update * op lint * not true * add to gitignore * speed up startup by reducing chars by ~half * fix scons * fix crash going from qt * preserve original lang * cancel kb live translate * no preserve * fix lint --- .gitignore | 1 + pyproject.toml | 2 +- selfdrive/SConscript | 3 +- selfdrive/ui/SConscript | 183 +-- selfdrive/ui/layouts/settings/developer.py | 29 +- selfdrive/ui/layouts/settings/device.py | 64 +- selfdrive/ui/layouts/settings/firehose.py | 17 +- selfdrive/ui/layouts/settings/settings.py | 19 +- selfdrive/ui/layouts/settings/software.py | 10 +- selfdrive/ui/layouts/settings/toggles.py | 43 +- selfdrive/ui/layouts/sidebar.py | 44 +- selfdrive/ui/translations/app.pot | 1134 ++++++++++++++++++ selfdrive/ui/translations/app_de.po | 1225 +++++++++++++++++++ selfdrive/ui/translations/app_en.po | 1211 +++++++++++++++++++ selfdrive/ui/translations/app_es.po | 1229 +++++++++++++++++++ selfdrive/ui/translations/app_fr.po | 1234 ++++++++++++++++++++ selfdrive/ui/translations/app_pt-BR.po | 1220 +++++++++++++++++++ selfdrive/ui/translations/app_tr.po | 1214 +++++++++++++++++++ selfdrive/ui/update_translations_raylib.py | 38 + selfdrive/ui/widgets/offroad_alerts.py | 39 +- selfdrive/ui/widgets/setup.py | 6 +- selfdrive/ui/widgets/ssh_key.py | 12 +- system/ui/lib/application.py | 12 +- system/ui/lib/multilang.py | 70 +- system/ui/widgets/button.py | 2 +- system/ui/widgets/keyboard.py | 2 +- system/ui/widgets/label.py | 12 +- system/ui/widgets/list_view.py | 38 +- system/ui/widgets/network.py | 30 +- 29 files changed, 8874 insertions(+), 269 deletions(-) create mode 100644 selfdrive/ui/translations/app.pot create mode 100644 selfdrive/ui/translations/app_de.po create mode 100644 selfdrive/ui/translations/app_en.po create mode 100644 selfdrive/ui/translations/app_es.po create mode 100644 selfdrive/ui/translations/app_fr.po create mode 100644 selfdrive/ui/translations/app_pt-BR.po create mode 100644 selfdrive/ui/translations/app_tr.po create mode 100755 selfdrive/ui/update_translations_raylib.py diff --git a/.gitignore b/.gitignore index 0832b3964e..d3306a11ba 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ a.out *.pyxbldc *.vcd *.qm +*.mo *_pyx.cpp config.json clcache diff --git a/pyproject.toml b/pyproject.toml index 78bf8c22ee..6cc6298eb3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -177,7 +177,7 @@ quiet-level = 3 # if you've got a short variable name that's getting flagged, add it here ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite" builtin = "clear,rare,informal,code,names,en-GB_to_en-US" -skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*" +skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*" [tool.mypy] python_version = "3.11" diff --git a/selfdrive/SConscript b/selfdrive/SConscript index 43bf7d0476..55f347c44e 100644 --- a/selfdrive/SConscript +++ b/selfdrive/SConscript @@ -3,5 +3,4 @@ SConscript(['controls/lib/lateral_mpc_lib/SConscript']) SConscript(['controls/lib/longitudinal_mpc_lib/SConscript']) SConscript(['locationd/SConscript']) SConscript(['modeld/SConscript']) -if GetOption('extras'): - SConscript(['ui/SConscript']) +SConscript(['ui/SConscript']) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 7ede8c394a..a7670b2482 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,98 +1,111 @@ +import os import json Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') -base_libs = [common, messaging, visionipc, transformations, - 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] - -if arch == 'larch64': - base_libs.append('EGL') - -if arch == "Darwin": - del base_libs[base_libs.index('OpenCL')] - qt_env['FRAMEWORKS'] += ['OpenCL'] - -# FIXME: remove this once we're on 5.15 (24.04) -qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] - -qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs) -widgets_src = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc", - "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", - "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", - "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", - "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] - -widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs) -Export('widgets') -qt_libs = [widgets, qt_util] + base_libs - -qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc", - "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", - "qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", - "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc", - "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc", - "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] - -# build translation files +# compile gettext .po -> .mo translations with open(File("translations/languages.json").abspath) as f: languages = json.loads(f.read()) -translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.values()] -translation_targets = [src.replace(".ts", ".qm") for src in translation_sources] -lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease' -lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES") -qt_env.NoClean(translation_sources) -qt_env.Precious(translation_sources) +po_sources = [f"#selfdrive/ui/translations/app_{l}.po" for l in languages.values()] +po_sources = [src for src in po_sources if os.path.exists(File(src).abspath)] +mo_targets = [src.replace(".po", ".mo") for src in po_sources] +mo_build = [] +for src, tgt in zip(po_sources, mo_targets): + mo_build.append(qt_env.Command(tgt, src, "msgfmt -o $TARGET $SOURCE")) +mo_alias = qt_env.Alias('mo', mo_build) +qt_env.AlwaysBuild(mo_alias) -# create qrc file for compiled translations to include with assets -translations_assets_src = "#selfdrive/assets/translations_assets.qrc" -with open(File(translations_assets_src).abspath, 'w') as f: - f.write('\n\n') - f.write('\n'.join([f'../ui/translations/{l}.qm' for l in languages.values()])) - f.write('\n\n') - -# build assets -assets = "#selfdrive/assets/assets.cc" -assets_src = "#selfdrive/assets/assets.qrc" -qt_env.Command(assets, [assets_src, translations_assets_src], f"rcc $SOURCES -o $TARGET") -qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, translations_assets_src, "#selfdrive/assets/assets.o"]) + [lrelease]) -asset_obj = qt_env.Object("assets", assets) - -# build main UI -qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs) if GetOption('extras'): - qt_src.remove("main.cc") # replaced by test_runner - qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs) + base_libs = [common, messaging, visionipc, transformations, + 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] - # build installers - if arch != "Darwin": - raylib_env = env.Clone() - raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] - raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') + if arch == 'larch64': + base_libs.append('EGL') - raylib_libs = common + ["raylib"] - if arch == "larch64": - raylib_libs += ["GLESv2", "EGL", "gbm", "drm"] - else: - raylib_libs += ["GL"] + if arch == "Darwin": + del base_libs[base_libs.index('OpenCL')] + qt_env['FRAMEWORKS'] += ['OpenCL'] - release = "release3" - installers = [ - ("openpilot", release), - ("openpilot_test", f"{release}-staging"), - ("openpilot_nightly", "nightly"), - ("openpilot_internal", "nightly-dev"), - ] + # FIXME: remove this once we're on 5.15 (24.04) + qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] - cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh", - "ld -r -b binary -o $TARGET $SOURCE") - inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf", - "ld -r -b binary -o $TARGET $SOURCE") - for name, branch in installers: - d = {'BRANCH': f"'\"{branch}\"'"} - if "internal" in name: - d['INTERNAL'] = "1" + qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs) + widgets_src = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc", + "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", + "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", + "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", + "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] - obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) - f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) - # keep installers small - assert f[0].get_size() < 1900*1e3, f[0].get_size() + widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs) + Export('widgets') + qt_libs = [widgets, qt_util] + base_libs + + qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc", + "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", + "qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", + "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc", + "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc", + "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] + + # build translation files + translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.values()] + translation_targets = [src.replace(".ts", ".qm") for src in translation_sources] + lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease' + + lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES") + qt_env.NoClean(translation_sources) + qt_env.Precious(translation_sources) + + # create qrc file for compiled translations to include with assets + translations_assets_src = "#selfdrive/assets/translations_assets.qrc" + with open(File(translations_assets_src).abspath, 'w') as f: + f.write('\n\n') + f.write('\n'.join([f'../ui/translations/{l}.qm' for l in languages.values()])) + f.write('\n\n') + + # build assets + assets = "#selfdrive/assets/assets.cc" + assets_src = "#selfdrive/assets/assets.qrc" + qt_env.Command(assets, [assets_src, translations_assets_src], f"rcc $SOURCES -o $TARGET") + qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, translations_assets_src, "#selfdrive/assets/assets.o"]) + [lrelease]) + asset_obj = qt_env.Object("assets", assets) + + # build main UI + qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs) + if GetOption('extras'): + qt_src.remove("main.cc") # replaced by test_runner + qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs) + + # build installers + if arch != "Darwin": + raylib_env = env.Clone() + raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] + raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') + + raylib_libs = common + ["raylib"] + if arch == "larch64": + raylib_libs += ["GLESv2", "EGL", "gbm", "drm"] + else: + raylib_libs += ["GL"] + + release = "release3" + installers = [ + ("openpilot", release), + ("openpilot_test", f"{release}-staging"), + ("openpilot_nightly", "nightly"), + ("openpilot_internal", "nightly-dev"), + ] + + cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh", + "ld -r -b binary -o $TARGET $SOURCE") + inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf", + "ld -r -b binary -o $TARGET $SOURCE") + for name, branch in installers: + d = {'BRANCH': f"'\"{branch}\"'"} + if "internal" in name: + d['INTERNAL'] = "1" + + obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) + f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) + # keep installers small + assert f[0].get_size() < 1900*1e3, f[0].get_size() diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index 91268960cb..4b8f087968 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -6,23 +6,24 @@ from openpilot.system.ui.widgets.list_view import toggle_item from openpilot.system.ui.widgets.scroller import Scroller from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.lib.application import gui_app -from openpilot.system.ui.lib.multilang import tr +from openpilot.system.ui.lib.multilang import tr, tr_noop from openpilot.system.ui.widgets import DialogResult # Description constants DESCRIPTIONS = { - 'enable_adb': tr( + 'enable_adb': tr_noop( "ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " + "See https://docs.comma.ai/how-to/connect-to-comma for more info." ), - 'ssh_key': tr( + 'ssh_key': tr_noop( "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " + "other than your own. A comma employee will NEVER ask you to add their GitHub username." ), - 'alpha_longitudinal': tr( + 'alpha_longitudinal': tr_noop( "WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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." + "Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. " + + "Changing this setting will restart openpilot if the car is powered on." ), } @@ -35,8 +36,8 @@ class DeveloperLayout(Widget): # Build items and keep references for callbacks/state updates self._adb_toggle = toggle_item( - tr("Enable ADB"), - description=DESCRIPTIONS["enable_adb"], + lambda: tr("Enable ADB"), + description=lambda: DESCRIPTIONS["enable_adb"], initial_state=self._params.get_bool("AdbEnabled"), callback=self._on_enable_adb, enabled=ui_state.is_offroad, @@ -44,15 +45,15 @@ class DeveloperLayout(Widget): # SSH enable toggle + SSH key management self._ssh_toggle = toggle_item( - tr("Enable SSH"), + lambda: tr("Enable SSH"), description="", initial_state=self._params.get_bool("SshEnabled"), callback=self._on_enable_ssh, ) - self._ssh_keys = ssh_key_item("SSH Keys", description=DESCRIPTIONS["ssh_key"]) + self._ssh_keys = ssh_key_item(lambda: tr("SSH Keys"), description=lambda: DESCRIPTIONS["ssh_key"]) self._joystick_toggle = toggle_item( - tr("Joystick Debug Mode"), + lambda: tr("Joystick Debug Mode"), description="", initial_state=self._params.get_bool("JoystickDebugMode"), callback=self._on_joystick_debug_mode, @@ -60,22 +61,20 @@ class DeveloperLayout(Widget): ) self._long_maneuver_toggle = toggle_item( - tr("Longitudinal Maneuver Mode"), + lambda: tr("Longitudinal Maneuver Mode"), description="", initial_state=self._params.get_bool("LongitudinalManeuverMode"), callback=self._on_long_maneuver_mode, ) self._alpha_long_toggle = toggle_item( - tr("openpilot Longitudinal Control (Alpha)"), - description=DESCRIPTIONS["alpha_longitudinal"], + lambda: tr("openpilot Longitudinal Control (Alpha)"), + description=lambda: DESCRIPTIONS["alpha_longitudinal"], initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), callback=self._on_alpha_long_enabled, enabled=lambda: not ui_state.engaged, ) - self._alpha_long_toggle.set_description(self._alpha_long_toggle.description + " Changing this setting will restart openpilot if the car is powered on.") - items = [ self._adb_toggle, self._ssh_toggle, diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index e9bdf59e26..3f762a8842 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -1,5 +1,4 @@ import os -import json import math from cereal import messaging, log @@ -12,7 +11,7 @@ from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app -from openpilot.system.ui.lib.multilang import tr +from openpilot.system.ui.lib.multilang import multilang, tr, tr_noop from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog from openpilot.system.ui.widgets.html_render import HtmlModal @@ -22,10 +21,10 @@ from openpilot.system.ui.widgets.scroller import Scroller # Description constants DESCRIPTIONS = { - 'pair_device': tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."), - 'driver_camera': tr("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)"), - 'reset_calibration': tr("openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down."), - 'review_guide': tr("Review the rules, features, and limitations of openpilot"), + 'pair_device': tr_noop("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."), + 'driver_camera': tr_noop("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)"), + 'reset_calibration': tr_noop("openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down."), + 'review_guide': tr_noop("Review the rules, features, and limitations of openpilot"), } @@ -46,27 +45,27 @@ class DeviceLayout(Widget): ui_state.add_offroad_transition_callback(self._offroad_transition) def _initialize_items(self): - dongle_id = self._params.get("DongleId") or tr("N/A") - serial = self._params.get("HardwareSerial") or tr("N/A") - - self._pair_device_btn = button_item(tr("Pair Device"), tr("PAIR"), DESCRIPTIONS['pair_device'], callback=self._pair_device) + self._pair_device_btn = button_item(lambda: tr("Pair Device"), lambda: tr("PAIR"), lambda: tr(DESCRIPTIONS['pair_device']), callback=self._pair_device) self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired()) - self._reset_calib_btn = button_item(tr("Reset Calibration"), tr("RESET"), DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt) + self._reset_calib_btn = button_item(lambda: tr("Reset Calibration"), lambda: tr("RESET"), lambda: tr(DESCRIPTIONS['reset_calibration']), + callback=self._reset_calibration_prompt) self._reset_calib_btn.set_description_opened_callback(self._update_calib_description) - self._power_off_btn = dual_button_item(tr("Reboot"), tr("Power Off"), left_callback=self._reboot_prompt, right_callback=self._power_off_prompt) + self._power_off_btn = dual_button_item(lambda: tr("Reboot"), lambda: tr("Power Off"), + left_callback=self._reboot_prompt, right_callback=self._power_off_prompt) items = [ - text_item(tr("Dongle ID"), dongle_id), - text_item(tr("Serial"), serial), + text_item(lambda: tr("Dongle ID"), self._params.get("DongleId") or (lambda: tr("N/A"))), + text_item(lambda: tr("Serial"), self._params.get("HardwareSerial") or (lambda: tr("N/A"))), self._pair_device_btn, - button_item(tr("Driver Camera"), tr("PREVIEW"), DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), + button_item(lambda: tr("Driver Camera"), lambda: tr("PREVIEW"), lambda: tr(DESCRIPTIONS['driver_camera']), + callback=self._show_driver_camera, enabled=ui_state.is_offroad), self._reset_calib_btn, - button_item(tr("Review Training Guide"), tr("REVIEW"), DESCRIPTIONS['review_guide'], self._on_review_training_guide, enabled=ui_state.is_offroad), - regulatory_btn := button_item(tr("Regulatory"), tr("VIEW"), callback=self._on_regulatory, enabled=ui_state.is_offroad), - # TODO: implement multilang - # button_item(tr("Change Language"), tr("CHANGE"), callback=self._show_language_selection, enabled=ui_state.is_offroad), + button_item(lambda: tr("Review Training Guide"), lambda: tr("REVIEW"), lambda: tr(DESCRIPTIONS['review_guide']), + self._on_review_training_guide, enabled=ui_state.is_offroad), + regulatory_btn := button_item(lambda: tr("Regulatory"), lambda: tr("VIEW"), callback=self._on_regulatory, enabled=ui_state.is_offroad), + button_item(lambda: tr("Change Language"), lambda: tr("CHANGE"), callback=self._show_language_dialog), self._power_off_btn, ] regulatory_btn.set_visible(TICI) @@ -81,23 +80,16 @@ class DeviceLayout(Widget): def _render(self, rect): self._scroller.render(rect) - def _show_language_selection(self): - try: - languages_file = os.path.join(BASEDIR, "selfdrive/ui/translations/languages.json") - with open(languages_file, encoding='utf-8') as f: - languages = json.load(f) + def _show_language_dialog(self): + def handle_language_selection(result: int): + if result == 1 and self._select_language_dialog: + selected_language = multilang.languages[self._select_language_dialog.selection] + multilang.change_language(selected_language) + self._update_calib_description() + self._select_language_dialog = None - self._select_language_dialog = MultiOptionDialog("Select a language", languages) - gui_app.set_modal_overlay(self._select_language_dialog, callback=self._handle_language_selection) - except FileNotFoundError: - pass - - def _handle_language_selection(self, result: int): - if result == 1 and self._select_language_dialog: - selected_language = self._select_language_dialog.selection - self._params.put("LanguageSetting", selected_language) - - self._select_language_dialog = None + self._select_language_dialog = MultiOptionDialog(tr("Select a language"), multilang.languages, multilang.codes[multilang.language]) + gui_app.set_modal_overlay(self._select_language_dialog, callback=handle_language_selection) def _show_driver_camera(self): if not self._driver_camera: @@ -127,7 +119,7 @@ class DeviceLayout(Widget): gui_app.set_modal_overlay(dialog, callback=reset_calibration) def _update_calib_description(self): - desc = DESCRIPTIONS['reset_calibration'] + desc = tr(DESCRIPTIONS['reset_calibration']) calib_bytes = self._params.get("CalibrationParams") if calib_bytes: diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py index f4d70eaa47..bbd4aef532 100644 --- a/selfdrive/ui/layouts/settings/firehose.py +++ b/selfdrive/ui/layouts/settings/firehose.py @@ -8,20 +8,20 @@ from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE -from openpilot.system.ui.lib.multilang import tr, trn +from openpilot.system.ui.lib.multilang import tr, trn, tr_noop from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.selfdrive.ui.lib.api_helpers import get_token -TITLE = tr("Firehose Mode") -DESCRIPTION = tr( +TITLE = tr_noop("Firehose Mode") +DESCRIPTION = tr_noop( "openpilot learns to drive by watching humans, like you, drive.\n\n" + "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." ) -INSTRUCTIONS = tr( +INSTRUCTIONS = tr_noop( "For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n" + "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n\n" + "Frequently Asked Questions\n\n" @@ -86,14 +86,15 @@ class FirehoseLayout(Widget): w = int(rect.width - 80) # Title (centered) + title_text = tr(TITLE) # live translate title_font = gui_app.font(FontWeight.MEDIUM) - text_width = measure_text_cached(title_font, TITLE, 100).x + text_width = measure_text_cached(title_font, title_text, 100).x title_x = rect.x + (rect.width - text_width) / 2 - rl.draw_text_ex(title_font, TITLE, rl.Vector2(title_x, y), 100, 0, rl.WHITE) + rl.draw_text_ex(title_font, title_text, rl.Vector2(title_x, y), 100, 0, rl.WHITE) y += 200 # Description - y = self._draw_wrapped_text(x, y, w, DESCRIPTION, gui_app.font(FontWeight.NORMAL), 45, rl.WHITE) + y = self._draw_wrapped_text(x, y, w, tr(DESCRIPTION), gui_app.font(FontWeight.NORMAL), 45, rl.WHITE) y += 40 + 20 # Separator @@ -117,7 +118,7 @@ class FirehoseLayout(Widget): y += 30 + 20 # Instructions - y = self._draw_wrapped_text(x, y, w, INSTRUCTIONS, gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY) + y = self._draw_wrapped_text(x, y, w, tr(INSTRUCTIONS), gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY) # bottom margin + remove effect of scroll offset return int(round(y - self.scroll_panel.offset + 40)) diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index 7f431d3a77..f13383fa55 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -8,7 +8,7 @@ from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos -from openpilot.system.ui.lib.multilang import tr +from openpilot.system.ui.lib.multilang import tr, tr_noop from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wifi_manager import WifiManager from openpilot.system.ui.widgets import Widget @@ -59,12 +59,12 @@ class SettingsLayout(Widget): wifi_manager.set_active(False) self._panels = { - PanelType.DEVICE: PanelInfo(tr("Device"), DeviceLayout()), - PanelType.NETWORK: PanelInfo(tr("Network"), NetworkUI(wifi_manager)), - PanelType.TOGGLES: PanelInfo(tr("Toggles"), TogglesLayout()), - PanelType.SOFTWARE: PanelInfo(tr("Software"), SoftwareLayout()), - PanelType.FIREHOSE: PanelInfo(tr("Firehose"), FirehoseLayout()), - PanelType.DEVELOPER: PanelInfo(tr("Developer"), DeveloperLayout()), + PanelType.DEVICE: PanelInfo(tr_noop("Device"), DeviceLayout()), + PanelType.NETWORK: PanelInfo(tr_noop("Network"), NetworkUI(wifi_manager)), + PanelType.TOGGLES: PanelInfo(tr_noop("Toggles"), TogglesLayout()), + PanelType.SOFTWARE: PanelInfo(tr_noop("Software"), SoftwareLayout()), + PanelType.FIREHOSE: PanelInfo(tr_noop("Firehose"), FirehoseLayout()), + PanelType.DEVELOPER: PanelInfo(tr_noop("Developer"), DeveloperLayout()), } self._font_medium = gui_app.font(FontWeight.MEDIUM) @@ -116,11 +116,12 @@ class SettingsLayout(Widget): is_selected = panel_type == self._current_panel text_color = TEXT_SELECTED if is_selected else TEXT_NORMAL # Draw button text (right-aligned) - text_size = measure_text_cached(self._font_medium, panel_info.name, 65) + panel_name = tr(panel_info.name) + text_size = measure_text_cached(self._font_medium, panel_name, 65) text_pos = rl.Vector2( button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2 ) - rl.draw_text_ex(self._font_medium, panel_info.name, text_pos, 65, 0, text_color) + rl.draw_text_ex(self._font_medium, panel_name, text_pos, 65, 0, text_color) # Store button rect for click detection panel_info.button_rect = button_rect diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 1b78b5c9e8..73c8946f55 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -45,12 +45,12 @@ class SoftwareLayout(Widget): def __init__(self): super().__init__() - self._onroad_label = ListItem(title=tr("Updates are only downloaded while the car is off.")) - self._version_item = text_item(tr("Current Version"), ui_state.params.get("UpdaterCurrentDescription") or "") - self._download_btn = button_item(tr("Download"), tr("CHECK"), callback=self._on_download_update) + self._onroad_label = ListItem(lambda: tr("Updates are only downloaded while the car is off.")) + self._version_item = text_item(lambda: tr("Current Version"), ui_state.params.get("UpdaterCurrentDescription") or "") + self._download_btn = button_item(lambda: tr("Download"), lambda: tr("CHECK"), callback=self._on_download_update) # Install button is initially hidden - self._install_btn = button_item(tr("Install Update"), tr("INSTALL"), callback=self._on_install_update) + self._install_btn = button_item(lambda: tr("Install Update"), lambda: tr("INSTALL"), callback=self._on_install_update) self._install_btn.set_visible(False) self._select_branch_dialog: MultiOptionDialog | None = None @@ -70,7 +70,7 @@ class SoftwareLayout(Widget): self._download_btn, self._install_btn, self._target_branch_btn, - button_item("Uninstall", tr("UNINSTALL"), callback=self._on_uninstall), + button_item(lambda: tr("Uninstall"), lambda: tr("UNINSTALL"), callback=self._on_uninstall), ] return items diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 1e4d5c6c85..e4441edec3 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -5,7 +5,7 @@ from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_i from openpilot.system.ui.widgets.scroller import Scroller from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.lib.application import gui_app -from openpilot.system.ui.lib.multilang import tr +from openpilot.system.ui.lib.multilang import tr, tr_noop from openpilot.system.ui.widgets import DialogResult from openpilot.selfdrive.ui.ui_state import ui_state @@ -13,24 +13,24 @@ PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants # Description constants DESCRIPTIONS = { - "OpenpilotEnabledToggle": tr( + "OpenpilotEnabledToggle": tr_noop( "Use the openpilot system for adaptive cruise control and lane keep driver assistance. " + "Your attention is required at all times to use this feature." ), - "DisengageOnAccelerator": tr("When enabled, pressing the accelerator pedal will disengage openpilot."), - "LongitudinalPersonality": tr( + "DisengageOnAccelerator": tr_noop("When enabled, pressing the accelerator pedal will disengage openpilot."), + "LongitudinalPersonality": tr_noop( "Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " + "In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " + "your steering wheel distance button." ), - "IsLdwEnabled": tr( + "IsLdwEnabled": tr_noop( "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line " + "without a turn signal activated while driving over 31 mph (50 km/h)." ), - "AlwaysOnDM": tr("Enable driver monitoring even when openpilot is not engaged."), - 'RecordFront': tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), - "IsMetric": tr("Display speed in km/h instead of mph."), - "RecordAudio": tr("Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect."), + "AlwaysOnDM": tr_noop("Enable driver monitoring even when openpilot is not engaged."), + 'RecordFront': tr_noop("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), + "IsMetric": tr_noop("Display speed in km/h instead of mph."), + "RecordAudio": tr_noop("Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect."), } @@ -43,49 +43,49 @@ class TogglesLayout(Widget): # param, title, desc, icon, needs_restart self._toggle_defs = { "OpenpilotEnabledToggle": ( - tr("Enable openpilot"), + lambda: tr("Enable openpilot"), DESCRIPTIONS["OpenpilotEnabledToggle"], "chffr_wheel.png", True, ), "ExperimentalMode": ( - tr("Experimental Mode"), + lambda: tr("Experimental Mode"), "", "experimental_white.png", False, ), "DisengageOnAccelerator": ( - tr("Disengage on Accelerator Pedal"), + lambda: tr("Disengage on Accelerator Pedal"), DESCRIPTIONS["DisengageOnAccelerator"], "disengage_on_accelerator.png", False, ), "IsLdwEnabled": ( - tr("Enable Lane Departure Warnings"), + lambda: tr("Enable Lane Departure Warnings"), DESCRIPTIONS["IsLdwEnabled"], "warning.png", False, ), "AlwaysOnDM": ( - tr("Always-On Driver Monitoring"), + lambda: tr("Always-On Driver Monitoring"), DESCRIPTIONS["AlwaysOnDM"], "monitoring.png", False, ), "RecordFront": ( - tr("Record and Upload Driver Camera"), + lambda: tr("Record and Upload Driver Camera"), DESCRIPTIONS["RecordFront"], "monitoring.png", True, ), "RecordAudio": ( - tr("Record and Upload Microphone Audio"), + lambda: tr("Record and Upload Microphone Audio"), DESCRIPTIONS["RecordAudio"], "microphone.png", True, ), "IsMetric": ( - tr("Use Metric System"), + lambda: tr("Use Metric System"), DESCRIPTIONS["IsMetric"], "metric.png", False, @@ -93,9 +93,9 @@ class TogglesLayout(Widget): } self._long_personality_setting = multiple_button_item( - tr("Driving Personality"), + lambda: tr("Driving Personality"), DESCRIPTIONS["LongitudinalPersonality"], - buttons=[tr("Aggressive"), tr("Standard"), tr("Relaxed")], + buttons=[lambda: tr("Aggressive"), lambda: tr("Standard"), lambda: tr("Relaxed")], button_width=255, callback=self._set_longitudinal_personality, selected_index=self._params.get("LongitudinalPersonality", return_default=True), @@ -119,8 +119,11 @@ class TogglesLayout(Widget): locked = False toggle.action_item.set_enabled(not locked) + # Make description callable for live translation + additional_desc = "" if needs_restart and not locked: - toggle.set_description(toggle.description + tr(" Changing this setting will restart openpilot if the car is powered on.")) + additional_desc = tr("Changing this setting will restart openpilot if the car is powered on.") + toggle.set_description(lambda og_desc=toggle.description, add_desc=additional_desc: tr(og_desc) + (" " + tr(add_desc) if add_desc else "")) # track for engaged state updates if locked: diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 48b577ea59..d468442b15 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -5,7 +5,7 @@ from collections.abc import Callable from cereal import log from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, FONT_SCALE -from openpilot.system.ui.lib.multilang import tr +from openpilot.system.ui.lib.multilang import tr, tr_noop from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget @@ -40,13 +40,13 @@ class Colors: NETWORK_TYPES = { - NetworkType.none: tr("--"), - NetworkType.wifi: tr("Wi-Fi"), - NetworkType.ethernet: tr("ETH"), - NetworkType.cell2G: tr("2G"), - NetworkType.cell3G: tr("3G"), - NetworkType.cell4G: tr("LTE"), - NetworkType.cell5G: tr("5G"), + NetworkType.none: tr_noop("--"), + NetworkType.wifi: tr_noop("Wi-Fi"), + NetworkType.ethernet: tr_noop("ETH"), + NetworkType.cell2G: tr_noop("2G"), + NetworkType.cell3G: tr_noop("3G"), + NetworkType.cell4G: tr_noop("LTE"), + NetworkType.cell5G: tr_noop("5G"), } @@ -68,9 +68,9 @@ class Sidebar(Widget): self._net_type = NETWORK_TYPES.get(NetworkType.none) self._net_strength = 0 - self._temp_status = MetricData(tr("TEMP"), tr("GOOD"), Colors.GOOD) - self._panda_status = MetricData(tr("VEHICLE"), tr("ONLINE"), Colors.GOOD) - self._connect_status = MetricData(tr("CONNECT"), tr("OFFLINE"), Colors.WARNING) + self._temp_status = MetricData(tr_noop("TEMP"), tr_noop("GOOD"), Colors.GOOD) + self._panda_status = MetricData(tr_noop("VEHICLE"), tr_noop("ONLINE"), Colors.GOOD) + self._connect_status = MetricData(tr_noop("CONNECT"), tr_noop("OFFLINE"), Colors.WARNING) self._recording_audio = False self._home_img = gui_app.texture("images/button_home.png", HOME_BTN.width, HOME_BTN.height) @@ -114,7 +114,7 @@ class Sidebar(Widget): self._update_panda_status() def _update_network_status(self, device_state): - self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, tr("Unknown")) + self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, tr_noop("Unknown")) strength = device_state.networkStrength self._net_strength = max(0, min(5, strength.raw + 1)) if strength > 0 else 0 @@ -122,26 +122,26 @@ class Sidebar(Widget): thermal_status = device_state.thermalStatus if thermal_status == ThermalStatus.green: - self._temp_status.update(tr("TEMP"), tr("GOOD"), Colors.GOOD) + self._temp_status.update(tr_noop("TEMP"), tr_noop("GOOD"), Colors.GOOD) elif thermal_status == ThermalStatus.yellow: - self._temp_status.update(tr("TEMP"), tr("OK"), Colors.WARNING) + self._temp_status.update(tr_noop("TEMP"), tr_noop("OK"), Colors.WARNING) else: - self._temp_status.update(tr("TEMP"), tr("HIGH"), Colors.DANGER) + self._temp_status.update(tr_noop("TEMP"), tr_noop("HIGH"), Colors.DANGER) def _update_connection_status(self, device_state): last_ping = device_state.lastAthenaPingTime if last_ping == 0: - self._connect_status.update(tr("CONNECT"), tr("OFFLINE"), Colors.WARNING) + self._connect_status.update(tr_noop("CONNECT"), tr_noop("OFFLINE"), Colors.WARNING) elif time.monotonic_ns() - last_ping < 80_000_000_000: # 80 seconds in nanoseconds - self._connect_status.update(tr("CONNECT"), tr("ONLINE"), Colors.GOOD) + self._connect_status.update(tr_noop("CONNECT"), tr_noop("ONLINE"), Colors.GOOD) else: - self._connect_status.update(tr("CONNECT"), tr("ERROR"), Colors.DANGER) + self._connect_status.update(tr_noop("CONNECT"), tr_noop("ERROR"), Colors.DANGER) def _update_panda_status(self): if ui_state.panda_type == log.PandaState.PandaType.unknown: - self._panda_status.update(tr("NO"), tr("PANDA"), Colors.DANGER) + self._panda_status.update(tr_noop("NO"), tr_noop("PANDA"), Colors.DANGER) else: - self._panda_status.update(tr("VEHICLE"), tr("ONLINE"), Colors.GOOD) + self._panda_status.update(tr_noop("VEHICLE"), tr_noop("ONLINE"), Colors.GOOD) def _handle_mouse_release(self, mouse_pos: MousePos): if rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN): @@ -197,7 +197,7 @@ class Sidebar(Widget): # Network type text text_y = rect.y + 247 text_pos = rl.Vector2(rect.x + 58, text_y) - rl.draw_text_ex(self._font_regular, self._net_type, text_pos, FONT_SIZE, 0, Colors.WHITE) + rl.draw_text_ex(self._font_regular, tr(self._net_type), text_pos, FONT_SIZE, 0, Colors.WHITE) def _draw_metrics(self, rect: rl.Rectangle): metrics = [(self._temp_status, 338), (self._panda_status, 496), (self._connect_status, 654)] @@ -217,7 +217,7 @@ class Sidebar(Widget): rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.3, 10, 2, Colors.METRIC_BORDER) # Draw label and value - labels = [metric.label, metric.value] + labels = [tr(metric.label), tr(metric.value)] text_y = metric_rect.y + (metric_rect.height / 2 - len(labels) * FONT_SIZE * FONT_SCALE) for text in labels: text_size = measure_text_cached(self._font_bold, text, FONT_SIZE) diff --git a/selfdrive/ui/translations/app.pot b/selfdrive/ui/translations/app.pot new file mode 100644 index 0000000000..b68a197870 --- /dev/null +++ b/selfdrive/ui/translations/app.pot @@ -0,0 +1,1134 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#, python-format +msgid "Select" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#, python-format +msgid "Error" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#, python-format +msgid "never" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#, python-format +msgid "now" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Current Version" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Download" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#, python-format +msgid "CHECK" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#, python-format +msgid "Install Update" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#, python-format +msgid "INSTALL" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Uninstall" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#, python-format +msgid "UNINSTALL" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#, python-format +msgid "failed to check for update" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#, python-format +msgid "update available" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "DOWNLOAD" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#, python-format +msgid "up to date, last checked {}" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#, python-format +msgid "up to date, last checked never" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Reboot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Power Off" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Reset" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "down" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "up" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "left" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "right" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#, python-format +msgid "Disengage to Reboot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#, python-format +msgid "Disengage to Power Off" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "" diff --git a/selfdrive/ui/translations/app_de.po b/selfdrive/ui/translations/app_de.po new file mode 100644 index 0000000000..34b3d8321d --- /dev/null +++ b/selfdrive/ui/translations/app_de.po @@ -0,0 +1,1225 @@ +# German translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"PO-Revision-Date: 2025-10-20 16:35-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " Die Lenkmoment-Reaktionskalibrierung ist abgeschlossen." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " Die Lenkmoment-Reaktionskalibrierung ist zu {}% abgeschlossen." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " Ihr Gerät ist um {:.1f}° {} und {:.1f}° {} ausgerichtet." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "1 Jahr Fahrtdatenspeicherung" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "24/7 LTE‑Verbindung" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"WARNUNG: Die Längsregelung von openpilot befindet sich für dieses " +"Fahrzeug in der Alpha-Phase und deaktiviert das automatische Notbremssystem " +"(AEB).

Auf diesem Fahrzeug verwendet openpilot standardmäßig den " +"integrierten ACC statt der openpilot-Längsregelung. Aktivieren Sie dies, um " +"auf die openpilot-Längsregelung umzuschalten. Das Aktivieren des " +"Experimentalmodus wird empfohlen, wenn Sie die openpilot-Längsregelung " +"(Alpha) aktivieren." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "

Kalibrierung der Lenkverzögerung abgeschlossen." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "

Kalibrierung der Lenkverzögerung zu {}% abgeschlossen." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "AKTIV" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB (Android Debug Bridge) ermöglicht die Verbindung mit Ihrem Gerät über " +"USB oder über das Netzwerk. Siehe https://docs.comma.ai/how-to/connect-to-" +"comma für weitere Informationen." + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "HINZUFÜGEN" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "APN‑Einstellung" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "Übermäßige Betätigung bestätigen" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "Erweitert" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "Aggressiv" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "Zustimmen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "Immer aktive Fahrerüberwachung" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"Eine Alpha-Version der openpilot-Längsregelung kann zusammen mit dem " +"Experimentalmodus auf Nicht-Release-Zweigen getestet werden." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "Sind Sie sicher, dass Sie ausschalten möchten?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "Sind Sie sicher, dass Sie neu starten möchten?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "Sind Sie sicher, dass Sie die Kalibrierung zurücksetzen möchten?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "Sind Sie sicher, dass Sie deinstallieren möchten?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "Zurück" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "Werden Sie comma prime Mitglied auf connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "" +"Fügen Sie connect.comma.ai Ihrem Startbildschirm hinzu, um es wie eine App " +"zu verwenden" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "ÄNDERN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#, python-format +msgid "CHECK" +msgstr "PRÜFEN" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "CHILL‑MODUS AKTIV" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "VERBINDUNG" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "VERBINDUNG" + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "Abbrechen" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "Getaktete Mobilfunkverbindung" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "Sprache ändern" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "" +" Durch Ändern dieser Einstellung wird openpilot neu gestartet, wenn das Auto " +"eingeschaltet ist." + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "Klicken Sie auf \"add new device\" und scannen Sie den QR‑Code rechts" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "Schließen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Current Version" +msgstr "Aktuelle Version" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "DOWNLOAD" +msgstr "HERUNTERLADEN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "Ablehnen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "Ablehnen, openpilot deinstallieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "Entwickler" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "Gerät" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "Beim Gaspedal deaktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#, python-format +msgid "Disengage to Power Off" +msgstr "Zum Ausschalten deaktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#, python-format +msgid "Disengage to Reboot" +msgstr "Zum Neustart deaktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "Zum Zurücksetzen der Kalibrierung deaktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "Geschwindigkeit in km/h statt mph anzeigen." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "Dongle-ID" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Download" +msgstr "Herunterladen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "Fahrerkamera" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "Fahrstil" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "BEARBEITEN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "FEHLER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "EXPERIMENTALMODUS AKTIV" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "Aktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "ADB aktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "Spurverlassenswarnungen aktivieren" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "openpilot aktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "SSH aktivieren" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "Spurverlassenswarnungen aktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "Fahrerüberwachung auch aktivieren, wenn openpilot nicht aktiv ist." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "openpilot aktivieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" +"Den Schalter für die openpilot-Längsregelung (Alpha) aktivieren, um den " +"Experimentalmodus zu erlauben." + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "APN eingeben" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "SSID eingeben" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "Neues Tethering‑Passwort eingeben" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "Passwort eingeben" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "Geben Sie Ihren GitHub‑Benutzernamen ein" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#, python-format +msgid "Error" +msgstr "Fehler" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "Experimentalmodus" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"Der Experimentalmodus ist derzeit auf diesem Fahrzeug nicht verfügbar, da " +"der serienmäßige ACC für die Längsregelung verwendet wird." + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "WIRD VERGESSEN..." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "Einrichtung abschließen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Firehose‑Modus" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"Für maximale Wirksamkeit bringen Sie Ihr Gerät regelmäßig ins Haus und " +"verbinden es wöchentlich mit einem guten USB‑C‑Adapter und WLAN.\n" +"\n" +"Der Firehose‑Modus kann auch während der Fahrt funktionieren, wenn eine " +"Verbindung zu einem Hotspot oder einer unbegrenzten SIM besteht.\n" +"\n" +"\n" +"Häufig gestellte Fragen\n" +"\n" +"Spielt es eine Rolle, wie oder wo ich fahre? Nein, fahren Sie einfach wie " +"gewöhnlich.\n" +"\n" +"Werden alle meine Segmente im Firehose‑Modus abgeholt? Nein, wir ziehen " +"selektiv eine Teilmenge Ihrer Segmente.\n" +"\n" +"Was ist ein guter USB‑C‑Adapter? Jeder schnelle Telefon‑ oder Laptoplader " +"sollte ausreichen.\n" +"\n" +"Spielt es eine Rolle, welche Software ich verwende? Ja, nur " +"Upstream‑openpilot (und bestimmte Forks) können für das Training verwendet " +"werden." + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "Vergessen" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "WLAN‑Netz „{}“ vergessen?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "GUT" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "Gehen Sie auf Ihrem Telefon zu https://connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "HOCH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "Netzwerk" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "INAKTIV: Mit einem unlimitierten Netzwerk verbinden" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#, python-format +msgid "INSTALL" +msgstr "INSTALLIEREN" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "IP‑Adresse" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#, python-format +msgid "Install Update" +msgstr "Update installieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "Joystick‑Debugmodus" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "LADEN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "Längsmanövermodus" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "MAX" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" +"Maximieren Sie Ihre Trainingsdaten‑Uploads, um die Fahrmodelle von openpilot " +"zu verbessern." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "k. A." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "KEIN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "Netzwerk" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "Keine SSH‑Schlüssel gefunden" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "Keine SSH‑Schlüssel für Benutzer '{username}' gefunden" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "Keine Versionshinweise verfügbar." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "OFFLINE" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "OK" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "ONLINE" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "Öffnen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "KOPPELN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "VORSCHAU" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "PRIME‑FUNKTIONEN:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "Gerät koppeln" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "Gerät koppeln" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "Koppeln Sie Ihr Gerät mit Ihrem comma‑Konto" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"Koppeln Sie Ihr Gerät mit comma connect (connect.comma.ai) und lösen Sie Ihr " +"comma‑prime‑Angebot ein." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "Bitte mit WLAN verbinden, um das erste Koppeln abzuschließen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Power Off" +msgstr "Ausschalten" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"Vorschau der Fahrer‑Kamera, um sicherzustellen, dass die Fahrerüberwachung " +"gute Sicht hat. (Fahrzeug muss ausgeschaltet sein)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "QR‑Code‑Fehler" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "ENTFERNEN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "ZURÜCKSETZEN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "ANSEHEN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Reboot" +msgstr "Neustart" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "Gerät neu starten" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "Neustarten und aktualisieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"Erhalten Sie Warnungen, um zurück in die Spur zu lenken, wenn Ihr Fahrzeug " +"ohne Blinker über eine erkannte Spurlinie driftet und über 31 mph (50 km/h) " +"fährt." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "Fahrerkamera aufzeichnen und hochladen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "Mikrofonton aufzeichnen und hochladen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"Mikrofonton während der Fahrt aufzeichnen und speichern. Die Audiospur wird " +"im Dashcam‑Video in comma connect enthalten sein." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "Vorschriften" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "Entspannt" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "Fernzugriff" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "Remote‑Schnappschüsse" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "Zeitüberschreitung bei der Anfrage" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Reset" +msgstr "Zurücksetzen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "Kalibrierung zurücksetzen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "Trainingsanleitung ansehen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "" +"Überprüfen Sie die Regeln, Funktionen und Einschränkungen von openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "SSH‑Schlüssel" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "WLAN‑Netzwerke werden gesucht..." + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#, python-format +msgid "Select" +msgstr "Auswählen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "Sprache auswählen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "Seriennummer" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "Update verschieben" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "Software" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "Standard" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"Standard wird empfohlen. Im aggressiven Modus folgt openpilot " +"vorausfahrenden Fahrzeugen näher und ist beim Gasgeben und Bremsen " +"aggressiver. Im entspannten Modus bleibt openpilot weiter entfernt. Bei " +"unterstützten Fahrzeugen können Sie mit der Abstandstaste am Lenkrad " +"zwischen diesen Profilen wechseln." + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "System reagiert nicht" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "SOFORT DIE KONTROLLE ÜBERNEHMEN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "TEMP" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "Tethering‑Passwort" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "Schalter" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#, python-format +msgid "UNINSTALL" +msgstr "DEINSTALLIEREN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "UPDATE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Uninstall" +msgstr "Deinstallieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "Unbekannt" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "Updates werden nur heruntergeladen, wenn das Auto aus ist." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "Jetzt abonnieren" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"Daten von der Fahrer‑Kamera hochladen und den Fahrerüberwachungs‑Algorithmus " +"verbessern." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "Metersystem verwenden" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"Verwenden Sie openpilot für adaptive Geschwindigkeitsregelung und " +"Spurhalteassistenz. Ihre Aufmerksamkeit ist jederzeit erforderlich, um diese " +"Funktion zu nutzen." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "FAHRZEUG" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "ANSEHEN" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "Warten auf Start" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"Warnung: Dies gewährt SSH‑Zugriff auf alle öffentlichen Schlüssel in Ihren " +"GitHub‑Einstellungen. Geben Sie niemals einen anderen GitHub‑Benutzernamen " +"als Ihren eigenen ein. Ein comma‑Mitarbeiter wird Sie NIEMALS bitten, seinen " +"GitHub‑Benutzernamen hinzuzufügen." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "Willkommen bei openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "Wenn aktiviert, deaktiviert das Drücken des Gaspedals openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "WLAN" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "Getaktetes WLAN‑Netzwerk" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "Falsches Passwort" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "" +"Sie müssen die Nutzungsbedingungen akzeptieren, um openpilot zu verwenden." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"Sie müssen die Nutzungsbedingungen akzeptieren, um openpilot zu verwenden. " +"Lesen Sie die aktuellen Bedingungen unter https://comma.ai/terms, bevor Sie " +"fortfahren." + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "Kamera startet" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "Standard" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "down" +msgstr "unten" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#, python-format +msgid "failed to check for update" +msgstr "Überprüfung auf Updates fehlgeschlagen" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "für „{}“" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "km/h" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "für automatische Konfiguration leer lassen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "left" +msgstr "links" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "getaktet" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "mph" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#, python-format +msgid "never" +msgstr "nie" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#, python-format +msgid "now" +msgstr "jetzt" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "openpilot Längsregelung (Alpha)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot nicht verfügbar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilot fährt standardmäßig im Chill‑Modus. Der Experimentalmodus " +"aktiviert Funktionen im Alpha‑Status, die für den Chill‑Modus noch nicht " +"bereit sind. Die experimentellen Funktionen sind unten aufgeführt:" +"

End-to‑End‑Längsregelung


Das Fahrmodell steuert Gas und " +"Bremse. openpilot fährt so, wie es einen Menschen einschätzt, einschließlich " +"Anhalten an roten Ampeln und Stoppschildern. Da das Modell die " +"Geschwindigkeit bestimmt, dient die eingestellte Geschwindigkeit nur als " +"Obergrenze. Dies ist eine Alpha‑Funktion; Fehler sind zu erwarten." +"

Neue Fahrvisualisierung


Die Visualisierung wechselt bei " +"niedriger Geschwindigkeit auf die nach vorn gerichtete Weitwinkelkamera, um " +"manche Kurven besser zu zeigen. Das Experimentalmodus‑Logo wird außerdem " +"oben rechts angezeigt." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +" Durch Ändern dieser Einstellung wird openpilot neu gestartet, wenn das Auto " +"eingeschaltet ist." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilot lernt das Fahren, indem es Menschen wie Sie beobachtet.\n" +"\n" +"Der Firehose‑Modus ermöglicht es Ihnen, Ihre Trainingsdaten‑Uploads zu " +"maximieren, um die Fahrmodelle von openpilot zu verbessern. Mehr Daten " +"bedeuten größere Modelle – und damit einen besseren Experimentalmodus." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "Die openpilot‑Längsregelung könnte in einem zukünftigen Update kommen." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" +"openpilot erfordert, dass das Gerät innerhalb von 4° nach links oder rechts " +"und innerhalb von 5° nach oben oder 9° nach unten montiert ist." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "right" +msgstr "rechts" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "unbegrenzt" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "up" +msgstr "oben" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#, python-format +msgid "up to date, last checked never" +msgstr "Aktuell, zuletzt geprüft: nie" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#, python-format +msgid "up to date, last checked {}" +msgstr "Aktuell, zuletzt geprüft: {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#, python-format +msgid "update available" +msgstr "Update verfügbar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} WARNUNG" +msgstr[1] "{} WARNUNGEN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "vor {} Tag" +msgstr[1] "vor {} Tagen" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "vor {} Stunde" +msgstr[1] "vor {} Stunden" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "vor {} Minute" +msgstr[1] "vor {} Minuten" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "{} Segment Ihrer Fahrten ist bisher im Trainingsdatensatz." +msgstr[1] "{} Segmente Ihrer Fahrten sind bisher im Trainingsdatensatz." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ ABONNIERT" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Firehose‑Modus 🔥" diff --git a/selfdrive/ui/translations/app_en.po b/selfdrive/ui/translations/app_en.po new file mode 100644 index 0000000000..55554e9d9c --- /dev/null +++ b/selfdrive/ui/translations/app_en.po @@ -0,0 +1,1211 @@ +# English translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"PO-Revision-Date: 2025-10-21 18:18-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " Steering torque response calibration is complete." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " Steering torque response calibration is {}% complete." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " Your device is pointed {:.1f}° {} and {:.1f}° {}." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "1 year of drive storage" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "24/7 LTE connectivity" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "

Steering lag calibration is complete." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "

Steering lag calibration is {}% complete." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "ACTIVE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "ADD" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "APN Setting" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "Acknowledge Excessive Actuation" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "Advanced" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "Aggressive" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "Agree" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "Always-On Driver Monitoring" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "Are you sure you want to power off?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "Are you sure you want to reboot?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "Are you sure you want to reset calibration?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "Are you sure you want to uninstall?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "Back" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "Become a comma prime member at connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "Bookmark connect.comma.ai to your home screen to use it like an app" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "CHANGE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#, python-format +msgid "CHECK" +msgstr "CHECK" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "CHILL MODE ON" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "CONNECT" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "CONNECTING..." + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "Cancel" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "Cellular Metered" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "Change Language" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "Changing this setting will restart openpilot if the car is powered on." + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "Click \"add new device\" and scan the QR code on the right" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "Close" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Current Version" +msgstr "Current Version" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "DOWNLOAD" +msgstr "DOWNLOAD" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "Decline" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "Decline, uninstall openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "Developer" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "Device" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "Disengage on Accelerator Pedal" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#, python-format +msgid "Disengage to Power Off" +msgstr "Disengage to Power Off" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#, python-format +msgid "Disengage to Reboot" +msgstr "Disengage to Reboot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "Disengage to Reset Calibration" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "Display speed in km/h instead of mph." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "Dongle ID" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Download" +msgstr "Download" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "Driver Camera" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "Driving Personality" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "EDIT" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "ERROR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "EXPERIMENTAL MODE ON" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "Enable" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "Enable ADB" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "Enable Lane Departure Warnings" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "Enable Roaming" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "Enable SSH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "Enable Tethering" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "Enable driver monitoring even when openpilot is not engaged." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "Enable openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "Enter APN" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "Enter SSID" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "Enter new tethering password" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "Enter password" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "Enter your GitHub username" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#, python-format +msgid "Error" +msgstr "Error" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "Experimental Mode" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "FORGETTING..." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "Finish Setup" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Firehose Mode" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "Forget" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "Forget Wi-Fi Network \"{}\"?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "GOOD" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "Go to https://connect.comma.ai on your phone" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "HIGH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "Hidden Network" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "INACTIVE: connect to an unmetered network" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#, python-format +msgid "INSTALL" +msgstr "INSTALL" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "IP Address" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#, python-format +msgid "Install Update" +msgstr "Install Update" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "Joystick Debug Mode" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "LOADING" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "Longitudinal Maneuver Mode" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "MAX" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" +"Maximize your training data uploads to improve openpilot's driving models." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "N/A" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "NO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "Network" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "No SSH keys found" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "No SSH keys found for user '{}'" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "No release notes available." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "OFFLINE" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "OK" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "ONLINE" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "Open" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "PAIR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "PREVIEW" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "PRIME FEATURES:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "Pair Device" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "Pair device" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "Pair your device to your comma account" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "Please connect to Wi-Fi to complete initial pairing" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Power Off" +msgstr "Power Off" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "Prevent large data uploads when on a metered Wi-Fi connection" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "Prevent large data uploads when on a metered cellular connection" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "QR Code Error" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "REMOVE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "RESET" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "REVIEW" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Reboot" +msgstr "Reboot" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "Reboot Device" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "Reboot and Update" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "Record and Upload Driver Camera" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "Record and Upload Microphone Audio" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "Regulatory" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "Relaxed" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "Remote access" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "Remote snapshots" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "Request timed out" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Reset" +msgstr "Reset" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "Reset Calibration" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "Review Training Guide" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "Review the rules, features, and limitations of openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "SSH Keys" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "Scanning Wi-Fi networks..." + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#, python-format +msgid "Select" +msgstr "Select" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "Select a language" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "Serial" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "Snooze Update" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "Software" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "Standard" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "System Unresponsive" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "TAKE CONTROL IMMEDIATELY" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "TEMP" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "Tethering Password" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "Toggles" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#, python-format +msgid "UNINSTALL" +msgstr "UNINSTALL" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "UPDATE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Uninstall" +msgstr "Uninstall" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "Unknown" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "Updates are only downloaded while the car is off." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "Upgrade Now" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "Use Metric System" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "VEHICLE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "VIEW" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "Waiting to start" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "Welcome to openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "When enabled, pressing the accelerator pedal will disengage openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi-Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "Wi-Fi Network Metered" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "Wrong password" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "You must accept the Terms and Conditions in order to use openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "camera starting" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "default" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "down" +msgstr "down" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#, python-format +msgid "failed to check for update" +msgstr "failed to check for update" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "for \"{}\"" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "km/h" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "leave blank for automatic configuration" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "left" +msgstr "left" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "metered" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "mph" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#, python-format +msgid "never" +msgstr "never" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#, python-format +msgid "now" +msgstr "now" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "openpilot Longitudinal Control (Alpha)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot Unavailable" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "openpilot longitudinal control may come in a future update." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "right" +msgstr "right" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "unmetered" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "up" +msgstr "up" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#, python-format +msgid "up to date, last checked never" +msgstr "up to date, last checked never" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#, python-format +msgid "up to date, last checked {}" +msgstr "up to date, last checked {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#, python-format +msgid "update available" +msgstr "update available" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} ALERT" +msgstr[1] "{} ALERTS" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "{} day ago" +msgstr[1] "{} days ago" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "{} hour ago" +msgstr[1] "{} hours ago" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "{} minute ago" +msgstr[1] "{} minutes ago" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "{} segment of your driving is in the training dataset so far." +msgstr[1] "{} segments of your driving is in the training dataset so far." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ SUBSCRIBED" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Firehose Mode 🔥" diff --git a/selfdrive/ui/translations/app_es.po b/selfdrive/ui/translations/app_es.po new file mode 100644 index 0000000000..a7424c0915 --- /dev/null +++ b/selfdrive/ui/translations/app_es.po @@ -0,0 +1,1229 @@ +# Spanish translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"PO-Revision-Date: 2025-10-20 16:35-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " La calibración de respuesta de par de dirección está completa." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " La calibración de respuesta de par de dirección está {}% completa." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " Tu dispositivo está orientado {:.1f}° {} y {:.1f}° {}." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "1 año de almacenamiento de conducción" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "Conectividad LTE 24/7" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"ADVERTENCIA: el control longitudinal de openpilot está en alpha para este " +"coche y deshabilitará el Frenado Automático de Emergencia (AEB).

En este coche, openpilot usa por defecto el ACC integrado del " +"coche en lugar del control longitudinal de openpilot. Activa esto para " +"cambiar al control longitudinal de openpilot. Se recomienda activar el modo " +"Experimental al habilitar el control longitudinal de openpilot (alpha)." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "ACTIVO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB (Android Debug Bridge) permite conectar tu dispositivo por USB o por la " +"red. Consulta https://docs.comma.ai/how-to/connect-to-comma para más " +"información." + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "AÑADIR" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "Reconocer actuación excesiva" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "Agresivo" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "Aceptar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "Supervisión del conductor siempre activa" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"Se puede probar una versión alpha del control longitudinal de openpilot, " +"junto con el modo Experimental, en ramas que no son de lanzamiento." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "¿Seguro que quieres apagar?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "¿Seguro que quieres reiniciar?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "¿Seguro que quieres restablecer la calibración?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "¿Seguro que quieres desinstalar?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "Atrás" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "Hazte miembro de comma prime en connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "" +"Añade connect.comma.ai a tu pantalla de inicio para usarlo como una app" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "CAMBIAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#, python-format +msgid "CHECK" +msgstr "COMPROBAR" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "MODO CHILL ACTIVADO" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "CONECTAR" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "CONECTAR" + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "Cambiar idioma" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "" +" Cambiar esta configuración reiniciará openpilot si el coche está encendido." + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "" +"Haz clic en \"añadir nuevo dispositivo\" y escanea el código QR de la derecha" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "Cerrar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Current Version" +msgstr "Versión actual" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "DOWNLOAD" +msgstr "DESCARGAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "Rechazar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "Rechazar, desinstalar openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "Desarrollador" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "Dispositivo" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "Desactivar con el pedal del acelerador" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#, python-format +msgid "Disengage to Power Off" +msgstr "Desactivar para apagar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#, python-format +msgid "Disengage to Reboot" +msgstr "Desactivar para reiniciar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "Desactivar para restablecer la calibración" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "Mostrar la velocidad en km/h en lugar de mph." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "ID del dongle" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Download" +msgstr "Descargar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "Cámara del conductor" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "Estilo de conducción" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "ERROR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "MODO EXPERIMENTAL ACTIVADO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "Activar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "Activar ADB" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "Activar advertencias de salida de carril" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "Activar openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "Activar SSH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "Activar advertencias de salida de carril" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "" +"Activar la supervisión del conductor incluso cuando openpilot no esté " +"activado." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "Activar openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" +"Activa el interruptor de control longitudinal de openpilot (alpha) para " +"permitir el modo Experimental." + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "Introduce tu nombre de usuario de GitHub" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#, python-format +msgid "Error" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "Modo experimental" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"El modo experimental no está disponible actualmente en este coche, ya que se " +"usa el ACC de fábrica para el control longitudinal." + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "Finalizar configuración" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Modo Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"Para la máxima efectividad, lleva tu dispositivo al interior y conéctalo " +"semanalmente a un buen adaptador USB‑C y Wi‑Fi.\n" +"\n" +"El Modo Firehose también puede funcionar mientras conduces si está conectado " +"a un hotspot o a una SIM ilimitada.\n" +"\n" +"\n" +"Preguntas frecuentes\n" +"\n" +"¿Importa cómo o dónde conduzco? No, conduce como normalmente lo harías.\n" +"\n" +"¿Se suben todos mis segmentos en el Modo Firehose? No, seleccionamos un " +"subconjunto de tus segmentos.\n" +"\n" +"¿Qué es un buen adaptador USB‑C? Cualquier cargador rápido de teléfono o " +"laptop sirve.\n" +"\n" +"¿Importa qué software ejecuto? Sí, solo openpilot upstream (y forks " +"particulares) pueden usarse para entrenamiento." + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "BUENO" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "Ve a https://connect.comma.ai en tu teléfono" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "ALTO" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "Red" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "INACTIVO: conéctate a una red sin límites" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#, python-format +msgid "INSTALL" +msgstr "INSTALAR" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#, python-format +msgid "Install Update" +msgstr "Instalar actualización" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "Modo de depuración de joystick" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "CARGANDO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "Modo de maniobra longitudinal" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "MÁX" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" +"Maximiza tus cargas de datos de entrenamiento para mejorar los modelos de " +"conducción de openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "NO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "Red" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "No se encontraron claves SSH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "No se encontraron claves SSH para el usuario '{username}'" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "No hay notas de versión disponibles." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "SIN CONEXIÓN" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "OK" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "EN LÍNEA" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "Abrir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "EMPAREJAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "VISTA PREVIA" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "FUNCIONES PRIME:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "Emparejar dispositivo" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "Emparejar dispositivo" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "Empareja tu dispositivo con tu cuenta de comma" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"Empareja tu dispositivo con comma connect (connect.comma.ai) y reclama tu " +"oferta de comma prime." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "Conéctate a Wi‑Fi para completar el emparejamiento inicial" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Power Off" +msgstr "Apagar" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"Previsualiza la cámara hacia el conductor para asegurarte de que la " +"supervisión del conductor tenga buena visibilidad. (el vehículo debe estar " +"apagado)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "Error de código QR" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "ELIMINAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "RESTABLECER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "REVISAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Reboot" +msgstr "Reiniciar" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "Reiniciar dispositivo" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "Reiniciar y actualizar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"Recibe alertas para volver al carril cuando tu vehículo se desvíe sobre una " +"línea de carril detectada sin la direccional activada mientras conduces a " +"más de 31 mph (50 km/h)." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "Grabar y subir cámara del conductor" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "Grabar y subir audio del micrófono" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"Grabar y almacenar audio del micrófono mientras conduces. El audio se " +"incluirá en el video de la dashcam en comma connect." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "Reglamentario" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "Relajado" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "Acceso remoto" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "Capturas remotas" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "Se agotó el tiempo de espera de la solicitud" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Reset" +msgstr "Restablecer" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "Restablecer calibración" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "Revisar guía de entrenamiento" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "Revisa las reglas, funciones y limitaciones de openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#, python-format +msgid "Select" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "Selecciona un idioma" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "Número de serie" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "Posponer actualización" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "Software" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "Estándar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"Se recomienda Estándar. En modo agresivo, openpilot seguirá más de cerca a " +"los coches delanteros y será más agresivo con el acelerador y el freno. En " +"modo relajado, openpilot se mantendrá más lejos de los coches delanteros. En " +"coches compatibles, puedes cambiar entre estas personalidades con el botón " +"de distancia del volante." + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "Sistema sin respuesta" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "TOME EL CONTROL INMEDIATAMENTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "TEMP" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "Interruptores" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#, python-format +msgid "UNINSTALL" +msgstr "DESINSTALAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "ACTUALIZAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Uninstall" +msgstr "Desinstalar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "Desconocido" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "Las actualizaciones solo se descargan cuando el coche está apagado." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "Mejorar ahora" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"Sube datos de la cámara orientada al conductor y ayuda a mejorar el " +"algoritmo de supervisión del conductor." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "Usar sistema métrico" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"Usa el sistema openpilot para control de crucero adaptativo y asistencia de " +"mantenimiento de carril. Tu atención se requiere en todo momento para usar " +"esta función." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "VEHÍCULO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "VER" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "Esperando para iniciar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"Advertencia: Esto otorga acceso SSH a todas las claves públicas en tu " +"configuración de GitHub. Nunca introduzcas un nombre de usuario de GitHub " +"que no sea el tuyo. Un empleado de comma NUNCA te pedirá que agregues su " +"nombre de usuario de GitHub." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "Bienvenido a openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "" +"Cuando está activado, al presionar el pedal del acelerador se desactivará " +"openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "Debes aceptar los Términos y Condiciones para poder usar openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"Debes aceptar los Términos y Condiciones para usar openpilot. Lee los " +"términos más recientes en https://comma.ai/terms antes de continuar." + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "iniciando cámara" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "down" +msgstr "abajo" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#, python-format +msgid "failed to check for update" +msgstr "Error al buscar actualizaciones" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "km/h" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "left" +msgstr "izquierda" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "mph" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#, python-format +msgid "never" +msgstr "nunca" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#, python-format +msgid "now" +msgstr "ahora" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "Control longitudinal de openpilot (Alpha)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot no disponible" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilot conduce por defecto en modo chill. El modo Experimental habilita " +"funciones de nivel alpha que no están listas para el modo chill. Las " +"funciones experimentales se enumeran a continuación:

Control " +"longitudinal de extremo a extremo


Deja que el modelo de conducción " +"controle el acelerador y los frenos. openpilot conducirá como piensa que lo " +"haría un humano, incluyendo detenerse en luces rojas y señales de alto. Dado " +"que el modelo decide la velocidad a la que conducir, la velocidad " +"establecida solo actuará como límite superior. Esta es una función de " +"calidad alpha; se deben esperar errores.

Nueva visualización de " +"conducción


La visualización de conducción hará la transición a la " +"cámara gran angular orientada a la carretera a bajas velocidades para " +"mostrar mejor algunos giros. El logotipo del modo Experimental también se " +"mostrará en la esquina superior derecha." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +" Cambiar esta configuración reiniciará openpilot si el coche está encendido." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilot aprende a conducir observando a humanos, como tú, conducir.\n" +"\n" +"El Modo Firehose te permite maximizar tus cargas de datos de entrenamiento " +"para mejorar los modelos de conducción de openpilot. Más datos significan " +"modelos más grandes, lo que significa un mejor Modo Experimental." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "" +"El control longitudinal de openpilot podría llegar en una actualización " +"futura." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" +"openpilot requiere que el dispositivo esté montado dentro de 4° a izquierda " +"o derecha y dentro de 5° hacia arriba o 9° hacia abajo." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "right" +msgstr "derecha" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "up" +msgstr "arriba" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#, python-format +msgid "up to date, last checked never" +msgstr "actualizado, última comprobación: nunca" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#, python-format +msgid "up to date, last checked {}" +msgstr "actualizado, última comprobación: {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#, python-format +msgid "update available" +msgstr "actualización disponible" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} ALERTA" +msgstr[1] "{} ALERTAS" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "hace {} día" +msgstr[1] "hace {} días" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "hace {} hora" +msgstr[1] "hace {} horas" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "hace {} minuto" +msgstr[1] "hace {} minutos" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "" +"{} segmento de tu conducción está en el conjunto de entrenamiento hasta " +"ahora." +msgstr[1] "" +"{} segmentos de tu conducción están en el conjunto de entrenamiento hasta " +"ahora." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ SUSCRITO" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Modo Firehose 🔥" diff --git a/selfdrive/ui/translations/app_fr.po b/selfdrive/ui/translations/app_fr.po new file mode 100644 index 0000000000..12bdcb3a8c --- /dev/null +++ b/selfdrive/ui/translations/app_fr.po @@ -0,0 +1,1234 @@ +# French translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"PO-Revision-Date: 2025-10-20 18:19-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "1 an de stockage de trajets" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "Connexion LTE 24/7" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"ATTENTION : le contrôle longitudinal openpilot est en alpha pour cette " +"voiture et désactivera le freinage d'urgence automatique (AEB).

Sur cette voiture, openpilot utilise par défaut le régulateur de " +"vitesse adaptatif intégré au véhicule plutôt que le contrôle longitudinal " +"d'openpilot. Activez ceci pour passer au contrôle longitudinal openpilot. Il " +"est recommandé d'activer le mode expérimental lors de l'activation du " +"contrôle longitudinal openpilot alpha." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "ACTIF" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB (Android Debug Bridge) permet de connecter votre appareil via USB ou via " +"le réseau. Voir https://docs.comma.ai/how-to/connect-to-comma pour plus " +"d'informations." + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "AJOUTER" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "Accuser réception d'actionnement excessif" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "Agressif" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "Accepter" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "Surveillance continue du conducteur" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"Une version alpha du contrôle longitudinal openpilot peut être testée, avec " +"le mode expérimental, sur des branches non publiées." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "Êtes-vous sûr de vouloir éteindre ?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "Êtes-vous sûr de vouloir redémarrer ?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "Êtes-vous sûr de vouloir réinitialiser la calibration ?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "Êtes-vous sûr de vouloir désinstaller ?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "Retour" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "Devenez membre comma prime sur connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "" +"Ajoutez connect.comma.ai à votre écran d'accueil pour l'utiliser comme une " +"application" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "CHANGER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#, python-format +msgid "CHECK" +msgstr "VÉRIFIER" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "MODE CHILL ACTIVÉ" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "CONNECTER" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "CONNECTER" + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "Changer la langue" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "" +" La modification de ce réglage redémarrera openpilot si la voiture est sous " +"tension." + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "Cliquez sur \"add new device\" et scannez le code QR à droite" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "Fermer" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Current Version" +msgstr "Version actuelle" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "DOWNLOAD" +msgstr "TÉLÉCHARGER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "Refuser" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "Refuser, désinstaller openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "Développeur" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "Appareil" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "Désengager à l'appui sur l'accélérateur" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#, python-format +msgid "Disengage to Power Off" +msgstr "Désengager pour éteindre" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#, python-format +msgid "Disengage to Reboot" +msgstr "Désengager pour redémarrer" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "Désengager pour réinitialiser la calibration" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "Afficher la vitesse en km/h au lieu de mph." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "ID du dongle" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Download" +msgstr "Télécharger" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "Caméra conducteur" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "Personnalité de conduite" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "ERREUR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "MODE EXPÉRIMENTAL ACTIVÉ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "Activer" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "Activer ADB" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "Activer les alertes de sortie de voie" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "Activer openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "Activer SSH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "Activer les alertes de sortie de voie" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "" +"Activer la surveillance du conducteur même lorsque openpilot n'est pas " +"engagé." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "Activer openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" +"Activez l'option de contrôle longitudinal openpilot (alpha) pour autoriser " +"le mode expérimental." + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "Entrez votre nom d'utilisateur GitHub" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#, python-format +msgid "Error" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "Mode expérimental" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"Le mode expérimental est actuellement indisponible sur cette voiture car " +"l'ACC d'origine est utilisé pour le contrôle longitudinal." + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "Terminer la configuration" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Mode Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"Pour une efficacité maximale, rentrez votre appareil et connectez-le chaque " +"semaine à un bon adaptateur USB-C et au Wi‑Fi.\n" +"\n" +"Le Mode Firehose peut aussi fonctionner pendant que vous conduisez si vous " +"êtes connecté à un hotspot ou à une carte SIM illimitée.\n" +"\n" +"\n" +"Foire aux questions\n" +"\n" +"Est-ce que la manière ou l'endroit où je conduis compte ? Non, conduisez " +"normalement.\n" +"\n" +"Tous mes segments sont-ils récupérés en Mode Firehose ? Non, nous récupérons " +"de façon sélective un sous-ensemble de vos segments.\n" +"\n" +"Quel est un bon adaptateur USB-C ? Tout chargeur rapide de téléphone ou " +"d'ordinateur portable convient.\n" +"\n" +"Le logiciel utilisé importe-t-il ? Oui, seul openpilot amont (et certains " +"forks) peut être utilisé pour l'entraînement." + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "BON" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "Allez sur https://connect.comma.ai sur votre téléphone" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "ÉLEVÉ" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "Réseau" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "INACTIF : connectez-vous à un réseau non limité" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#, python-format +msgid "INSTALL" +msgstr "INSTALLER" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#, python-format +msgid "Install Update" +msgstr "Installer la mise à jour" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "Mode débogage joystick" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "CHARGEMENT" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "Mode de manœuvre longitudinale" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "MAX" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" +"Maximisez vos envois de données d'entraînement pour améliorer les modèles de " +"conduite d'openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "NON" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "Réseau" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "Aucune clé SSH trouvée" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "Aucune clé SSH trouvée pour l'utilisateur '{username}'" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "Aucune note de version disponible." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "HORS LIGNE" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "OK" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "EN LIGNE" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "Ouvrir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "ASSOCIER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "APERÇU" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "FONCTIONNALITÉS PRIME :" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "Associer l'appareil" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "Associer l'appareil" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "Associez votre appareil à votre compte comma" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"Associez votre appareil à comma connect (connect.comma.ai) et réclamez votre " +"offre comma prime." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "Veuillez vous connecter au Wi‑Fi pour terminer l'association initiale" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Power Off" +msgstr "Éteindre" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"Prévisualisez la caméra orientée conducteur pour vous assurer que la " +"surveillance du conducteur a une bonne visibilité. (le véhicule doit être " +"éteint)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "Erreur de code QR" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "SUPPRIMER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "RÉINITIALISER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "CONSULTER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Reboot" +msgstr "Redémarrer" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "Redémarrer l'appareil" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "Redémarrer et mettre à jour" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"Recevez des alertes pour revenir dans la voie lorsque votre véhicule dépasse " +"une ligne de voie détectée sans clignotant activé en roulant au-delà de 31 " +"mph (50 km/h)." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "Enregistrer et téléverser la caméra conducteur" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "Enregistrer et téléverser l'audio du microphone" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"Enregistrer et stocker l'audio du microphone pendant la conduite. L'audio " +"sera inclus dans la vidéo dashcam dans comma connect." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "Réglementaire" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "Détendu" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "Accès à distance" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "Captures à distance" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "Délai de la requête dépassé" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Reset" +msgstr "Réinitialiser" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "Réinitialiser la calibration" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "Consulter le guide d'entraînement" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "Consultez les règles, fonctionnalités et limitations d'openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#, python-format +msgid "Select" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "Numéro de série" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "Reporter la mise à jour" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "Logiciel" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "Standard" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"Le mode standard est recommandé. En mode agressif, openpilot suivra les " +"véhicules de tête de plus près et sera plus agressif avec l'accélérateur et " +"le frein. En mode détendu, openpilot restera plus éloigné des véhicules de " +"tête. Sur les voitures compatibles, vous pouvez parcourir ces personnalités " +"avec le bouton de distance du volant." + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "Système non réactif" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "REPRENEZ IMMÉDIATEMENT LE CONTRÔLE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "TEMPÉRATURE" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "Options" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#, python-format +msgid "UNINSTALL" +msgstr "DÉSINSTALLER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "METTRE À JOUR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Uninstall" +msgstr "Désinstaller" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "Inconnu" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "" +"Les mises à jour ne sont téléchargées que lorsque la voiture est éteinte." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "Mettre à niveau maintenant" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"Téléverser les données de la caméra orientée conducteur et aider à améliorer " +"l'algorithme de surveillance du conducteur." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "Utiliser le système métrique" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"Utilisez le système openpilot pour l'ACC et l'assistance au maintien de " +"voie. Votre attention est requise en permanence pour utiliser cette " +"fonctionnalité." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "VÉHICULE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "VOIR" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "En attente de démarrage" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"Avertissement : Ceci accorde un accès SSH à toutes les clés publiques dans " +"vos paramètres GitHub. N'entrez jamais un nom d'utilisateur GitHub autre que " +"le vôtre. Un employé comma ne vous demandera JAMAIS d'ajouter son nom " +"d'utilisateur GitHub." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "Bienvenue sur openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "" +"Lorsque activé, appuyer sur la pédale d'accélérateur désengagera openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "Vous devez accepter les conditions générales pour utiliser openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"Vous devez accepter les conditions générales pour utiliser openpilot. Lisez " +"les dernières conditions sur https://comma.ai/terms avant de continuer." + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "démarrage de la caméra" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "down" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#, python-format +msgid "failed to check for update" +msgstr "échec de la vérification de mise à jour" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "km/h" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "left" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "mph" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#, python-format +msgid "never" +msgstr "jamais" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#, python-format +msgid "now" +msgstr "maintenant" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "Contrôle longitudinal openpilot (Alpha)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot indisponible" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilot roule par défaut en mode chill. Le mode expérimental active des " +"fonctionnalités de niveau alpha qui ne sont pas prêtes pour le mode chill. " +"Les fonctionnalités expérimentales sont listées ci‑dessous:

Contrôle " +"longitudinal de bout en bout


Laissez le modèle de conduite contrôler " +"l'accélérateur et les freins. openpilot conduira comme il pense qu'un humain " +"le ferait, y compris s'arrêter aux feux rouges et aux panneaux stop. Comme " +"le modèle décide de la vitesse à adopter, la vitesse réglée n'agira que " +"comme une limite supérieure. C'est une fonctionnalité de qualité alpha ; des " +"erreurs sont à prévoir.

Nouvelle visualisation de conduite


La " +"visualisation passera à la caméra grand angle orientée route à basse vitesse " +"pour mieux montrer certains virages. Le logo du mode expérimental sera " +"également affiché en haut à droite." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +" La modification de ce réglage redémarrera openpilot si la voiture est sous " +"tension." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilot apprend à conduire en regardant des humains, comme vous, " +"conduire.\n" +"\n" +"Le Mode Firehose vous permet de maximiser vos envois de données " +"d'entraînement pour améliorer les modèles de conduite d'openpilot. Plus de " +"données signifie des modèles plus grands, ce qui signifie un meilleur Mode " +"expérimental." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "" +"Le contrôle longitudinal openpilot pourra arriver dans une future mise à " +"jour." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" +"openpilot exige que l'appareil soit monté à moins de 4° à gauche ou à droite " +"et à moins de 5° vers le haut ou 9° vers le bas." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "right" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "up" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#, python-format +msgid "up to date, last checked never" +msgstr "à jour, dernière vérification jamais" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#, python-format +msgid "up to date, last checked {}" +msgstr "à jour, dernière vérification {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#, python-format +msgid "update available" +msgstr "mise à jour disponible" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} ALERTE" +msgstr[1] "{} ALERTES" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "il y a {} jour" +msgstr[1] "il y a {} jours" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "il y a {} heure" +msgstr[1] "il y a {} heures" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "il y a {} minute" +msgstr[1] "il y a {} minutes" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "" +"{} segment de votre conduite est dans l'ensemble d'entraînement jusqu'à " +"présent." +msgstr[1] "" +"{} segments de votre conduite sont dans l'ensemble d'entraînement jusqu'à " +"présent." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ ABONNÉ" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Mode Firehose 🔥" diff --git a/selfdrive/ui/translations/app_pt-BR.po b/selfdrive/ui/translations/app_pt-BR.po new file mode 100644 index 0000000000..1829dccd83 --- /dev/null +++ b/selfdrive/ui/translations/app_pt-BR.po @@ -0,0 +1,1220 @@ +# Language pt-BR translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"PO-Revision-Date: 2025-10-21 00:00-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: pt-BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " A calibração da resposta de torque da direção foi concluída." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " A calibração da resposta de torque da direção está {}% concluída." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " Seu dispositivo está apontado {:.1f}° {} e {:.1f}° {}." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "1 ano de armazenamento de condução" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "Conectividade LTE 24/7" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"AVISO: o controle longitudinal do openpilot está em alpha para este carro " +"e desativará a Frenagem Automática de Emergência (AEB).

Neste " +"carro, o openpilot usa por padrão o ACC integrado do carro em vez do " +"controle longitudinal do openpilot. Ative isto para alternar para o controle " +"longitudinal do openpilot. Recomenda-se ativar o Modo Experimental ao ativar " +"o controle longitudinal do openpilot em alpha." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "ATIVO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB (Android Debug Bridge) permite conectar ao seu dispositivo via USB ou " +"pela rede. Veja https://docs.comma.ai/how-to/connect-to-comma para mais " +"informações." + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "ADICIONAR" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "Reconhecer Atuação Excessiva" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "Agressivo" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "Concordo" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "Monitoramento de Motorista Sempre Ativo" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"Uma versão alpha do controle longitudinal do openpilot pode ser testada, " +"junto com o Modo Experimental, em ramificações fora de release." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "Tem certeza de que deseja desligar?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "Tem certeza de que deseja reiniciar?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "Tem certeza de que deseja redefinir a calibração?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "Tem certeza de que deseja desinstalar?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "Voltar" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "Torne-se membro comma prime em connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "Adicione connect.comma.ai à tela inicial para usá-lo como um app" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "ALTERAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#, python-format +msgid "CHECK" +msgstr "VERIFICAR" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "MODO CHILL ATIVO" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "CONECTAR" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "CONECTAR" + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "Alterar Idioma" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "" +" Alterar esta configuração reiniciará o openpilot se o carro estiver ligado." + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "Toque em \"adicionar novo dispositivo\" e escaneie o QR code à direita" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "Fechar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Current Version" +msgstr "Versão Atual" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "DOWNLOAD" +msgstr "BAIXAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "Recusar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "Recusar, desinstalar o openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "Desenvolvedor" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "Dispositivo" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "Desativar ao pressionar o acelerador" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#, python-format +msgid "Disengage to Power Off" +msgstr "Desativar para Desligar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#, python-format +msgid "Disengage to Reboot" +msgstr "Desativar para Reiniciar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "Desativar para Redefinir Calibração" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "Exibir velocidade em km/h em vez de mph." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "ID do Dongle" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Download" +msgstr "Baixar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "Câmera do Motorista" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "Personalidade de Condução" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "ERRO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "MODO EXPERIMENTAL ATIVO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "Ativar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "Ativar ADB" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "Ativar alertas de saída de faixa" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "Ativar openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "Ativar SSH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "Ativar alertas de saída de faixa" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "" +"Ativar monitoramento do motorista mesmo quando o openpilot não está engajado." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "Ativar openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" +"Ative a opção de controle longitudinal do openpilot (alpha) para permitir o " +"Modo Experimental." + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "Digite seu nome de usuário do GitHub" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#, python-format +msgid "Error" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "Modo Experimental" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"O Modo Experimental está indisponível neste carro pois o ACC original do " +"carro é usado para controle longitudinal." + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "Concluir Configuração" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Modo Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"Para máxima efetividade, leve seu dispositivo para dentro e conecte a um bom " +"adaptador USB-C e Wi‑Fi semanalmente.\n" +"\n" +"O Modo Firehose também pode funcionar enquanto você dirige se estiver " +"conectado a um hotspot ou a um SIM ilimitado.\n" +"\n" +"\n" +"Perguntas Frequentes\n" +"\n" +"Importa como ou onde eu dirijo? Não, apenas dirija como normalmente.\n" +"\n" +"Todos os meus segmentos são puxados no Modo Firehose? Não, puxamos " +"seletivamente um subconjunto dos seus segmentos.\n" +"\n" +"Qual é um bom adaptador USB‑C? Qualquer carregador rápido de telefone ou " +"laptop serve.\n" +"\n" +"Importa qual software eu executo? Sim, apenas o openpilot upstream (e forks " +"específicos) podem ser usados para treinamento." + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "BOM" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "Acesse https://connect.comma.ai no seu telefone" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "ALTO" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "Rede" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "INATIVO: conecte a uma rede sem franquia" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#, python-format +msgid "INSTALL" +msgstr "INSTALAR" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#, python-format +msgid "Install Update" +msgstr "Instalar Atualização" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "Modo de Depuração do Joystick" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "CARREGANDO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "Modo de Manobra Longitudinal" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "MÁX" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" +"Maximize seus envios de dados de treinamento para melhorar os modelos de " +"condução do openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "NÃO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "Rede" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "Nenhuma chave SSH encontrada" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "Nenhuma chave SSH encontrada para o usuário '{username}'" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "Sem notas de versão disponíveis." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "OFFLINE" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "OK" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "ONLINE" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "Abrir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "EMPARELHAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "PRÉVIA" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "RECURSOS PRIME:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "Emparelhar Dispositivo" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "Emparelhar dispositivo" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "Emparelhe seu dispositivo à sua conta comma" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"Emparelhe seu dispositivo com o comma connect (connect.comma.ai) e resgate " +"sua oferta comma prime." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "Conecte-se ao Wi‑Fi para concluir o emparelhamento inicial" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Power Off" +msgstr "Desligar" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"Pré-visualize a câmera voltada para o motorista para garantir que o " +"monitoramento do motorista tenha boa visibilidade. (veículo deve estar " +"desligado)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "Erro no QR Code" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "REMOVER" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "REDEFINIR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "REVISAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Reboot" +msgstr "Reiniciar" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "Reiniciar Dispositivo" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "Reiniciar e Atualizar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"Receba alertas para voltar à faixa quando seu veículo cruzar uma linha de " +"faixa detectada sem seta ativada ao dirigir acima de 31 mph (50 km/h)." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "Gravar e Enviar Câmera do Motorista" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "Gravar e Enviar Áudio do Microfone" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"Grave e armazene o áudio do microfone enquanto dirige. O áudio será incluído " +"no vídeo da dashcam no comma connect." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "Regulatório" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "Relaxado" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "Acesso remoto" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "Capturas remotas" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "Tempo da solicitação esgotado" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Reset" +msgstr "Redefinir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "Redefinir Calibração" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "Revisar Guia de Treinamento" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "Revise as regras, recursos e limitações do openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#, python-format +msgid "Select" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "Selecione um idioma" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "Serial" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "Adiar Atualização" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "Software" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "Padrão" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"Padrão é recomendado. No modo agressivo, o openpilot seguirá veículos à " +"frente mais de perto e será mais agressivo com acelerador e freio. No modo " +"relaxado, o openpilot ficará mais longe dos veículos à frente. Em carros " +"compatíveis, você pode alternar essas personalidades com o botão de " +"distância do volante." + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "Sistema sem resposta" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "ASSUMA O CONTROLE IMEDIATAMENTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "TEMP" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "Alternâncias" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#, python-format +msgid "UNINSTALL" +msgstr "DESINSTALAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "ATUALIZAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Uninstall" +msgstr "Desinstalar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "Desconhecido" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "Atualizações são baixadas apenas com o carro desligado." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "Atualizar Agora" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"Envie dados da câmera voltada para o motorista e ajude a melhorar o " +"algoritmo de monitoramento do motorista." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "Usar Sistema Métrico" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"Use o sistema openpilot para controle de cruzeiro adaptativo e assistência " +"de permanência em faixa. Sua atenção é necessária o tempo todo para usar " +"este recurso." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "VEÍCULO" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "VER" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "Aguardando para iniciar" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"Aviso: Isso concede acesso SSH a todas as chaves públicas nas suas " +"configurações do GitHub. Nunca informe um nome de usuário do GitHub que não " +"seja o seu. Um funcionário da comma NUNCA pedirá para você adicionar o nome " +"de usuário do GitHub dele." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "Bem-vindo ao openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "" +"Quando ativado, pressionar o pedal do acelerador desengajará o openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "Você deve aceitar os Termos e Condições para usar o openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"Você deve aceitar os Termos e Condições para usar o openpilot. Leia os " +"termos mais recentes em https://comma.ai/terms antes de continuar." + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "câmera iniciando" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "down" +msgstr "para baixo" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#, python-format +msgid "failed to check for update" +msgstr "falha ao verificar atualização" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "km/h" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "left" +msgstr "à esquerda" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "mph" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#, python-format +msgid "never" +msgstr "nunca" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#, python-format +msgid "now" +msgstr "agora" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "Controle Longitudinal do openpilot (Alpha)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot Indisponível" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"o openpilot dirige por padrão no modo chill. O Modo Experimental habilita " +"recursos em nível alpha que não estão prontos para o modo chill. Os recursos " +"experimentais são listados abaixo:

Controle Longitudinal End-to-End
Permita que o modelo de condução controle o acelerador e os freios. O " +"openpilot dirigirá como acha que um humano faria, incluindo parar em sinais " +"e semáforos vermelhos. Como o modelo decide a velocidade, a velocidade " +"definida atuará apenas como limite superior. Este é um recurso de qualidade " +"alpha; erros devem ser esperados.

Nova Visualização de Condução
A visualização de condução mudará para a câmera grande-angular " +"voltada para a estrada em baixas velocidades para mostrar melhor algumas " +"curvas. O logotipo do Modo Experimental também será exibido no canto " +"superior direito." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +" Alterar esta configuração reiniciará o openpilot se o carro estiver ligado." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"o openpilot aprende a dirigir observando humanos, como você, dirigirem.\n" +"\n" +"O Modo Firehose permite maximizar seus envios de dados de treinamento para " +"melhorar os modelos de condução do openpilot. Mais dados significam modelos " +"maiores, o que significa um Modo Experimental melhor." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "" +"o controle longitudinal do openpilot pode vir em uma atualização futura." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" +"o openpilot requer que o dispositivo seja montado dentro de 4° para a " +"esquerda ou direita e dentro de 5° para cima ou 9° para baixo." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "right" +msgstr "à direita" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "up" +msgstr "para cima" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#, python-format +msgid "up to date, last checked never" +msgstr "atualizado, última verificação: nunca" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#, python-format +msgid "up to date, last checked {}" +msgstr "atualizado, última verificação: {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#, python-format +msgid "update available" +msgstr "atualização disponível" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} ALERTA" +msgstr[1] "{} ALERTAS" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "{} dia atrás" +msgstr[1] "{} dias atrás" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "{} hora atrás" +msgstr[1] "{} horas atrás" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "{} minuto atrás" +msgstr[1] "{} minutos atrás" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "" +"{} segmento da sua condução está no conjunto de treinamento até agora." +msgstr[1] "" +"{} segmentos da sua condução estão no conjunto de treinamento até agora." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ ASSINADO" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Modo Firehose 🔥" diff --git a/selfdrive/ui/translations/app_tr.po b/selfdrive/ui/translations/app_tr.po new file mode 100644 index 0000000000..29d3b9b9fe --- /dev/null +++ b/selfdrive/ui/translations/app_tr.po @@ -0,0 +1,1214 @@ +# Turkish translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"PO-Revision-Date: 2025-10-20 18:19-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: tr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " Direksiyon tork tepkisi kalibrasyonu tamamlandı." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " Direksiyon tork tepkisi kalibrasyonu {}% tamamlandı." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " Cihazınız {:.1f}° {} ve {:.1f}° {} yönünde konumlandırılmış." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "1 yıl sürüş depolaması" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "7/24 LTE bağlantısı" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"UYARI: Bu araç için openpilot boylamsal kontrolü alfa aşamasındadır ve " +"Otomatik Acil Frenlemeyi (AEB) devre dışı bırakacaktır.

Bu araçta " +"openpilot, openpilot'un boylamsal kontrolü yerine aracın yerleşik ACC'sini " +"varsayılan olarak kullanır. openpilot boylamsal kontrolüne geçmek için bunu " +"etkinleştirin. openpilot boylamsal kontrol alfayı etkinleştirirken Deneysel " +"modu etkinleştirmeniz önerilir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "AKTİF" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB (Android Debug Bridge), cihazınıza USB veya ağ üzerinden bağlanmayı " +"sağlar. Daha fazla bilgi için https://docs.comma.ai/how-to/connect-to-comma " +"adresine bakın." + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "EKLE" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "Aşırı Müdahaleyi Onayla" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "Agresif" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "Kabul et" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "Sürekli Sürücü İzleme" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"openpilot boylamsal kontrolünün alfa sürümü, Deneysel mod ile birlikte, " +"yayın dışı dallarda test edilebilir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "Kapatmak istediğinizden emin misiniz?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "Yeniden başlatmak istediğinizden emin misiniz?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "Kalibrasyonu sıfırlamak istediğinizden emin misiniz?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "Kaldırmak istediğinizden emin misiniz?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "Geri" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "connect.comma.ai adresinde comma prime üyesi olun" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "" +"connect.comma.ai'yi ana ekranınıza ekleyerek bir uygulama gibi kullanın" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "DEĞİŞTİR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#, python-format +msgid "CHECK" +msgstr "KONTROL ET" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "CHILL MODU AÇIK" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "BAĞLAN" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "BAĞLAN" + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "Dili Değiştir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "" +" Bu ayarı değiştirmek, araç çalışıyorsa openpilot'u yeniden başlatacaktır." + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "\"yeni cihaz ekle\"ye tıklayın ve sağdaki QR kodunu tarayın" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "Kapat" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Current Version" +msgstr "Geçerli Sürüm" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "DOWNLOAD" +msgstr "İNDİR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "Reddet" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "Reddet, openpilot'u kaldır" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "Geliştirici" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "Cihaz" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "Gaz Pedalında Devreden Çık" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#, python-format +msgid "Disengage to Power Off" +msgstr "Kapatmak için Devreden Çıkın" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#, python-format +msgid "Disengage to Reboot" +msgstr "Yeniden Başlatmak için Devreden Çıkın" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "Kalibrasyonu Sıfırlamak için Devreden Çıkın" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "Hızı mph yerine km/h olarak göster." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "Dongle ID" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Download" +msgstr "İndir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "Sürücü Kamerası" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "Sürüş Kişiliği" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "HATA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "DENEYSEL MOD AÇIK" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "Etkinleştir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "ADB'yi Etkinleştir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "Şerit Terk Uyarılarını Etkinleştir" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "openpilot'u etkinleştir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "SSH'yi Etkinleştir" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "Şerit Terk Uyarılarını Etkinleştir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "openpilot devrede değilken bile sürücü izlemesini etkinleştir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "openpilot'u etkinleştir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" +"Deneysel modu etkinleştirmek için openpilot boylamsal kontrolünü (alfa) açın." + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "GitHub kullanıcı adınızı girin" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#, python-format +msgid "Error" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "Deneysel Mod" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"Bu araçta boylamsal kontrol için stok ACC kullanıldığından şu anda Deneysel " +"mod kullanılamıyor." + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "Kurulumu Bitir" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Firehose Modu" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"Maksimum verim için cihazınızı içeri alın ve haftalık olarak iyi bir USB-C " +"adaptörüne ve Wi‑Fi'a bağlayın.\n" +"\n" +"Firehose Modu, bir hotspot'a veya sınırsız SIM karta bağlıyken sürüş " +"sırasında da çalışabilir.\n" +"\n" +"\n" +"Sıkça Sorulan Sorular\n" +"\n" +"Nasıl veya nerede sürdüğüm önemli mi? Hayır, normalde nasıl sürüyorsanız " +"öyle sürün.\n" +"\n" +"Firehose Modu'nda tüm segmentlerim çekiliyor mu? Hayır, segmentlerinizin bir " +"alt kümesini seçerek çekiyoruz.\n" +"\n" +"İyi bir USB‑C adaptörü nedir? Hızlı bir telefon veya dizüstü şarj cihazı " +"uygundur.\n" +"\n" +"Hangi yazılımı çalıştırdığım önemli mi? Evet, yalnızca upstream openpilot " +"(ve bazı fork'lar) eğitim için kullanılabilir." + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "İYİ" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "Telefonunuzda https://connect.comma.ai adresine gidin" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "YÜKSEK" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "Ağ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "PASİF: sınırsız bir ağa bağlanın" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#, python-format +msgid "INSTALL" +msgstr "YÜKLE" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#, python-format +msgid "Install Update" +msgstr "Güncellemeyi Yükle" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "Joystick Hata Ayıklama Modu" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "YÜKLENİYOR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "Boylamsal Manevra Modu" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "MAKS" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" +"openpilot'un sürüş modellerini iyileştirmek için eğitim veri yüklemelerinizi " +"en üst düzeye çıkarın." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "HAYIR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "Ağ" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "SSH anahtarı bulunamadı" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "'{username}' için SSH anahtarı bulunamadı" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "Sürüm notu mevcut değil." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "ÇEVRİMDIŞI" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "OK" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "ÇEVRİMİÇİ" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "Aç" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "EŞLE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "ÖNİZLEME" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "PRIME ÖZELLİKLERİ:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "Cihazı Eşle" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "Cihazı eşle" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "Cihazınızı comma hesabınızla eşleştirin" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"Cihazınızı comma connect (connect.comma.ai) ile eşleştirin ve comma prime " +"teklifinizi alın." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "İlk eşleştirmeyi tamamlamak için lütfen Wi‑Fi'a bağlanın" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#, python-format +msgid "Power Off" +msgstr "Kapat" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"Sürücü izleme görünürlüğünün iyi olduğundan emin olmak için sürücüye bakan " +"kamerayı önizleyin. (araç kapalı olmalıdır)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "QR Kod Hatası" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "KALDIR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "SIFIRLA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "GÖZDEN GEÇİR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#, python-format +msgid "Reboot" +msgstr "Yeniden Başlat" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "Cihazı Yeniden Başlat" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "Yeniden Başlat ve Güncelle" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"Araç 31 mph (50 km/h) üzerindeyken sinyal verilmeden algılanan şerit " +"çizgisini aştığınızda şeride geri dönmeniz için uyarılar alın." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "Sürücü Kamerasını Kaydet ve Yükle" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "Mikrofon Sesini Kaydet ve Yükle" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"Sürüş sırasında mikrofon sesini kaydedip saklayın. Ses, comma connect'teki " +"ön kamera videosuna dahil edilecektir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "Mevzuat" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "Rahat" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "Uzaktan erişim" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "Uzaktan anlık görüntüler" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "İstek zaman aşımına uğradı" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#, python-format +msgid "Reset" +msgstr "Sıfırla" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "Kalibrasyonu Sıfırla" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "Eğitim Kılavuzunu İncele" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "" +"openpilot'un kurallarını, özelliklerini ve sınırlamalarını gözden geçirin" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#, python-format +msgid "Select" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "Bir dil seçin" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "Seri" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "Güncellemeyi Ertele" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "Yazılım" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "Standart" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"Standart önerilir. Agresif modda openpilot öndeki aracı daha yakından takip " +"eder ve gaz/fren kullanımında daha ataktır. Rahat modda openpilot öndeki " +"araçlardan daha uzak durur. Desteklenen araçlarda bu kişilikler arasında " +"direksiyon mesafe düğmesiyle geçiş yapabilirsiniz." + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "Sistem Yanıt Vermiyor" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "HEMEN KONTROLÜ DEVRALIN" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "TEMP" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "Seçenekler" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#, python-format +msgid "UNINSTALL" +msgstr "KALDIR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "GÜNCELLE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#, python-format +msgid "Uninstall" +msgstr "Kaldır" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "Bilinmiyor" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "Güncellemeler yalnızca araç kapalıyken indirilir." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "Şimdi Yükselt" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"Sürücüye bakan kameradan veri yükleyin ve sürücü izleme algoritmasını " +"geliştirmeye yardımcı olun." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "Metrik Sistemi Kullan" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"Uyarlanabilir hız sabitleyici ve şerit koruma sürücü yardımında openpilot " +"sistemini kullanın. Bu özelliği kullanırken her zaman dikkatli olmanız " +"gerekir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "ARAÇ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "GÖRÜNTÜLE" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "Başlatma bekleniyor" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"Uyarı: Bu, GitHub ayarlarınızdaki tüm açık anahtarlara SSH erişimi verir. " +"Kendi adınız dışında asla bir GitHub kullanıcı adı girmeyin. Bir comma " +"çalışanı sizden asla GitHub kullanıcı adlarını eklemenizi İSTEMEZ." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "openpilot'a hoş geldiniz" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "" +"Etkinleştirildiğinde, gaz pedalına basmak openpilot'u devreden çıkarır." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "openpilot'u kullanmak için Şartlar ve Koşulları kabul etmelisiniz." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"openpilot'u kullanmak için Şartlar ve Koşulları kabul etmelisiniz. Devam " +"etmeden önce en güncel şartları https://comma.ai/terms adresinde okuyun." + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "kamera başlatılıyor" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "down" +msgstr "aşağı" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#, python-format +msgid "failed to check for update" +msgstr "güncelleme kontrolü başarısız" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "km/h" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "left" +msgstr "sol" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "mph" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#, python-format +msgid "never" +msgstr "asla" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#, python-format +msgid "now" +msgstr "şimdi" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "openpilot Boylamsal Kontrol (Alfa)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot Kullanılamıyor" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilot varsayılan olarak chill modunda sürer. Deneysel mod, chill moduna " +"hazır olmayan alfa seviyesindeki özellikleri etkinleştirir. Deneysel " +"özellikler aşağıda listelenmiştir:

Uçtan Uca Boylamsal Kontrol
Sürüş modelinin gaz ve frenleri kontrol etmesine izin verin. " +"openpilot, kırmızı ışıklarda ve dur işaretlerinde durmak dahil, bir insan " +"nasıl sürer diye düşündüğüne göre sürer. Hızı sürüş modeli belirlediğinden, " +"ayarlanan hız yalnızca üst sınır olarak işlev görür. Bu bir alfa kalitesinde " +"özelliktir; hatalar beklenmelidir.

Yeni Sürüş Görselleştirmesi
Sürüş görselleştirmesi, düşük hızlarda bazı dönüşleri daha iyi " +"göstermek için yola bakan geniş açılı kameraya geçer. Deneysel mod logosu " +"sağ üst köşede de gösterilecektir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +" Bu ayarı değiştirmek, araç çalışıyorsa openpilot'u yeniden başlatacaktır." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilot, sizin gibi insanların nasıl sürdüğünü izleyerek sürmeyi öğrenir.\n" +"\n" +"Firehose Modu, openpilot'un sürüş modellerini geliştirmek için eğitim veri " +"yüklemelerinizi en üst düzeye çıkarmanıza olanak tanır. Daha fazla veri, " +"daha büyük modeller demektir; bu da daha iyi Deneysel Mod anlamına gelir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "openpilot boylamsal kontrolü gelecekteki bir güncellemede gelebilir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" +"openpilot, cihazın sağa/sola 4° ve yukarı 5° veya aşağı 9° içinde monte " +"edilmesini gerektirir." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "right" +msgstr "sağ" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#, python-format +msgid "up" +msgstr "yukarı" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#, python-format +msgid "up to date, last checked never" +msgstr "güncel, son kontrol asla" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#, python-format +msgid "up to date, last checked {}" +msgstr "güncel, son kontrol {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#, python-format +msgid "update available" +msgstr "güncelleme mevcut" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} UYARI" +msgstr[1] "{} UYARILAR" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "{} gün önce" +msgstr[1] "{} gün önce" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "{} saat önce" +msgstr[1] "{} saat önce" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "{} dakika önce" +msgstr[1] "{} dakika önce" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "{} segment sürüşünüz eğitim veri setinde." +msgstr[1] "{} segment sürüşünüz eğitim veri setinde." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ ABONE" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Firehose Modu 🔥" diff --git a/selfdrive/ui/update_translations_raylib.py b/selfdrive/ui/update_translations_raylib.py new file mode 100755 index 0000000000..e91820f242 --- /dev/null +++ b/selfdrive/ui/update_translations_raylib.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +from itertools import chain +import os +from openpilot.system.ui.lib.multilang import SYSTEM_UI_DIR, UI_DIR, TRANSLATIONS_DIR, multilang + + +def update_translations(): + files = [] + for root, _, filenames in chain(os.walk(SYSTEM_UI_DIR), + os.walk(os.path.join(UI_DIR, "widgets")), + os.walk(os.path.join(UI_DIR, "layouts")), + os.walk(os.path.join(UI_DIR, "onroad"))): + for filename in filenames: + if filename.endswith(".py"): + files.append(os.path.join(root, filename)) + + # Create main translation file + cmd = ("xgettext -L Python --keyword=tr --keyword=trn:1,2 --keyword=tr_noop --from-code=UTF-8 " + + "--flag=tr:1:python-brace-format --flag=trn:1:python-brace-format --flag=trn:2:python-brace-format " + + "-o translations/app.pot {}").format(" ".join(files)) + + ret = os.system(cmd) + assert ret == 0 + + # Generate/update translation files for each language + for name in multilang.languages.values(): + if os.path.exists(os.path.join(TRANSLATIONS_DIR, f"app_{name}.po")): + cmd = f"msgmerge --update --no-fuzzy-matching --backup=none --sort-output translations/app_{name}.po translations/app.pot" + ret = os.system(cmd) + assert ret == 0 + else: + cmd = f"msginit -l {name} --no-translator --input translations/app.pot --output-file translations/app_{name}.po" + ret = os.system(cmd) + assert ret == 0 + + +if __name__ == "__main__": + update_translations() diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 0bd5b161b9..802243ff3e 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -15,9 +15,6 @@ from openpilot.system.ui.widgets.html_render import HtmlRenderer from openpilot.selfdrive.selfdrived.alertmanager import OFFROAD_ALERTS -NO_RELEASE_NOTES = tr("

No release notes available.

") - - class AlertColors: HIGH_SEVERITY = rl.Color(226, 44, 44, 255) LOW_SEVERITY = rl.Color(41, 41, 41, 255) @@ -56,21 +53,23 @@ class ButtonStyle(IntEnum): class ActionButton(Widget): - def __init__(self, text: str, style: ButtonStyle = ButtonStyle.LIGHT, + def __init__(self, text: str | Callable[[], str], style: ButtonStyle = ButtonStyle.LIGHT, min_width: int = AlertConstants.MIN_BUTTON_WIDTH): super().__init__() + self._text = text self._style = style self._min_width = min_width self._font = gui_app.font(FontWeight.MEDIUM) - self.set_text(text) - def set_text(self, text: str): - self._text = text - self._text_size = measure_text_cached(gui_app.font(FontWeight.MEDIUM), self._text, AlertConstants.FONT_SIZE) - self._rect.width = max(self._text_size.x + 60 * 2, self._min_width) - self._rect.height = AlertConstants.BUTTON_HEIGHT + @property + def text(self) -> str: + return self._text() if callable(self._text) else self._text def _render(self, _): + text_size = measure_text_cached(gui_app.font(FontWeight.MEDIUM), self.text, AlertConstants.FONT_SIZE) + self._rect.width = max(text_size.x + 60 * 2, self._min_width) + self._rect.height = AlertConstants.BUTTON_HEIGHT + roundness = AlertConstants.BORDER_RADIUS / self._rect.height bg_color = AlertColors.BUTTON if self._style == ButtonStyle.LIGHT else AlertColors.SNOOZE_BG if self.is_pressed: @@ -80,9 +79,9 @@ class ActionButton(Widget): # center text color = rl.WHITE if self._style == ButtonStyle.DARK else rl.BLACK - text_x = int(self._rect.x + (self._rect.width - self._text_size.x) // 2) - text_y = int(self._rect.y + (self._rect.height - self._text_size.y) // 2) - rl.draw_text_ex(self._font, self._text, rl.Vector2(text_x, text_y), AlertConstants.FONT_SIZE, 0, color) + text_x = int(self._rect.x + (self._rect.width - text_size.x) // 2) + text_y = int(self._rect.y + (self._rect.height - text_size.y) // 2) + rl.draw_text_ex(self._font, self.text, rl.Vector2(text_x, text_y), AlertConstants.FONT_SIZE, 0, color) class AbstractAlert(Widget, ABC): @@ -102,15 +101,15 @@ class AbstractAlert(Widget, ABC): if self.dismiss_callback: self.dismiss_callback() - self.dismiss_btn = ActionButton(tr("Close")) + self.dismiss_btn = ActionButton(lambda: tr("Close")) - self.snooze_btn = ActionButton(tr("Snooze Update"), style=ButtonStyle.DARK) + self.snooze_btn = ActionButton(lambda: tr("Snooze Update"), style=ButtonStyle.DARK) self.snooze_btn.set_click_callback(snooze_callback) - self.excessive_actuation_btn = ActionButton(tr("Acknowledge Excessive Actuation"), style=ButtonStyle.DARK, min_width=800) + self.excessive_actuation_btn = ActionButton(lambda: tr("Acknowledge Excessive Actuation"), style=ButtonStyle.DARK, min_width=800) self.excessive_actuation_btn.set_click_callback(excessive_actuation_callback) - self.reboot_btn = ActionButton(tr("Reboot and Update"), min_width=600) + self.reboot_btn = ActionButton(lambda: tr("Reboot and Update"), min_width=600) self.reboot_btn.set_click_callback(lambda: HARDWARE.reboot()) # TODO: just use a Scroller? @@ -318,12 +317,14 @@ class UpdateAlert(AbstractAlert): def refresh(self) -> bool: update_available: bool = self.params.get_bool("UpdateAvailable") + no_release_notes = "

" + tr("No release notes available.") + "

" + if update_available: self.release_notes = (self.params.get("UpdaterNewReleaseNotes") or b"").decode("utf8").strip() - self._html_renderer.parse_html_content(self.release_notes or NO_RELEASE_NOTES) + self._html_renderer.parse_html_content(self.release_notes or no_release_notes) self._cached_content_height = 0 else: - self._html_renderer.parse_html_content(NO_RELEASE_NOTES) + self._html_renderer.parse_html_content(no_release_notes) return update_available diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index ea88180ef8..fcc5a14103 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -16,10 +16,10 @@ class SetupWidget(Widget): super().__init__() self._open_settings_callback = None self._pairing_dialog: PairingDialog | None = None - self._pair_device_btn = Button(tr("Pair device"), self._show_pairing, button_style=ButtonStyle.PRIMARY) - self._open_settings_btn = Button(tr("Open"), lambda: self._open_settings_callback() if self._open_settings_callback else None, + self._pair_device_btn = Button(lambda: tr("Pair device"), self._show_pairing, button_style=ButtonStyle.PRIMARY) + self._open_settings_btn = Button(lambda: tr("Open"), lambda: self._open_settings_callback() if self._open_settings_callback else None, button_style=ButtonStyle.PRIMARY) - self._firehose_label = Label(tr("🔥 Firehose Mode 🔥"), font_weight=FontWeight.MEDIUM, font_size=64) + self._firehose_label = Label(lambda: tr("🔥 Firehose Mode 🔥"), font_weight=FontWeight.MEDIUM, font_size=64) def set_open_settings_callback(self, callback): self._open_settings_callback = callback diff --git a/selfdrive/ui/widgets/ssh_key.py b/selfdrive/ui/widgets/ssh_key.py index e44c4aad5d..88389cb053 100644 --- a/selfdrive/ui/widgets/ssh_key.py +++ b/selfdrive/ui/widgets/ssh_key.py @@ -7,7 +7,7 @@ from enum import Enum from openpilot.common.params import Params from openpilot.system.ui.lib.application import gui_app, FontWeight -from openpilot.system.ui.lib.multilang import tr +from openpilot.system.ui.lib.multilang import tr, tr_noop from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.button import Button, ButtonStyle @@ -26,9 +26,9 @@ VALUE_FONT_SIZE = 48 class SshKeyActionState(Enum): - LOADING = tr("LOADING") - ADD = tr("ADD") - REMOVE = tr("REMOVE") + LOADING = tr_noop("LOADING") + ADD = tr_noop("ADD") + REMOVE = tr_noop("REMOVE") class SshKeyAction(ItemAction): @@ -78,7 +78,7 @@ class SshKeyAction(ItemAction): # Draw button button_rect = rl.Rectangle(rect.x + rect.width - BUTTON_WIDTH, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT) self._button.set_rect(button_rect) - self._button.set_text(self._state.value) + self._button.set_text(tr(self._state.value)) self._button.set_enabled(self._state != SshKeyActionState.LOADING) self._button.render(button_rect) return False @@ -127,5 +127,5 @@ class SshKeyAction(ItemAction): self._state = SshKeyActionState.ADD -def ssh_key_item(title: str, description: str): +def ssh_key_item(title: str | Callable[[], str], description: str | Callable[[], str]) -> ListItem: return ListItem(title=title, description=description, action_item=SshKeyAction()) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index ff7163793d..476a33a99d 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -14,6 +14,7 @@ from typing import NamedTuple from importlib.resources import as_file, files from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE, PC, TICI +from openpilot.system.ui.lib.multilang import TRANSLATIONS_DIR, multilang from openpilot.common.realtime import Ratekeeper _DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) @@ -352,8 +353,17 @@ class GuiApplication: all_chars = set() for layout in KEYBOARD_LAYOUTS.values(): all_chars.update(key for row in layout for key in row) + all_chars |= set("–‑✓×°§•") + + # Load only the characters used in translations + for language in multilang.codes: + try: + with open(os.path.join(TRANSLATIONS_DIR, f"app_{language}.po")) as f: + all_chars |= set(f.read()) + except FileNotFoundError: + cloudlog.warning(f"Translation file for language '{language}' not found when loading fonts.") + all_chars = "".join(all_chars) - all_chars += "–✓×°§•" codepoint_count = rl.ffi.new("int *", 1) codepoints = rl.load_codepoints(all_chars, codepoint_count) diff --git a/system/ui/lib/multilang.py b/system/ui/lib/multilang.py index c020b33d5e..7670808924 100644 --- a/system/ui/lib/multilang.py +++ b/system/ui/lib/multilang.py @@ -1,10 +1,76 @@ import os +import json import gettext +from openpilot.common.params import Params from openpilot.common.basedir import BASEDIR +SYSTEM_UI_DIR = os.path.join(BASEDIR, "system", "ui") UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui") TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations") LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json") -tr = gettext.gettext -trn = gettext.ngettext +SUPPORTED_LANGUAGES = [ + "en", + "de", + "fr", + "pt-BR", + "es", + "tr", +] + + +class Multilang: + def __init__(self): + self._params = Params() + self.languages = {} + self.codes = {} + self._translation: gettext.NullTranslations | gettext.GNUTranslations = gettext.NullTranslations() + self._load_languages() + + @property + def language(self) -> str: + lang = str(self._params.get("LanguageSetting")).strip("main_") + if lang not in SUPPORTED_LANGUAGES: + lang = "en" + return lang + + def setup(self): + language = self.language + try: + with open(os.path.join(TRANSLATIONS_DIR, f'app_{language}.mo'), 'rb') as fh: + translation = gettext.GNUTranslations(fh) + translation.install() + self._translation = translation + print(f"Loaded translations for language: {language}") + except FileNotFoundError: + print(f"No translation file found for language: {language}, using default.") + gettext.install('app') + self._translation = gettext.NullTranslations() + return None + + def change_language(self, language_code: str) -> None: + # Reinstall gettext with the selected language + self._params.put("LanguageSetting", language_code) + self.setup() + + def tr(self, text: str) -> str: + return self._translation.gettext(text) + + def trn(self, singular: str, plural: str, n: int) -> str: + return self._translation.ngettext(singular, plural, n) + + def _load_languages(self): + with open(LANGUAGES_FILE, encoding='utf-8') as f: + self.languages = {k: v for k, v in json.load(f).items() if v in SUPPORTED_LANGUAGES} + self.codes = {v: k for k, v in self.languages.items() if v in SUPPORTED_LANGUAGES} + + +multilang = Multilang() +multilang.setup() + +tr, trn = multilang.tr, multilang.trn + + +# no-op marker for static strings translated later +def tr_noop(s: str) -> str: + return s diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index baf45ce95a..84969d032e 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -79,7 +79,7 @@ BUTTON_DISABLED_BACKGROUND_COLORS = { class Button(Widget): def __init__(self, - text: str, + text: str | Callable[[], str], click_callback: Callable[[], None] | None = None, font_size: int = DEFAULT_BUTTON_FONT_SIZE, font_weight: FontWeight = FontWeight.MEDIUM, diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 80b87483de..b0e1035687 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -78,7 +78,7 @@ class Keyboard(Widget): self._backspace_last_repeat: float = 0.0 self._render_return_status = -1 - self._cancel_button = Button(tr("Cancel"), self._cancel_button_callback) + self._cancel_button = Button(lambda: tr("Cancel"), self._cancel_button_callback) self._eye_button = Button("", self._eye_button_callback, button_style=ButtonStyle.TRANSPARENT) diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 99aed529a7..756495436b 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -1,3 +1,4 @@ +from collections.abc import Callable from itertools import zip_longest from typing import Union import pyray as rl @@ -12,6 +13,13 @@ from openpilot.system.ui.widgets import Widget ICON_PADDING = 15 +# TODO: make this common +def _resolve_value(value, default=""): + if callable(value): + return value() + return value if value is not None else default + + # TODO: This should be a Widget class def gui_label( rect: rl.Rectangle, @@ -90,7 +98,7 @@ def gui_text_box( # Non-interactive text area. Can render emojis and an optional specified icon. class Label(Widget): def __init__(self, - text: str, + text: str | Callable[[], str], font_size: int = DEFAULT_TEXT_SIZE, font_weight: FontWeight = FontWeight.NORMAL, text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_CENTER, @@ -130,7 +138,7 @@ class Label(Widget): def _update_text(self, text): self._emojis = [] self._text_size = [] - self._text_wrapped = wrap_text(self._font, text, self._font_size, round(self._rect.width - (self._text_padding * 2))) + self._text_wrapped = wrap_text(self._font, _resolve_value(text), self._font_size, round(self._rect.width - (self._text_padding * 2))) for t in self._text_wrapped: self._emojis.append(find_emoji(t)) self._text_size.append(measure_text_cached(self._font, t, self._font_size)) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index a02c7a1ebc..a604c8e16d 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -171,11 +171,9 @@ class TextAction(ItemAction): class DualButtonAction(ItemAction): - def __init__(self, left_text: str, right_text: str, left_callback: Callable = None, + def __init__(self, left_text: str | Callable[[], str], right_text: str | Callable[[], str], left_callback: Callable = None, right_callback: Callable = None, enabled: bool | Callable[[], bool] = True): super().__init__(width=0, enabled=enabled) # Width 0 means use full width - self.left_text, self.right_text = left_text, right_text - self.left_button = Button(left_text, click_callback=left_callback, button_style=ButtonStyle.NORMAL, text_padding=0) self.right_button = Button(right_text, click_callback=right_callback, button_style=ButtonStyle.DANGER, text_padding=0) @@ -206,7 +204,7 @@ class DualButtonAction(ItemAction): class MultipleButtonAction(ItemAction): - def __init__(self, buttons: list[str], button_width: int, selected_index: int = 0, callback: Callable = None): + def __init__(self, buttons: list[str | Callable[[], str]], button_width: int, selected_index: int = 0, callback: Callable = None): super().__init__(width=len(buttons) * button_width + (len(buttons) - 1) * RIGHT_ITEM_PADDING, enabled=True) self.buttons = buttons self.button_width = button_width @@ -225,7 +223,7 @@ class MultipleButtonAction(ItemAction): spacing = RIGHT_ITEM_PADDING button_y = rect.y + (rect.height - BUTTON_HEIGHT) / 2 - for i, text in enumerate(self.buttons): + for i, _text in enumerate(self.buttons): button_x = rect.x + i * (self.button_width + spacing) button_rect = rl.Rectangle(button_x, button_y, self.button_width, BUTTON_HEIGHT) @@ -249,6 +247,7 @@ class MultipleButtonAction(ItemAction): rl.draw_rectangle_rounded(button_rect, 1.0, 20, bg_color) # Draw text + text = _resolve_value(_text, "") text_size = measure_text_cached(self._font, text, 40) text_x = button_x + (self.button_width - text_size.x) / 2 text_y = button_y + (BUTTON_HEIGHT - text_size.y) / 2 @@ -258,7 +257,7 @@ class MultipleButtonAction(ItemAction): def _handle_mouse_release(self, mouse_pos: MousePos): spacing = RIGHT_ITEM_PADDING button_y = self._rect.y + (self._rect.height - BUTTON_HEIGHT) / 2 - for i, _text in enumerate(self.buttons): + for i, _ in enumerate(self.buttons): button_x = self._rect.x + i * (self.button_width + spacing) button_rect = rl.Rectangle(button_x, button_y, self.button_width, BUTTON_HEIGHT) if rl.check_collision_point_rec(mouse_pos, button_rect): @@ -268,11 +267,11 @@ class MultipleButtonAction(ItemAction): class ListItem(Widget): - def __init__(self, title: str = "", icon: str | None = None, description: str | Callable[[], str] | None = None, + def __init__(self, title: str | Callable[[], str] = "", icon: str | None = None, description: str | Callable[[], str] | None = None, description_visible: bool = False, callback: Callable | None = None, action_item: ItemAction | None = None): super().__init__() - self.title = title + self._title = title self.set_icon(icon) self._description = description self.description_visible = description_visible @@ -285,7 +284,7 @@ class ListItem(Widget): self._html_renderer = HtmlRenderer(text="", text_size={ElementType.P: ITEM_DESC_FONT_SIZE}, text_color=ITEM_DESC_TEXT_COLOR) - self.set_description(self.description) + self._parse_description(self.description) # Cached properties for performance self._prev_description: str | None = self.description @@ -332,7 +331,7 @@ class ListItem(Widget): # Detect changes if description is callback new_description = self.description if new_description != self._prev_description: - self.set_description(new_description) + self._parse_description(new_description) def _render(self, _): if not self.is_visible: @@ -385,10 +384,15 @@ class ListItem(Widget): def set_description(self, description: str | Callable[[], str] | None): self._description = description - new_desc = self.description + + def _parse_description(self, new_desc): self._html_renderer.parse_html_content(new_desc) self._prev_description = new_desc + @property + def title(self): + return _resolve_value(self._title, "") + @property def description(self): return _resolve_value(self._description, "") @@ -423,35 +427,35 @@ class ListItem(Widget): # Factory functions -def simple_item(title: str, callback: Callable | None = None) -> ListItem: +def simple_item(title: str | Callable[[], str], callback: Callable | None = None) -> ListItem: return ListItem(title=title, callback=callback) -def toggle_item(title: str, description: str | Callable[[], str] | None = None, initial_state: bool = False, +def toggle_item(title: str | Callable[[], str], description: str | Callable[[], str] | None = None, initial_state: bool = False, callback: Callable | None = None, icon: str = "", enabled: bool | Callable[[], bool] = True) -> ListItem: action = ToggleAction(initial_state=initial_state, enabled=enabled, callback=callback) return ListItem(title=title, description=description, action_item=action, icon=icon) -def button_item(title: str, button_text: str | Callable[[], str], description: str | Callable[[], str] | None = None, +def button_item(title: str | Callable[[], str], button_text: str | Callable[[], str], description: str | Callable[[], str] | None = None, callback: Callable | None = None, enabled: bool | Callable[[], bool] = True) -> ListItem: action = ButtonAction(text=button_text, enabled=enabled) return ListItem(title=title, description=description, action_item=action, callback=callback) -def text_item(title: str, value: str | Callable[[], str], description: str | Callable[[], str] | None = None, +def text_item(title: str | Callable[[], str], value: str | Callable[[], str], description: str | Callable[[], str] | None = None, callback: Callable | None = None, enabled: bool | Callable[[], bool] = True) -> ListItem: action = TextAction(text=value, color=ITEM_TEXT_VALUE_COLOR, enabled=enabled) return ListItem(title=title, description=description, action_item=action, callback=callback) -def dual_button_item(left_text: str, right_text: str, left_callback: Callable = None, right_callback: Callable = None, +def dual_button_item(left_text: str | Callable[[], str], right_text: str | Callable[[], str], left_callback: Callable = None, right_callback: Callable = None, description: str | Callable[[], str] | None = None, enabled: bool | Callable[[], bool] = True) -> ListItem: action = DualButtonAction(left_text, right_text, left_callback, right_callback, enabled) return ListItem(title="", description=description, action_item=action) -def multiple_button_item(title: str, description: str, buttons: list[str], selected_index: int, +def multiple_button_item(title: str | Callable[[], str], description: str | Callable[[], str], buttons: list[str | Callable[[], str]], selected_index: int, button_width: int = BUTTON_WIDTH, callback: Callable = None, icon: str = ""): action = MultipleButtonAction(buttons, button_width, selected_index, callback=callback) return ListItem(title=title, description=description, icon=icon, action_item=action) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index a59030363b..e705c55f7c 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -117,40 +117,42 @@ class AdvancedNetworkSettings(Widget): # Tethering self._tethering_action = ToggleAction(initial_state=False) - tethering_btn = ListItem(title=tr("Enable Tethering"), action_item=self._tethering_action, callback=self._toggle_tethering) + tethering_btn = ListItem(lambda: tr("Enable Tethering"), action_item=self._tethering_action, callback=self._toggle_tethering) # Edit tethering password - self._tethering_password_action = ButtonAction(text=tr("EDIT")) - tethering_password_btn = ListItem(title=tr("Tethering Password"), action_item=self._tethering_password_action, callback=self._edit_tethering_password) + self._tethering_password_action = ButtonAction(lambda: tr("EDIT")) + tethering_password_btn = ListItem(lambda: tr("Tethering Password"), action_item=self._tethering_password_action, callback=self._edit_tethering_password) # Roaming toggle roaming_enabled = self._params.get_bool("GsmRoaming") self._roaming_action = ToggleAction(initial_state=roaming_enabled) - self._roaming_btn = ListItem(title=tr("Enable Roaming"), action_item=self._roaming_action, callback=self._toggle_roaming) + self._roaming_btn = ListItem(lambda: tr("Enable Roaming"), action_item=self._roaming_action, callback=self._toggle_roaming) # Cellular metered toggle cellular_metered = self._params.get_bool("GsmMetered") self._cellular_metered_action = ToggleAction(initial_state=cellular_metered) - self._cellular_metered_btn = ListItem(title=tr("Cellular Metered"), description=tr("Prevent large data uploads when on a metered cellular connection"), + self._cellular_metered_btn = ListItem(lambda: tr("Cellular Metered"), + description=lambda: tr("Prevent large data uploads when on a metered cellular connection"), action_item=self._cellular_metered_action, callback=self._toggle_cellular_metered) # APN setting - self._apn_btn = button_item(tr("APN Setting"), tr("EDIT"), callback=self._edit_apn) + self._apn_btn = button_item(lambda: tr("APN Setting"), lambda: tr("EDIT"), callback=self._edit_apn) # Wi-Fi metered toggle - self._wifi_metered_action = MultipleButtonAction([tr("default"), tr("metered"), tr("unmetered")], 255, 0, callback=self._toggle_wifi_metered) - wifi_metered_btn = ListItem(title=tr("Wi-Fi Network Metered"), description=tr("Prevent large data uploads when on a metered Wi-Fi connection"), + self._wifi_metered_action = MultipleButtonAction([lambda: tr("default"), lambda: tr("metered"), lambda: tr("unmetered")], 255, 0, + callback=self._toggle_wifi_metered) + wifi_metered_btn = ListItem(lambda: tr("Wi-Fi Network Metered"), description=lambda: tr("Prevent large data uploads when on a metered Wi-Fi connection"), action_item=self._wifi_metered_action) items: list[Widget] = [ tethering_btn, tethering_password_btn, - text_item(tr("IP Address"), lambda: self._wifi_manager.ipv4_address), + text_item(lambda: tr("IP Address"), lambda: self._wifi_manager.ipv4_address), self._roaming_btn, self._apn_btn, self._cellular_metered_btn, wifi_metered_btn, - button_item(tr("Hidden Network"), tr("CONNECT"), callback=self._connect_to_hidden_network), + button_item(lambda: tr("Hidden Network"), lambda: tr("CONNECT"), callback=self._connect_to_hidden_network), ] self._scroller = Scroller(items, line_separator=True, spacing=0) @@ -282,7 +284,6 @@ class WifiManagerUI(Widget): self._networks: list[Network] = [] self._networks_buttons: dict[str, Button] = {} self._forget_networks_buttons: dict[str, Button] = {} - self._confirm_dialog = ConfirmDialog("", tr("Forget"), tr("Cancel")) self._wifi_manager.set_callbacks(need_auth=self._on_need_auth, activated=self._on_activated, @@ -314,9 +315,10 @@ class WifiManagerUI(Widget): self.keyboard.reset(min_text_size=MIN_PASSWORD_LENGTH) gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(cast(Network, self._state_network), result)) elif self.state == UIState.SHOW_FORGET_CONFIRM and self._state_network: - self._confirm_dialog.set_text(tr("Forget Wi-Fi Network \"{}\"?").format(self._state_network.ssid)) - self._confirm_dialog.reset() - gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(self._state_network, result)) + confirm_dialog = ConfirmDialog("", tr("Forget"), tr("Cancel")) + confirm_dialog.set_text(tr("Forget Wi-Fi Network \"{}\"?").format(self._state_network.ssid)) + confirm_dialog.reset() + gui_app.set_modal_overlay(confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(self._state_network, result)) else: self._draw_network_list(rect) From a8660b5b4fef2827ee218d3641e4c456211527ea Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 22 Oct 2025 17:55:28 -0700 Subject: [PATCH 233/341] Revert "raylib: add branch switcher (#36411)" This reverts commit 856f8d3d47983a80bfdb08d2208bcef9278f5232. --- selfdrive/ui/layouts/settings/software.py | 32 +++-------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 73c8946f55..6f4629c75f 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -8,7 +8,6 @@ from openpilot.system.ui.lib.multilang import tr, trn from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.list_view import button_item, text_item, ListItem -from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog from openpilot.system.ui.widgets.scroller import Scroller # TODO: remove this. updater fails to respond on startup if time is not correct @@ -53,9 +52,6 @@ class SoftwareLayout(Widget): self._install_btn = button_item(lambda: tr("Install Update"), lambda: tr("INSTALL"), callback=self._on_install_update) self._install_btn.set_visible(False) - self._select_branch_dialog: MultiOptionDialog | None = None - self._target_branch_btn = button_item(tr("Target Branch"), tr("SELECT"), callback=self._on_select_branch) - # Track waiting-for-updater transition to avoid brief re-enable while still idle self._waiting_for_updater = False self._waiting_start_ts: float = 0.0 @@ -69,7 +65,8 @@ class SoftwareLayout(Widget): self._version_item, self._download_btn, self._install_btn, - self._target_branch_btn, + # TODO: implement branch switching + # button_item("Target Branch", "SELECT", callback=self._on_select_branch), button_item(lambda: tr("Uninstall"), lambda: tr("UNINSTALL"), callback=self._on_uninstall), ] return items @@ -90,8 +87,6 @@ class SoftwareLayout(Widget): self._version_item.action_item.set_text(current_desc) self._version_item.set_description(current_release_notes) - self._target_branch_btn.action_item.set_value(ui_state.params.get("UpdaterTargetBranch") or "") - # Update download button visibility and state self._download_btn.set_visible(ui_state.is_offroad()) @@ -168,25 +163,4 @@ class SoftwareLayout(Widget): self._install_btn.action_item.set_enabled(False) ui_state.params.put_bool("DoReboot", True) - def _on_select_branch(self): - current_branch = ui_state.params.get("GitBranch") or "" - branches_str = ui_state.params.get("UpdaterAvailableBranches") or "" - - available_branches = [b.strip() for b in branches_str.split(",") if b.strip()] - priority_branches = [current_branch, "devel-staging", "devel", "nightly", "nightly-dev", "master"] - for branch in priority_branches: - if branch in available_branches: - available_branches.remove(branch) - available_branches.insert(0, branch) - - self._select_branch_dialog = MultiOptionDialog(tr("Select a branch"), available_branches, current=current_branch) - gui_app.set_modal_overlay(self._select_branch_dialog, callback=self._handle_branch_selection) - - def _handle_branch_selection(self, result: int): - if result == 1 and self._select_branch_dialog: - selected = self._select_branch_dialog.selection - self._target_branch_btn.action_item.set_value(selected) - ui_state.params.put("UpdaterTargetBranch", selected) - os.system("pkill -SIGHUP -f system.updated.updated") - - self._select_branch_dialog = None + def _on_select_branch(self): pass From 2c41dbc472e554381048234c0481b957486a9fb0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 22 Oct 2025 18:10:32 -0700 Subject: [PATCH 234/341] raylib: hit rect for scroller items (#36432) * hit rect * clean up * comment * oh this is actually epic * rm line * type --- system/ui/widgets/__init__.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/system/ui/widgets/__init__.py b/system/ui/widgets/__init__.py index 0157dd3ef4..562cde39e1 100644 --- a/system/ui/widgets/__init__.py +++ b/system/ui/widgets/__init__.py @@ -14,7 +14,7 @@ class DialogResult(IntEnum): class Widget(abc.ABC): def __init__(self): self._rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) - self._parent_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) + self._parent_rect: rl.Rectangle | None = None self.__is_pressed = [False] * MAX_TOUCH_SLOTS # if current mouse/touch down started within the widget's rectangle self.__tracking_is_pressed = [False] * MAX_TOUCH_SLOTS @@ -75,6 +75,13 @@ class Widget(abc.ABC): if changed: self._update_layout_rects() + @property + def _hit_rect(self) -> rl.Rectangle: + # restrict touches to within parent rect if set, useful inside Scroller + if self._parent_rect is None: + return self._rect + return rl.get_collision_rec(self._rect, self._parent_rect) + def render(self, rect: rl.Rectangle = None) -> bool | int | None: if rect is not None: self.set_rect(rect) @@ -95,7 +102,7 @@ class Widget(abc.ABC): # Ignores touches/presses that start outside our rect # Allows touch to leave the rect and come back in focus if mouse did not release if mouse_event.left_pressed and self._touch_valid(): - if rl.check_collision_point_rec(mouse_event.pos, self._rect): + if rl.check_collision_point_rec(mouse_event.pos, self._hit_rect): self._handle_mouse_press(mouse_event.pos) self.__is_pressed[mouse_event.slot] = True self.__tracking_is_pressed[mouse_event.slot] = True @@ -106,18 +113,18 @@ class Widget(abc.ABC): self.__tracking_is_pressed[mouse_event.slot] = False elif mouse_event.left_released: - if self.__is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): + if self.__is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._hit_rect): self._handle_mouse_release(mouse_event.pos) self.__is_pressed[mouse_event.slot] = False self.__tracking_is_pressed[mouse_event.slot] = False # Mouse/touch is still within our rect - elif rl.check_collision_point_rec(mouse_event.pos, self._rect): + elif rl.check_collision_point_rec(mouse_event.pos, self._hit_rect): if self.__tracking_is_pressed[mouse_event.slot]: self.__is_pressed[mouse_event.slot] = True # Mouse/touch left our rect but may come back into focus later - elif not rl.check_collision_point_rec(mouse_event.pos, self._rect): + elif not rl.check_collision_point_rec(mouse_event.pos, self._hit_rect): self.__is_pressed[mouse_event.slot] = False return ret From 8f720a54f6c9c590fedfa9006745d354b7de117f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 22 Oct 2025 18:54:09 -0700 Subject: [PATCH 235/341] raylib: add branch switcher (#36359) * it's adversarial * try 2 * just do this * kinda works but doesn' tmatch * fine * qt is banned word * test * fix test * add elide support to Label * fixup * Revert "add elide support to Label" This reverts commit 28c3e0e7457345083d93f7b6a909a4103bd50d55. * Reapply "add elide support to Label" This reverts commit 92c2d6694146f164f30060d7621e19006e2fe2df. * todo * elide button value properly + debug/stash * clean up * clean up * yep looks good * clean up * eval visible once * no s * don't need * can do this * but this also works * clip to parent rect * fixes and multilang * clean up * set target branch * whops --- selfdrive/ui/layouts/settings/software.py | 46 +++++++++++++++---- .../ui/tests/test_ui/raylib_screenshots.py | 19 ++++++-- system/ui/widgets/button.py | 3 +- system/ui/widgets/label.py | 34 ++++++++++++-- system/ui/widgets/list_view.py | 19 +++++--- system/ui/widgets/option_dialog.py | 6 +-- 6 files changed, 101 insertions(+), 26 deletions(-) diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 6f4629c75f..09b09ddec1 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -8,6 +8,7 @@ from openpilot.system.ui.lib.multilang import tr, trn from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.list_view import button_item, text_item, ListItem +from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog from openpilot.system.ui.widgets.scroller import Scroller # TODO: remove this. updater fails to respond on startup if time is not correct @@ -56,20 +57,20 @@ class SoftwareLayout(Widget): self._waiting_for_updater = False self._waiting_start_ts: float = 0.0 - items = self._init_items() - self._scroller = Scroller(items, line_separator=True, spacing=0) + # Branch switcher + self._branch_btn = button_item(lambda: tr("Target Branch"), lambda: tr("SELECT"), callback=self._on_select_branch) + self._branch_btn.set_visible(not ui_state.params.get_bool("IsTestedBranch")) + self._branch_btn.action_item.set_value(ui_state.params.get("UpdaterTargetBranch") or "") + self._branch_dialog: MultiOptionDialog | None = None - def _init_items(self): - items = [ + self._scroller = Scroller([ self._onroad_label, self._version_item, self._download_btn, self._install_btn, - # TODO: implement branch switching - # button_item("Target Branch", "SELECT", callback=self._on_select_branch), + self._branch_btn, button_item(lambda: tr("Uninstall"), lambda: tr("UNINSTALL"), callback=self._on_uninstall), - ] - return items + ], line_separator=True, spacing=0) def show_event(self): self._scroller.show_event() @@ -123,6 +124,10 @@ class SoftwareLayout(Widget): # Only enable if we're not waiting for updater to flip out of idle self._download_btn.action_item.set_enabled(not self._waiting_for_updater) + # Update target branch button value + current_branch = ui_state.params.get("UpdaterTargetBranch") or "" + self._branch_btn.action_item.set_value(current_branch) + # Update install button self._install_btn.set_visible(ui_state.is_offroad() and update_available) if update_available: @@ -163,4 +168,27 @@ class SoftwareLayout(Widget): self._install_btn.action_item.set_enabled(False) ui_state.params.put_bool("DoReboot", True) - def _on_select_branch(self): pass + def _on_select_branch(self): + # Get available branches and order + current_git_branch = ui_state.params.get("GitBranch") or "" + branches_str = ui_state.params.get("UpdaterAvailableBranches") or "" + branches = [b for b in branches_str.split(",") if b] + + for b in [current_git_branch, "devel-staging", "devel", "nightly", "nightly-dev", "master"]: + if b in branches: + branches.remove(b) + branches.insert(0, b) + + current_target = ui_state.params.get("UpdaterTargetBranch") or "" + self._branch_dialog = MultiOptionDialog("Select a branch", branches, current_target) + + def handle_selection(result): + # Confirmed selection + if result == DialogResult.CONFIRM and self._branch_dialog is not None and self._branch_dialog.selection: + selection = self._branch_dialog.selection + ui_state.params.put("UpdaterTargetBranch", selection) + self._branch_btn.action_item.set_value(selection) + os.system("pkill -SIGUSR1 -f system.updated.updated") + self._branch_dialog = None + + gui_app.set_modal_overlay(self._branch_dialog, callback=handle_selection) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 2e96b3bd43..29fae5b61c 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -27,6 +27,9 @@ TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" UI_DELAY = 0.2 +BRANCH_NAME = "this-is-a-really-super-mega-ultra-max-extreme-ultimate-long-branch-name" +VERSION = f"0.10.1 / {BRANCH_NAME} / 7864838 / Oct 03" + # Offroad alerts to test OFFROAD_ALERTS = ['Offroad_IsTakingSnapshot'] @@ -34,6 +37,7 @@ OFFROAD_ALERTS = ['Offroad_IsTakingSnapshot'] def put_update_params(params: Params): params.put("UpdaterCurrentReleaseNotes", parse_release_notes(BASEDIR)) params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR)) + params.put("UpdaterTargetBranch", BRANCH_NAME) def setup_homescreen(click, pm: PubMaster): @@ -89,6 +93,15 @@ def setup_settings_software_release_notes(click, pm: PubMaster): click(588, 110) # expand description for current version +def setup_settings_software_branch_switcher(click, pm: PubMaster): + setup_settings_software(click, pm) + params = Params() + params.put("UpdaterAvailableBranches", f"master,nightly,release,{BRANCH_NAME}") + params.put("GitBranch", BRANCH_NAME) # should be on top + params.put("UpdaterTargetBranch", "nightly") # should be selected + click(1984, 449) + + def setup_settings_firehose(click, pm: PubMaster): setup_settings(click, pm) click(278, 845) @@ -240,6 +253,7 @@ CASES = { "settings_software": setup_settings_software, "settings_software_download": setup_settings_software_download, "settings_software_release_notes": setup_settings_software_release_notes, + "settings_software_branch_switcher": setup_settings_software_branch_switcher, "settings_firehose": setup_settings_firehose, "settings_developer": setup_settings_developer, "keyboard": setup_keyboard, @@ -309,9 +323,8 @@ def create_screenshots(): params.put("DongleId", "123456789012345") # Set branch name - description = "0.10.1 / this-is-a-really-super-mega-long-branch-name / 7864838 / Oct 03" - params.put("UpdaterCurrentDescription", description) - params.put("UpdaterNewDescription", description) + params.put("UpdaterCurrentDescription", VERSION) + params.put("UpdaterNewDescription", VERSION) if name == "homescreen_paired": params.put("PrimeType", 0) # NONE diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 84969d032e..bd6517ad49 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -88,6 +88,7 @@ class Button(Widget): text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_CENTER, text_padding: int = 20, icon=None, + elide_right: bool = False, multi_touch: bool = False, ): @@ -97,7 +98,7 @@ class Button(Widget): self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] self._label = Label(text, font_size, font_weight, text_alignment, text_padding=text_padding, - text_color=BUTTON_TEXT_COLOR[self._button_style], icon=icon) + text_color=BUTTON_TEXT_COLOR[self._button_style], icon=icon, elide_right=elide_right) self._click_callback = click_callback self._multi_touch = multi_touch diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 756495436b..10f6f1400d 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -37,17 +37,17 @@ def gui_label( # Elide text to fit within the rectangle if elide_right and text_size.x > rect.width: - ellipsis = "..." + _ellipsis = "..." left, right = 0, len(text) while left < right: mid = (left + right) // 2 - candidate = text[:mid] + ellipsis + candidate = text[:mid] + _ellipsis candidate_size = measure_text_cached(font, candidate, font_size) if candidate_size.x <= rect.width: left = mid + 1 else: right = mid - display_text = text[: left - 1] + ellipsis if left > 0 else ellipsis + display_text = text[: left - 1] + _ellipsis if left > 0 else _ellipsis text_size = measure_text_cached(font, display_text, font_size) # Calculate horizontal position based on alignment @@ -106,6 +106,7 @@ class Label(Widget): text_padding: int = 0, text_color: rl.Color = DEFAULT_TEXT_COLOR, icon: Union[rl.Texture, None] = None, # noqa: UP007 + elide_right: bool = False, ): super().__init__() @@ -117,6 +118,7 @@ class Label(Widget): self._text_padding = text_padding self._text_color = text_color self._icon = icon + self._elide_right = elide_right self._text = text self.set_text(text) @@ -138,7 +140,31 @@ class Label(Widget): def _update_text(self, text): self._emojis = [] self._text_size = [] - self._text_wrapped = wrap_text(self._font, _resolve_value(text), self._font_size, round(self._rect.width - (self._text_padding * 2))) + text = _resolve_value(text) + + if self._elide_right: + display_text = text + + # Elide text to fit within the rectangle + text_size = measure_text_cached(self._font, text, self._font_size) + content_width = self._rect.width - self._text_padding * 2 + if text_size.x > content_width: + _ellipsis = "..." + left, right = 0, len(text) + while left < right: + mid = (left + right) // 2 + candidate = text[:mid] + _ellipsis + candidate_size = measure_text_cached(self._font, candidate, self._font_size) + if candidate_size.x <= content_width: + left = mid + 1 + else: + right = mid + display_text = text[: left - 1] + _ellipsis if left > 0 else _ellipsis + + self._text_wrapped = [display_text] + else: + self._text_wrapped = wrap_text(self._font, text, self._font_size, round(self._rect.width - (self._text_padding * 2))) + for t in self._text_wrapped: self._emojis.append(find_emoji(t)) self._text_size.append(measure_text_cached(self._font, t, self._font_size)) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index a604c8e16d..e5f234ed39 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -100,6 +100,14 @@ class ButtonAction(ItemAction): ) self.set_enabled(enabled) + def get_width_hint(self) -> float: + value_text = self.value + if value_text: + text_width = measure_text_cached(self._font, value_text, ITEM_TEXT_FONT_SIZE).x + return text_width + BUTTON_WIDTH + TEXT_PADDING + else: + return BUTTON_WIDTH + def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: super().set_touch_valid_callback(touch_callback) self._button.set_touch_valid_callback(touch_callback) @@ -121,16 +129,15 @@ class ButtonAction(ItemAction): def _render(self, rect: rl.Rectangle) -> bool: self._button.set_text(self.text) self._button.set_enabled(_resolve_value(self.enabled)) - button_rect = rl.Rectangle(rect.x, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT) + button_rect = rl.Rectangle(rect.x + rect.width - BUTTON_WIDTH, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT) self._button.render(button_rect) value_text = self.value if value_text: - spacing = 20 - text_size = measure_text_cached(self._font, value_text, ITEM_TEXT_FONT_SIZE) - text_x = button_rect.x - spacing - text_size.x - text_y = rect.y + (rect.height - text_size.y) / 2 - rl.draw_text_ex(self._font, value_text, rl.Vector2(text_x, text_y), ITEM_TEXT_FONT_SIZE, 0, ITEM_TEXT_VALUE_COLOR) + value_rect = rl.Rectangle(rect.x, rect.y, rect.width - BUTTON_WIDTH - TEXT_PADDING, rect.height) + gui_label(value_rect, value_text, font_size=ITEM_TEXT_FONT_SIZE, color=ITEM_TEXT_VALUE_COLOR, + font_weight=FontWeight.NORMAL, alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, + alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE) # TODO: just use the generic Widget click callbacks everywhere, no returning from render pressed = self._pressed diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index 8c63ca3f9f..fc42b48081 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -28,11 +28,11 @@ class MultiOptionDialog(Widget): # Create scroller with option buttons self.option_buttons = [Button(option, click_callback=lambda opt=option: self._on_option_clicked(opt), text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.NORMAL, - text_padding=50) for option in options] + text_padding=50, elide_right=True) for option in options] self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING) - self.cancel_button = Button(tr("Cancel"), click_callback=lambda: self._set_result(DialogResult.CANCEL)) - self.select_button = Button(tr("Select"), click_callback=lambda: self._set_result(DialogResult.CONFIRM), button_style=ButtonStyle.PRIMARY) + self.cancel_button = Button(lambda: tr("Cancel"), click_callback=lambda: self._set_result(DialogResult.CANCEL)) + self.select_button = Button(lambda: tr("Select"), click_callback=lambda: self._set_result(DialogResult.CONFIRM), button_style=ButtonStyle.PRIMARY) def _set_result(self, result: DialogResult): self._result = result From b14270bd7122ac7a8f0e5e200dd705b2a3bc020f Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Wed, 22 Oct 2025 18:56:18 -0700 Subject: [PATCH 236/341] update RELEASES.md --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 568be6c353..558d322094 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,6 @@ Version 0.10.1 (2025-09-08) ======================== -* New driving model #36114 +* New driving model #36276 * World Model: removed global localization inputs * World Model: 2x the number of parameters * World Model: trained on 4x the number of segments From a0d48b6c63ead47cd0fe031a6cc92a7eb3d29464 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 22 Oct 2025 19:18:54 -0700 Subject: [PATCH 237/341] raylib: unifont for CJK languages (#36430) * add rest of langs * unifont * all langs are supported * add japanese translations * fix strip! * add language name chars * use unifont in lang selection * add korean * test all langs * doesn't work * unifont font fallback for multilang * add ar translations * fix labels not updating until scrolling * t chinese * more chn * we already default * wrap * update * fix thai * fix missing chinese langs and all are supported! * clean up * update * ??? mypy r u ok ??? * fix default option font weight --- .gitattributes | 1 + selfdrive/assets/fonts/unifont.otf | 3 + selfdrive/ui/layouts/settings/device.py | 5 +- selfdrive/ui/layouts/settings/software.py | 2 +- selfdrive/ui/translations/app.pot | 113 +- selfdrive/ui/translations/app_ar.po | 1237 +++++++++++++++++++++ selfdrive/ui/translations/app_de.po | 113 +- selfdrive/ui/translations/app_en.po | 113 +- selfdrive/ui/translations/app_es.po | 113 +- selfdrive/ui/translations/app_fr.po | 113 +- selfdrive/ui/translations/app_ja.po | 1216 ++++++++++++++++++++ selfdrive/ui/translations/app_ko.po | 1209 ++++++++++++++++++++ selfdrive/ui/translations/app_pt-BR.po | 113 +- selfdrive/ui/translations/app_th.po | 1148 +++++++++++++++++++ selfdrive/ui/translations/app_tr.po | 113 +- selfdrive/ui/translations/app_zh-CHS.po | 1193 ++++++++++++++++++++ selfdrive/ui/translations/app_zh-CHT.po | 1192 ++++++++++++++++++++ system/ui/lib/application.py | 19 +- system/ui/lib/multilang.py | 42 +- system/ui/lib/text_measure.py | 3 +- system/ui/lib/wrap_text.py | 2 + system/ui/widgets/label.py | 7 +- system/ui/widgets/option_dialog.py | 3 +- 23 files changed, 7700 insertions(+), 373 deletions(-) create mode 100644 selfdrive/assets/fonts/unifont.otf create mode 100644 selfdrive/ui/translations/app_ar.po create mode 100644 selfdrive/ui/translations/app_ja.po create mode 100644 selfdrive/ui/translations/app_ko.po create mode 100644 selfdrive/ui/translations/app_th.po create mode 100644 selfdrive/ui/translations/app_zh-CHS.po create mode 100644 selfdrive/ui/translations/app_zh-CHT.po diff --git a/.gitattributes b/.gitattributes index 50ac49dd7c..41a7367d84 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,6 +7,7 @@ *.png filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text *.ttf filter=lfs diff=lfs merge=lfs -text +*.otf filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text diff --git a/selfdrive/assets/fonts/unifont.otf b/selfdrive/assets/fonts/unifont.otf new file mode 100644 index 0000000000..b85597b1f4 --- /dev/null +++ b/selfdrive/assets/fonts/unifont.otf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9712a9bc089af7ddc06e0826aa84f2ee23ed2f1a1dddaf2a89c2483e753a8475 +size 5321484 diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 3f762a8842..f5f37fbd3c 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -10,7 +10,7 @@ from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog from openpilot.system.hardware import TICI -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import FontWeight, gui_app from openpilot.system.ui.lib.multilang import multilang, tr, tr_noop from openpilot.system.ui.widgets import Widget, DialogResult from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog @@ -88,7 +88,8 @@ class DeviceLayout(Widget): self._update_calib_description() self._select_language_dialog = None - self._select_language_dialog = MultiOptionDialog(tr("Select a language"), multilang.languages, multilang.codes[multilang.language]) + self._select_language_dialog = MultiOptionDialog(tr("Select a language"), multilang.languages, multilang.codes[multilang.language], + option_font_weight=FontWeight.UNIFONT) gui_app.set_modal_overlay(self._select_language_dialog, callback=handle_language_selection) def _show_driver_camera(self): diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 09b09ddec1..8166a8a9e4 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -180,7 +180,7 @@ class SoftwareLayout(Widget): branches.insert(0, b) current_target = ui_state.params.get("UpdaterTargetBranch") or "" - self._branch_dialog = MultiOptionDialog("Select a branch", branches, current_target) + self._branch_dialog = MultiOptionDialog(tr("Select a branch"), branches, current_target) def handle_selection(result): # Confirmed selection diff --git a/selfdrive/ui/translations/app.pot b/selfdrive/ui/translations/app.pot index b68a197870..e6a0e880db 100644 --- a/selfdrive/ui/translations/app.pot +++ b/selfdrive/ui/translations/app.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"POT-Creation-Date: 2025-10-22 19:09-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 #: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 #, python-format @@ -26,14 +26,14 @@ msgid "OK" msgstr "" #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 #: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 #: /home/batman/openpilot/system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" @@ -193,8 +193,8 @@ msgstr "" msgid "FORGETTING..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" @@ -609,112 +609,127 @@ msgstr "" msgid "Enable" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " @@ -752,13 +767,13 @@ msgid "RESET" msgstr "" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "" @@ -824,89 +839,89 @@ msgstr "" msgid "Select a language" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " "Resetting calibration will restart openpilot if the car is powered on." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "" diff --git a/selfdrive/ui/translations/app_ar.po b/selfdrive/ui/translations/app_ar.po new file mode 100644 index 0000000000..5860c94ddd --- /dev/null +++ b/selfdrive/ui/translations/app_ar.po @@ -0,0 +1,1237 @@ +# Arabic translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"PO-Revision-Date: 2025-10-22 16:32-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: ar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0?0:n==1?1:n==2?2:(n%100>=3 && " +"n%100<=10)?3:(n%100>=11 && n%100<=99)?4:5;\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " اكتملت معايرة استجابة عزم التوجيه." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " اكتملت معايرة استجابة عزم التوجيه بنسبة {}٪." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " جهازك موجه بمقدار {:.1f}° {} و {:.1f}° {}." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "سنة واحدة من تخزين القيادة" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "اتصال LTE على مدار الساعة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"تحذير: التحكم الطولي لـ openpilot في مرحلة ألفا لهذه السيارة وسيُعطّل نظام " +"الكبح التلقائي في حالات الطوارئ (AEB).

في هذه السيارة، يعتمد " +"openpilot افتراضياً على نظام ACC المدمج بدلاً من التحكم الطولي لـ openpilot. " +"فعّل هذا الخيار للتبديل إلى التحكم الطولي لـ openpilot. يُنصح بتمكين وضع " +"التجربة عند تفعيل نسخة ألفا من التحكم الطولي. تغيير هذا الإعداد سيعيد تشغيل " +"openpilot إذا كانت السيارة قيد التشغيل." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "

اكتملت معايرة تأخر التوجيه." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "

اكتملت معايرة تأخر التوجيه بنسبة {}٪." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "نشط" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"يتيح ADB (Android Debug Bridge) الاتصال بجهازك عبر USB أو عبر الشبكة. راجع " +"https://docs.comma.ai/how-to/connect-to-comma لمزيد من المعلومات." + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "إضافة" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "إعداد APN" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "تأكيد التشغيل المفرط" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "متقدم" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "عدواني" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "موافقة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "مراقبة السائق دائماً" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"يمكن اختبار نسخة ألفا من التحكم الطولي لـ openpilot، مع وضع التجربة، على " +"الفروع غير الإصدارية." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "هل أنت متأكد أنك تريد إيقاف التشغيل؟" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "هل أنت متأكد أنك تريد إعادة التشغيل؟" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "هل أنت متأكد أنك تريد إعادة ضبط المعايرة؟" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "هل أنت متأكد أنك تريد إلغاء التثبيت؟" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "رجوع" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "انضم إلى comma prime عبر connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "ثبّت connect.comma.ai على شاشتك الرئيسية لاستخدامه كتطبيق" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "تغيير" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#, python-format +msgid "CHECK" +msgstr "تحقق" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "وضع الهدوء مُفعل" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "CONNECT" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "CONNECTING..." + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "إلغاء" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "خلوي بتعرفة محدودة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "تغيير اللغة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "" +"سيؤدي تغيير هذا الإعداد إلى إعادة تشغيل openpilot إذا كانت السيارة قيد " +"التشغيل." + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "اضغط \"إضافة جهاز جديد\" ثم امسح رمز QR على اليمين" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "إغلاق" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Current Version" +msgstr "الإصدار الحالي" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#, python-format +msgid "DOWNLOAD" +msgstr "تنزيل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "رفض" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "رفض، وإلغاء تثبيت openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "المطور" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "الجهاز" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "فصل عند الضغط على دواسة الوقود" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#, python-format +msgid "Disengage to Power Off" +msgstr "افصل لإيقاف التشغيل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#, python-format +msgid "Disengage to Reboot" +msgstr "افصل لإعادة التشغيل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "افصل لإعادة ضبط المعايرة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "عرض السرعة بالكيلومتر/ساعة بدلاً من الميل/ساعة." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "معرّف الدونجل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#, python-format +msgid "Download" +msgstr "تنزيل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "كاميرا السائق" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "شخصية القيادة" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "تعديل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "خطأ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "وضع التجربة مُفعل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "تمكين" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "تمكين ADB" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "تمكين تحذيرات مغادرة المسار" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "تمكين التجوال" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "تمكين SSH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "تمكين الربط" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "تمكين مراقبة السائق حتى عندما لا يكون openpilot مُشغلاً." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "تمكين openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "فعّل تبديل التحكم الطولي (ألفا) لـ openpilot للسماح بوضع التجربة." + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "أدخل APN" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "أدخل SSID" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "أدخل كلمة مرور الربط الجديدة" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "أدخل كلمة المرور" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "أدخل اسم مستخدم GitHub الخاص بك" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#, python-format +msgid "Error" +msgstr "خطأ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "وضع التجربة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"وضع التجربة غير متاح حالياً في هذه السيارة لأن نظام ACC الأصلي يُستخدم للتحكم " +"الطولي." + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "جارٍ النسيان..." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "إنهاء الإعداد" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "وضع Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"لأقصى فعالية، أحضر جهازك إلى الداخل واتصل بمحوّل USB‑C جيد وبشبكة Wi‑Fi " +"أسبوعياً.\n" +"\n" +"يمكن أن يعمل وضع Firehose أيضاً أثناء القيادة إذا كنت متصلاً بنقطة اتصال أو " +"بشريحة غير محدودة.\n" +"\n" +"\n" +"الأسئلة الشائعة\n" +"\n" +"هل يهم كيف أو أين أقود؟ لا، قد بقدر المعتاد.\n" +"\n" +"هل يتم سحب كل مقاطعي في وضع Firehose؟ لا، نقوم بسحب مجموعة فرعية من " +"المقاطع.\n" +"\n" +"ما هو محول USB‑C الجيد؟ أي شاحن هاتف أو حاسب محمول سريع سيكون مناسباً.\n" +"\n" +"هل يهم أي برنامج أشغّل؟ نعم، فقط openpilot الأصلي (وبعض التفرعات المحددة) " +"يمكن استخدامه للتدريب." + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "نسيان" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "هل تريد نسيان شبكة Wi‑Fi \"{}\"؟" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "جيد" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "اذهب إلى https://connect.comma.ai على هاتفك" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "مرتفع" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "شبكة مخفية" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "غير نشط: اتصل بشبكة غير محدودة التعرفة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#, python-format +msgid "INSTALL" +msgstr "تثبيت" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "عنوان IP" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#, python-format +msgid "Install Update" +msgstr "تثبيت التحديث" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "وضع تصحيح عصا التحكم" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "جارٍ التحميل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "وضع المناورة الطولية" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "أقصى" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "زد من تحميل بيانات التدريب لتحسين نماذج قيادة openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "غير متوفر" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "لا" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "الشبكة" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "لم يتم العثور على مفاتيح SSH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "لم يتم العثور على مفاتيح SSH للمستخدم '{}'" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "لا توجد ملاحظات إصدار متاحة." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "غير متصل" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "موافق" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "متصل" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "فتح" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "إقران" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "معاينة" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "ميزات PRIME:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "إقران الجهاز" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "إقران الجهاز" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "قم بإقران جهازك بحساب comma" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"أقرِن جهازك مع comma connect (connect.comma.ai) واحصل على عرض comma prime." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "يرجى الاتصال بشبكة Wi‑Fi لإكمال الاقتران الأولي" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Power Off" +msgstr "إيقاف التشغيل" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "منع رفع البيانات الكبيرة عند الاتصال بشبكة Wi‑Fi محدودة التعرفة" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "منع رفع البيانات الكبيرة عند الاتصال الخلوي محدود التعرفة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"عاين كاميرا مواجهة السائق للتأكد من أن مراقبة السائق تتم برؤية جيدة. (يجب أن " +"تكون المركبة متوقفة)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "خطأ في رمز QR" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "إزالة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "إعادة ضبط" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "مراجعة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Reboot" +msgstr "إعادة التشغيل" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "إعادة تشغيل الجهاز" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "إعادة التشغيل والتحديث" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"استقبال تنبيهات للتوجيه للعودة إلى المسار عند انحراف المركبة فوق خط المسار " +"المُكتشف بدون إشارة انعطاف مفعّلة أثناء القيادة فوق 31 ميل/س (50 كم/س)." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "تسجيل ورفع فيديو كاميرا السائق" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "تسجيل ورفع صوت الميكروفون" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"تسجيل وتخزين صوت الميكروفون أثناء القيادة. سيُدرج الصوت في فيديو الكاميرا " +"الأمامية في comma connect." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "لوائح" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "مسترخٍ" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "وصول عن بُعد" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "لقطات عن بُعد" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "انتهت مهلة الطلب" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Reset" +msgstr "إعادة ضبط" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "إعادة ضبط المعايرة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "مراجعة دليل التدريب" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "مراجعة قواعد وميزات وحدود openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "اختيار" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "مفاتيح SSH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "جارٍ مسح شبكات Wi‑Fi..." + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#, python-format +msgid "Select" +msgstr "اختيار" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "اختر فرعاً" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "اختر لغة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "الرقم التسلسلي" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "تأجيل التحديث" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "البرمجيات" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "قياسي" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"يوصى بالوضع القياسي. في الوضع العدواني، سيتبع openpilot السيارات الأمامية عن " +"قرب وسيكون أكثر شدة في الوقود والفرامل. في الوضع المسترخي سيبقى بعيداً أكثر " +"عن السيارات الأمامية. في السيارات المدعومة، يمكنك التنقل بين هذه الشخصيات " +"بزر مسافة المقود." + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "النظام لا يستجيب" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "تولَّ السيطرة فوراً" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "الحرارة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "الفرع المستهدف" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "كلمة مرور الربط" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "مفاتيح التبديل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#, python-format +msgid "UNINSTALL" +msgstr "إلغاء التثبيت" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "تحديث" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Uninstall" +msgstr "إلغاء التثبيت" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "غير معروف" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "يتم تنزيل التحديثات فقط عندما تكون السيارة متوقفة." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "الترقية الآن" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"ارفع بيانات من كاميرا مواجهة السائق وساعد في تحسين خوارزمية مراقبة السائق." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "استخدام النظام المتري" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"استخدم نظام openpilot للتحكم الذكي بالسرعة والمساعدة على البقاء داخل المسار. " +"يتطلب استخدام هذه الميزة انتباهك الكامل في جميع الأوقات." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "المركبة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "عرض" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "بانتظار البدء" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"تحذير: يمنح هذا وصول SSH إلى جميع المفاتيح العامة في إعدادات GitHub الخاصة " +"بك. لا تُدخل مطلقاً اسم مستخدم GitHub غير اسمك. لن يطلب منك موظف في comma أبداً " +"إضافة اسم مستخدمهم." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "مرحباً بك في openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "عند التمكين، سيؤدي الضغط على دواسة الوقود إلى فصل openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "شبكة Wi‑Fi محدودة التعرفة" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "كلمة مرور خاطئة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "يجب عليك قبول الشروط والأحكام لاستخدام openpilot." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"يجب عليك قبول الشروط والأحكام لاستخدام openpilot. اقرأ أحدث الشروط على " +"https://comma.ai/terms قبل المتابعة." + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "بدء تشغيل الكاميرا" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "افتراضي" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "down" +msgstr "أسفل" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#, python-format +msgid "failed to check for update" +msgstr "فشل التحقق من وجود تحديث" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "لـ \"{}\"" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "كم/س" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "اتركه فارغاً للإعداد التلقائي" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "left" +msgstr "يسار" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "محدود التعرفة" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "ميل/س" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#, python-format +msgid "never" +msgstr "أبداً" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#, python-format +msgid "now" +msgstr "الآن" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "التحكم الطولي لـ openpilot (ألفا)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot غير متاح" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"يعمل openpilot افتراضياً في وضع الهدوء. يفعّل وضع التجربة ميزات بمستوى ألفا " +"غير الجاهزة لوضع الهدوء. الميزات التجريبية مدرجة أدناه:

التحكم الطولي " +"من طرف لطرف


دع نموذج القيادة يتحكم في الوقود والفرامل. سيقود " +"openpilot كما يظن أن الإنسان سيقود، بما في ذلك التوقف عند الإشارات الحمراء " +"وعلامات التوقف. بما أن نموذج القيادة يقرر السرعة، فإن السرعة المضبوطة تعمل " +"كحد أعلى فقط. هذه ميزة بجودة ألفا؛ يُتوقع حدوث أخطاء.

تصوير قيادة " +"جديد


سينتقل عرض القيادة إلى الكاميرا الواسعة المواجهة للطريق عند " +"السرعات المنخفضة لإظهار بعض المنعطفات بشكل أفضل. كما سيظهر شعار وضع التجربة " +"في الزاوية العلوية اليمنى." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +"يقوم openpilot بالمعايرة بشكل مستمر، ونادراً ما تتطلب إعادة الضبط. ستؤدي " +"إعادة ضبط المعايرة إلى إعادة تشغيل openpilot إذا كانت السيارة قيد التشغيل." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"يتعلم openpilot القيادة بمشاهدة البشر، مثلك، يقودون.\n" +"\n" +"يتيح وضع Firehose زيادة تحميل بيانات التدريب لتحسين نماذج قيادة openpilot. " +"المزيد من البيانات يعني نماذج أكبر، مما يعني وضع تجربة أفضل." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "قد يأتي التحكم الطولي لـ openpilot في تحديث مستقبلي." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" +"يتطلب openpilot تركيب الجهاز ضمن 4° يساراً أو يميناً وضمن 5° للأعلى أو 9° " +"للأسفل." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "right" +msgstr "يمين" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "غير محدود" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "up" +msgstr "أعلى" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#, python-format +msgid "up to date, last checked never" +msgstr "محدّث، آخر تحقق: أبداً" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#, python-format +msgid "up to date, last checked {}" +msgstr "محدّث، آخر تحقق {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "update available" +msgstr "تحديث متاح" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} تنبيه" +msgstr[1] "{} تنبيه" +msgstr[2] "{} تنبيهان" +msgstr[3] "{} تنبيهات" +msgstr[4] "{} تنبيهات" +msgstr[5] "{} تنبيه" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "قبل {} يوم" +msgstr[1] "قبل {} يوم" +msgstr[2] "قبل {} يومين" +msgstr[3] "قبل {} أيام" +msgstr[4] "قبل {} أيام" +msgstr[5] "قبل {} يوم" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "قبل {} ساعة" +msgstr[1] "قبل {} ساعة" +msgstr[2] "قبل {} ساعتين" +msgstr[3] "قبل {} ساعات" +msgstr[4] "قبل {} ساعات" +msgstr[5] "قبل {} ساعة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "قبل {} دقيقة" +msgstr[1] "قبل {} دقيقة" +msgstr[2] "قبل {} دقيقتين" +msgstr[3] "قبل {} دقائق" +msgstr[4] "قبل {} دقائق" +msgstr[5] "قبل {} دقيقة" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "{} مقطع من قيادتك ضمن مجموعة بيانات التدريب حتى الآن." +msgstr[1] "{} مقطع من قيادتك ضمن مجموعة بيانات التدريب حتى الآن." +msgstr[2] "{} مقطعان من قيادتك ضمن مجموعة بيانات التدريب حتى الآن." +msgstr[3] "{} مقاطع من قيادتك ضمن مجموعة بيانات التدريب حتى الآن." +msgstr[4] "{} مقاطع من قيادتك ضمن مجموعة بيانات التدريب حتى الآن." +msgstr[5] "{} مقطع من قيادتك ضمن مجموعة بيانات التدريب حتى الآن." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ مشترك" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 وضع Firehose 🔥" diff --git a/selfdrive/ui/translations/app_de.po b/selfdrive/ui/translations/app_de.po index 34b3d8321d..cac0faf887 100644 --- a/selfdrive/ui/translations/app_de.po +++ b/selfdrive/ui/translations/app_de.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" "PO-Revision-Date: 2025-10-20 16:35-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,17 +17,17 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " Die Lenkmoment-Reaktionskalibrierung ist abgeschlossen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " Die Lenkmoment-Reaktionskalibrierung ist zu {}% abgeschlossen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Ihr Gerät ist um {:.1f}° {} und {:.1f}° {} ausgerichtet." @@ -76,12 +76,12 @@ msgstr "" "Experimentalmodus wird empfohlen, wenn Sie die openpilot-Längsregelung " "(Alpha) aktivieren." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

Kalibrierung der Lenkverzögerung abgeschlossen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

Kalibrierung der Lenkverzögerung zu {}% abgeschlossen." @@ -144,22 +144,22 @@ msgstr "" "Eine Alpha-Version der openpilot-Längsregelung kann zusammen mit dem " "Experimentalmodus auf Nicht-Release-Zweigen getestet werden." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Sind Sie sicher, dass Sie ausschalten möchten?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Sind Sie sicher, dass Sie neu starten möchten?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Sind Sie sicher, dass Sie die Kalibrierung zurücksetzen möchten?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Sind Sie sicher, dass Sie deinstallieren möchten?" @@ -187,10 +187,10 @@ msgstr "" msgid "CHANGE" msgstr "ÄNDERN" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "PRÜFEN" @@ -215,7 +215,7 @@ msgid "CONNECTING..." msgstr "VERBINDUNG" #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 #: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 #: /home/batman/openpilot/system/ui/widgets/network.py:318 #, python-format @@ -249,12 +249,12 @@ msgstr "Klicken Sie auf \"add new device\" und scannen Sie den QR‑Code rechts" msgid "Close" msgstr "Schließen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Aktuelle Version" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "HERUNTERLADEN" @@ -282,17 +282,17 @@ msgstr "Gerät" msgid "Disengage on Accelerator Pedal" msgstr "Beim Gaspedal deaktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Zum Ausschalten deaktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Zum Neustart deaktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Zum Zurücksetzen der Kalibrierung deaktivieren" @@ -306,7 +306,7 @@ msgstr "Geschwindigkeit in km/h statt mph anzeigen." msgid "Dongle ID" msgstr "Dongle-ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Herunterladen" @@ -415,8 +415,8 @@ msgstr "Passwort eingeben" msgid "Enter your GitHub username" msgstr "Geben Sie Ihren GitHub‑Benutzernamen ein" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "Fehler" @@ -533,8 +533,8 @@ msgstr "Netzwerk" msgid "INACTIVE: connect to an unmetered network" msgstr "INAKTIV: Mit einem unlimitierten Netzwerk verbinden" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALLIEREN" @@ -544,7 +544,7 @@ msgstr "INSTALLIEREN" msgid "IP Address" msgstr "IP‑Adresse" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Update installieren" @@ -614,7 +614,7 @@ msgstr "Keine Versionshinweise verfügbar." msgid "OFFLINE" msgstr "OFFLINE" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 #: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 #, python-format @@ -682,7 +682,7 @@ msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Bitte mit WLAN verbinden, um das erste Koppeln abzuschließen" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Ausschalten" @@ -725,7 +725,7 @@ msgid "REVIEW" msgstr "ANSEHEN" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Neustart" @@ -793,7 +793,7 @@ msgstr "Remote‑Schnappschüsse" msgid "Request timed out" msgstr "Zeitüberschreitung bei der Anfrage" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Zurücksetzen" @@ -813,6 +813,11 @@ msgid "Review the rules, features, and limitations of openpilot" msgstr "" "Überprüfen Sie die Regeln, Funktionen und Einschränkungen von openpilot" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" @@ -823,11 +828,16 @@ msgstr "SSH‑Schlüssel" msgid "Scanning Wi-Fi networks..." msgstr "WLAN‑Netzwerke werden gesucht..." -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "Auswählen" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" @@ -883,6 +893,11 @@ msgstr "SOFORT DIE KONTROLLE ÜBERNEHMEN" msgid "TEMP" msgstr "TEMP" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "" + #: /home/batman/openpilot/system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" @@ -892,7 +907,7 @@ msgstr "Tethering‑Passwort" msgid "Toggles" msgstr "Schalter" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "DEINSTALLIEREN" @@ -902,8 +917,8 @@ msgstr "DEINSTALLIEREN" msgid "UPDATE" msgstr "UPDATE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Deinstallieren" @@ -912,7 +927,7 @@ msgstr "Deinstallieren" msgid "Unknown" msgstr "Unbekannt" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Updates werden nur heruntergeladen, wenn das Auto aus ist." @@ -1024,12 +1039,12 @@ msgstr "comma prime" msgid "default" msgstr "Standard" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "unten" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "Überprüfung auf Updates fehlgeschlagen" @@ -1050,7 +1065,7 @@ msgstr "km/h" msgid "leave blank for automatic configuration" msgstr "für automatische Konfiguration leer lassen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "links" @@ -1065,12 +1080,12 @@ msgstr "getaktet" msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "nie" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "jetzt" @@ -1113,7 +1128,7 @@ msgstr "" "manche Kurven besser zu zeigen. Das Experimentalmodus‑Logo wird außerdem " "oben rechts angezeigt." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1149,7 +1164,7 @@ msgstr "" "openpilot erfordert, dass das Gerät innerhalb von 4° nach links oder rechts " "und innerhalb von 5° nach oben oder 9° nach unten montiert ist." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "rechts" @@ -1159,22 +1174,22 @@ msgstr "rechts" msgid "unmetered" msgstr "unbegrenzt" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "oben" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "Aktuell, zuletzt geprüft: nie" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "Aktuell, zuletzt geprüft: {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "Update verfügbar" @@ -1186,21 +1201,21 @@ msgid_plural "{} ALERTS" msgstr[0] "{} WARNUNG" msgstr[1] "{} WARNUNGEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "vor {} Tag" msgstr[1] "vor {} Tagen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "vor {} Stunde" msgstr[1] "vor {} Stunden" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" diff --git a/selfdrive/ui/translations/app_en.po b/selfdrive/ui/translations/app_en.po index 55554e9d9c..66fecf358d 100644 --- a/selfdrive/ui/translations/app_en.po +++ b/selfdrive/ui/translations/app_en.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" "PO-Revision-Date: 2025-10-21 18:18-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,17 +17,17 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " Steering torque response calibration is complete." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " Steering torque response calibration is {}% complete." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Your device is pointed {:.1f}° {} and {:.1f}° {}." @@ -76,12 +76,12 @@ msgstr "" "control alpha. Changing this setting will restart openpilot if the car is " "powered on." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

Steering lag calibration is complete." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

Steering lag calibration is {}% complete." @@ -143,22 +143,22 @@ msgstr "" "An alpha version of openpilot longitudinal control can be tested, along with " "Experimental mode, on non-release branches." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Are you sure you want to power off?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Are you sure you want to reboot?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Are you sure you want to reset calibration?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Are you sure you want to uninstall?" @@ -184,10 +184,10 @@ msgstr "Bookmark connect.comma.ai to your home screen to use it like an app" msgid "CHANGE" msgstr "CHANGE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "CHECK" @@ -212,7 +212,7 @@ msgid "CONNECTING..." msgstr "CONNECTING..." #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 #: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 #: /home/batman/openpilot/system/ui/widgets/network.py:318 #, python-format @@ -244,12 +244,12 @@ msgstr "Click \"add new device\" and scan the QR code on the right" msgid "Close" msgstr "Close" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Current Version" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "DOWNLOAD" @@ -277,17 +277,17 @@ msgstr "Device" msgid "Disengage on Accelerator Pedal" msgstr "Disengage on Accelerator Pedal" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Disengage to Power Off" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Disengage to Reboot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Disengage to Reset Calibration" @@ -301,7 +301,7 @@ msgstr "Display speed in km/h instead of mph." msgid "Dongle ID" msgstr "Dongle ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Download" @@ -410,8 +410,8 @@ msgstr "Enter password" msgid "Enter your GitHub username" msgstr "Enter your GitHub username" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "Error" @@ -527,8 +527,8 @@ msgstr "Hidden Network" msgid "INACTIVE: connect to an unmetered network" msgstr "INACTIVE: connect to an unmetered network" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALL" @@ -538,7 +538,7 @@ msgstr "INSTALL" msgid "IP Address" msgstr "IP Address" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Install Update" @@ -607,7 +607,7 @@ msgstr "No release notes available." msgid "OFFLINE" msgstr "OFFLINE" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 #: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 #, python-format @@ -675,7 +675,7 @@ msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Please connect to Wi-Fi to complete initial pairing" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Power Off" @@ -718,7 +718,7 @@ msgid "REVIEW" msgstr "REVIEW" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Reboot" @@ -786,7 +786,7 @@ msgstr "Remote snapshots" msgid "Request timed out" msgstr "Request timed out" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Reset" @@ -805,6 +805,11 @@ msgstr "Review Training Guide" msgid "Review the rules, features, and limitations of openpilot" msgstr "Review the rules, features, and limitations of openpilot" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" @@ -815,11 +820,16 @@ msgstr "SSH Keys" msgid "Scanning Wi-Fi networks..." msgstr "Scanning Wi-Fi networks..." -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "Select" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" @@ -874,6 +884,11 @@ msgstr "TAKE CONTROL IMMEDIATELY" msgid "TEMP" msgstr "TEMP" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "" + #: /home/batman/openpilot/system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" @@ -883,7 +898,7 @@ msgstr "Tethering Password" msgid "Toggles" msgstr "Toggles" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "UNINSTALL" @@ -893,8 +908,8 @@ msgstr "UNINSTALL" msgid "UPDATE" msgstr "UPDATE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Uninstall" @@ -903,7 +918,7 @@ msgstr "Uninstall" msgid "Unknown" msgstr "Unknown" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Updates are only downloaded while the car is off." @@ -1011,12 +1026,12 @@ msgstr "comma prime" msgid "default" msgstr "default" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "down" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "failed to check for update" @@ -1037,7 +1052,7 @@ msgstr "km/h" msgid "leave blank for automatic configuration" msgstr "leave blank for automatic configuration" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "left" @@ -1052,12 +1067,12 @@ msgstr "metered" msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "never" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "now" @@ -1099,7 +1114,7 @@ msgstr "" "some turns. The Experimental mode logo will also be shown in the top right " "corner." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1135,7 +1150,7 @@ msgstr "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "right" @@ -1145,22 +1160,22 @@ msgstr "right" msgid "unmetered" msgstr "unmetered" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "up" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "up to date, last checked never" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "up to date, last checked {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "update available" @@ -1172,21 +1187,21 @@ msgid_plural "{} ALERTS" msgstr[0] "{} ALERT" msgstr[1] "{} ALERTS" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{} day ago" msgstr[1] "{} days ago" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{} hour ago" msgstr[1] "{} hours ago" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" diff --git a/selfdrive/ui/translations/app_es.po b/selfdrive/ui/translations/app_es.po index a7424c0915..04dffbf3e6 100644 --- a/selfdrive/ui/translations/app_es.po +++ b/selfdrive/ui/translations/app_es.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" "PO-Revision-Date: 2025-10-20 16:35-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,17 +17,17 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " La calibración de respuesta de par de dirección está completa." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " La calibración de respuesta de par de dirección está {}% completa." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Tu dispositivo está orientado {:.1f}° {} y {:.1f}° {}." @@ -75,12 +75,12 @@ msgstr "" "cambiar al control longitudinal de openpilot. Se recomienda activar el modo " "Experimental al habilitar el control longitudinal de openpilot (alpha)." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" @@ -143,22 +143,22 @@ msgstr "" "Se puede probar una versión alpha del control longitudinal de openpilot, " "junto con el modo Experimental, en ramas que no son de lanzamiento." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "¿Seguro que quieres apagar?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "¿Seguro que quieres reiniciar?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "¿Seguro que quieres restablecer la calibración?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "¿Seguro que quieres desinstalar?" @@ -185,10 +185,10 @@ msgstr "" msgid "CHANGE" msgstr "CAMBIAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "COMPROBAR" @@ -213,7 +213,7 @@ msgid "CONNECTING..." msgstr "CONECTAR" #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 #: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 #: /home/batman/openpilot/system/ui/widgets/network.py:318 #, python-format @@ -247,12 +247,12 @@ msgstr "" msgid "Close" msgstr "Cerrar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Versión actual" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "DESCARGAR" @@ -280,17 +280,17 @@ msgstr "Dispositivo" msgid "Disengage on Accelerator Pedal" msgstr "Desactivar con el pedal del acelerador" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Desactivar para apagar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Desactivar para reiniciar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Desactivar para restablecer la calibración" @@ -304,7 +304,7 @@ msgstr "Mostrar la velocidad en km/h en lugar de mph." msgid "Dongle ID" msgstr "ID del dongle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Descargar" @@ -415,8 +415,8 @@ msgstr "" msgid "Enter your GitHub username" msgstr "Introduce tu nombre de usuario de GitHub" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" @@ -531,8 +531,8 @@ msgstr "Red" msgid "INACTIVE: connect to an unmetered network" msgstr "INACTIVO: conéctate a una red sin límites" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALAR" @@ -542,7 +542,7 @@ msgstr "INSTALAR" msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Instalar actualización" @@ -612,7 +612,7 @@ msgstr "No hay notas de versión disponibles." msgid "OFFLINE" msgstr "SIN CONEXIÓN" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 #: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 #, python-format @@ -680,7 +680,7 @@ msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Conéctate a Wi‑Fi para completar el emparejamiento inicial" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Apagar" @@ -724,7 +724,7 @@ msgid "REVIEW" msgstr "REVISAR" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Reiniciar" @@ -792,7 +792,7 @@ msgstr "Capturas remotas" msgid "Request timed out" msgstr "Se agotó el tiempo de espera de la solicitud" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Restablecer" @@ -811,6 +811,11 @@ msgstr "Revisar guía de entrenamiento" msgid "Review the rules, features, and limitations of openpilot" msgstr "Revisa las reglas, funciones y limitaciones de openpilot" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" @@ -821,11 +826,16 @@ msgstr "" msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" @@ -881,6 +891,11 @@ msgstr "TOME EL CONTROL INMEDIATAMENTE" msgid "TEMP" msgstr "TEMP" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "" + #: /home/batman/openpilot/system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" @@ -890,7 +905,7 @@ msgstr "" msgid "Toggles" msgstr "Interruptores" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "DESINSTALAR" @@ -900,8 +915,8 @@ msgstr "DESINSTALAR" msgid "UPDATE" msgstr "ACTUALIZAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Desinstalar" @@ -910,7 +925,7 @@ msgstr "Desinstalar" msgid "Unknown" msgstr "Desconocido" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Las actualizaciones solo se descargan cuando el coche está apagado." @@ -1022,12 +1037,12 @@ msgstr "comma prime" msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "abajo" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "Error al buscar actualizaciones" @@ -1048,7 +1063,7 @@ msgstr "km/h" msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "izquierda" @@ -1063,12 +1078,12 @@ msgstr "" msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "nunca" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "ahora" @@ -1112,7 +1127,7 @@ msgstr "" "mostrar mejor algunos giros. El logotipo del modo Experimental también se " "mostrará en la esquina superior derecha." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1149,7 +1164,7 @@ msgstr "" "openpilot requiere que el dispositivo esté montado dentro de 4° a izquierda " "o derecha y dentro de 5° hacia arriba o 9° hacia abajo." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "derecha" @@ -1159,22 +1174,22 @@ msgstr "derecha" msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "arriba" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "actualizado, última comprobación: nunca" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "actualizado, última comprobación: {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "actualización disponible" @@ -1186,21 +1201,21 @@ msgid_plural "{} ALERTS" msgstr[0] "{} ALERTA" msgstr[1] "{} ALERTAS" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "hace {} día" msgstr[1] "hace {} días" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "hace {} hora" msgstr[1] "hace {} horas" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" diff --git a/selfdrive/ui/translations/app_fr.po b/selfdrive/ui/translations/app_fr.po index 12bdcb3a8c..4b0370403e 100644 --- a/selfdrive/ui/translations/app_fr.po +++ b/selfdrive/ui/translations/app_fr.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" "PO-Revision-Date: 2025-10-20 18:19-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,17 +17,17 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr "" @@ -76,12 +76,12 @@ msgstr "" "est recommandé d'activer le mode expérimental lors de l'activation du " "contrôle longitudinal openpilot alpha." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" @@ -144,22 +144,22 @@ msgstr "" "Une version alpha du contrôle longitudinal openpilot peut être testée, avec " "le mode expérimental, sur des branches non publiées." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Êtes-vous sûr de vouloir éteindre ?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Êtes-vous sûr de vouloir redémarrer ?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Êtes-vous sûr de vouloir réinitialiser la calibration ?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Êtes-vous sûr de vouloir désinstaller ?" @@ -187,10 +187,10 @@ msgstr "" msgid "CHANGE" msgstr "CHANGER" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "VÉRIFIER" @@ -215,7 +215,7 @@ msgid "CONNECTING..." msgstr "CONNECTER" #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 #: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 #: /home/batman/openpilot/system/ui/widgets/network.py:318 #, python-format @@ -249,12 +249,12 @@ msgstr "Cliquez sur \"add new device\" et scannez le code QR à droite" msgid "Close" msgstr "Fermer" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Version actuelle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "TÉLÉCHARGER" @@ -282,17 +282,17 @@ msgstr "Appareil" msgid "Disengage on Accelerator Pedal" msgstr "Désengager à l'appui sur l'accélérateur" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Désengager pour éteindre" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Désengager pour redémarrer" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Désengager pour réinitialiser la calibration" @@ -306,7 +306,7 @@ msgstr "Afficher la vitesse en km/h au lieu de mph." msgid "Dongle ID" msgstr "ID du dongle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Télécharger" @@ -417,8 +417,8 @@ msgstr "" msgid "Enter your GitHub username" msgstr "Entrez votre nom d'utilisateur GitHub" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" @@ -534,8 +534,8 @@ msgstr "Réseau" msgid "INACTIVE: connect to an unmetered network" msgstr "INACTIF : connectez-vous à un réseau non limité" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALLER" @@ -545,7 +545,7 @@ msgstr "INSTALLER" msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Installer la mise à jour" @@ -615,7 +615,7 @@ msgstr "Aucune note de version disponible." msgid "OFFLINE" msgstr "HORS LIGNE" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 #: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 #, python-format @@ -683,7 +683,7 @@ msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Veuillez vous connecter au Wi‑Fi pour terminer l'association initiale" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Éteindre" @@ -727,7 +727,7 @@ msgid "REVIEW" msgstr "CONSULTER" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Redémarrer" @@ -795,7 +795,7 @@ msgstr "Captures à distance" msgid "Request timed out" msgstr "Délai de la requête dépassé" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Réinitialiser" @@ -814,6 +814,11 @@ msgstr "Consulter le guide d'entraînement" msgid "Review the rules, features, and limitations of openpilot" msgstr "Consultez les règles, fonctionnalités et limitations d'openpilot" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" @@ -824,11 +829,16 @@ msgstr "" msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" @@ -884,6 +894,11 @@ msgstr "REPRENEZ IMMÉDIATEMENT LE CONTRÔLE" msgid "TEMP" msgstr "TEMPÉRATURE" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "" + #: /home/batman/openpilot/system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" @@ -893,7 +908,7 @@ msgstr "" msgid "Toggles" msgstr "Options" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "DÉSINSTALLER" @@ -903,8 +918,8 @@ msgstr "DÉSINSTALLER" msgid "UPDATE" msgstr "METTRE À JOUR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Désinstaller" @@ -913,7 +928,7 @@ msgstr "Désinstaller" msgid "Unknown" msgstr "Inconnu" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "" @@ -1025,12 +1040,12 @@ msgstr "comma prime" msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "échec de la vérification de mise à jour" @@ -1051,7 +1066,7 @@ msgstr "km/h" msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "" @@ -1066,12 +1081,12 @@ msgstr "" msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "jamais" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "maintenant" @@ -1114,7 +1129,7 @@ msgstr "" "pour mieux montrer certains virages. Le logo du mode expérimental sera " "également affiché en haut à droite." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1154,7 +1169,7 @@ msgstr "" "openpilot exige que l'appareil soit monté à moins de 4° à gauche ou à droite " "et à moins de 5° vers le haut ou 9° vers le bas." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "" @@ -1164,22 +1179,22 @@ msgstr "" msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "à jour, dernière vérification jamais" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "à jour, dernière vérification {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "mise à jour disponible" @@ -1191,21 +1206,21 @@ msgid_plural "{} ALERTS" msgstr[0] "{} ALERTE" msgstr[1] "{} ALERTES" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "il y a {} jour" msgstr[1] "il y a {} jours" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "il y a {} heure" msgstr[1] "il y a {} heures" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" diff --git a/selfdrive/ui/translations/app_ja.po b/selfdrive/ui/translations/app_ja.po new file mode 100644 index 0000000000..0ae8869634 --- /dev/null +++ b/selfdrive/ui/translations/app_ja.po @@ -0,0 +1,1216 @@ +# Japanese translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"PO-Revision-Date: 2025-10-22 16:32-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " ステアリングトルク応答のキャリブレーションが完了しました。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " ステアリングトルク応答のキャリブレーションは{}%完了しました。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " デバイスは{:.1f}°{}、{:.1f}°{}の向きです。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "走行データを1年間保存" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "24時間365日のLTE接続" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"警告: この車におけるopenpilotの縦制御はアルファ版であり、自動緊急ブレーキ" +"(AEB)を無効にします。

この車では、openpilotは縦制御として" +"openpilotではなく車両の内蔵ACCを既定で使用します。openpilotの縦制御に切り替え" +"るにはこの設定を有効にしてください。openpilot縦制御アルファを有効にする場合は" +"実験モードの有効化を推奨します。この設定を変更すると、車が起動中の場合は" +"openpilotが再起動します。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "

ステアリング遅延のキャリブレーションが完了しました。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "

ステアリング遅延のキャリブレーションは{}%完了しました。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "アクティブ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB(Android Debug Bridge)を使用すると、USBまたはネットワーク経由でデバイス" +"に接続できます。詳しくは https://docs.comma.ai/how-to/connect-to-comma を参照" +"してください。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "追加" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "APN設定" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "過度な作動を承認" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "詳細設定" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "アグレッシブ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "同意する" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "常時ドライバーモニタリング" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"openpilotの縦制御アルファ版は、実験モードと併せて非リリースブランチでテストで" +"きます。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "本当に電源をオフにしますか?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "本当に再起動しますか?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "本当にキャリブレーションをリセットしますか?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "本当にアンインストールしますか?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "戻る" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "connect.comma.aiで comma prime に加入" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "connect.comma.aiをホーム画面に追加してアプリのように使いましょう" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "変更" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#, python-format +msgid "CHECK" +msgstr "確認" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "チルモードON" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "接続" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "接続中..." + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "キャンセル" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "従量課金の携帯回線" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "言語を変更" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "車が起動中の場合、この設定を変更するとopenpilotが再起動します。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "\"add new device\"を押して右側のQRコードをスキャン" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "閉じる" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Current Version" +msgstr "現在のバージョン" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#, python-format +msgid "DOWNLOAD" +msgstr "ダウンロード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "拒否する" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "拒否してopenpilotをアンインストール" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "開発者" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "デバイス" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "アクセルで解除" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#, python-format +msgid "Disengage to Power Off" +msgstr "解除して電源オフ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#, python-format +msgid "Disengage to Reboot" +msgstr "解除して再起動" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "解除してキャリブレーションをリセット" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "速度をmphではなくkm/hで表示します。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "ドングルID" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#, python-format +msgid "Download" +msgstr "ダウンロード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "ドライバーカメラ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "走行性格" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "編集" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "エラー" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "実験モードON" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "有効化" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "ADBを有効化" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "車線逸脱警報を有効化" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "ローミングを有効化" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "SSHを有効化" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "テザリングを有効化" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "openpilotが未作動でもドライバーモニタリングを有効にします。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "openpilotを有効化" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" +"openpilot縦制御(アルファ)のトグルを有効にすると実験モードが使用できます。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "APNを入力" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "SSIDを入力" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "新しいテザリングのパスワードを入力" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "パスワードを入力" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "GitHubユーザー名を入力" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#, python-format +msgid "Error" +msgstr "エラー" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "実験モード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"この車では縦制御に純正ACCを使用するため、現在実験モードは利用できません。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "削除中..." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "セットアップを完了" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Firehoseモード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"最大限の効果を得るため、デバイスを屋内に持ち込み、週に一度は品質の良いUSB-Cア" +"ダプターとWi‑Fiに接続してください。\n" +"\n" +"Firehoseモードは、ホットスポットや無制限SIMに接続していれば走行中でも動作しま" +"す。\n" +"\n" +"\n" +"よくある質問\n" +"\n" +"運転の仕方や場所は関係ありますか? いいえ。普段どおりに運転してください。\n" +"\n" +"Firehoseモードではすべてのセグメントが取得されますか? いいえ。セグメントの一" +"部を選択的に取得します。\n" +"\n" +"良いUSB‑Cアダプターとは? 高速なスマホまたはノートPC用充電器で問題ありませ" +"ん。\n" +"\n" +"どのソフトウェアを使うかは重要ですか? はい。学習に使えるのは上流のopenpilot" +"(および特定のフォーク)のみです。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "削除" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "Wi‑Fiネットワーク「{}」を削除しますか?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "良好" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "スマートフォンで https://connect.comma.ai にアクセス" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "高温" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "非公開ネットワーク" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "非アクティブ:非従量のネットワークに接続してください" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#, python-format +msgid "INSTALL" +msgstr "インストール" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "IPアドレス" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#, python-format +msgid "Install Update" +msgstr "アップデートをインストール" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "ジョイスティックデバッグモード" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "読み込み中" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "縦制御マヌーバーモード" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "最大" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" +"学習データのアップロードを最大化してopenpilotの運転モデルを改善しましょう。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "該当なし" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "いいえ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "ネットワーク" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "SSH鍵が見つかりません" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "ユーザー'{}'のSSH鍵が見つかりません" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "リリースノートはありません。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "オフライン" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "OK" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "オンライン" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "開く" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "ペアリング" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "プレビュー" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "prime の特典:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "デバイスをペアリング" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "デバイスをペアリング" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "デバイスをあなたの comma アカウントにペアリング" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"デバイスを comma connect(connect.comma.ai)とペアリングして、comma prime 特" +"典を受け取りましょう。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "初回ペアリングを完了するにはWi‑Fiに接続してください" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Power Off" +msgstr "電源オフ" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "従量課金のWi‑Fi接続時は大きなデータのアップロードを抑制" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "従量課金の携帯回線接続時は大きなデータのアップロードを抑制" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"ドライバー向きカメラのプレビューでモニタリングの視界を確認します。(車両は停" +"止状態である必要があります)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "QRコードエラー" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "削除" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "リセット" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "確認" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Reboot" +msgstr "再起動" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "デバイスを再起動" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "再起動して更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"時速31mph(50km/h)を超えて走行中にウインカーを出さず検出された車線を外れた場" +"合、車線内に戻るよう警告を受け取ります。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "ドライバーカメラを記録してアップロード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "マイク音声を記録してアップロード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"走行中にマイク音声を記録・保存します。音声は comma connect のドライブレコー" +"ダー動画に含まれます。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "規制情報" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "リラックス" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "リモートアクセス" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "リモートスナップショット" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "リクエストがタイムアウトしました" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Reset" +msgstr "リセット" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "キャリブレーションをリセット" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "トレーニングガイドを確認" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "openpilotのルール、機能、制限を確認" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "選択" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "SSH鍵" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "Wi‑Fiネットワークを検索中..." + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#, python-format +msgid "Select" +msgstr "選択" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "ブランチを選択" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "言語を選択" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "シリアル" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "更新を後で通知" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "ソフトウェア" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "スタンダード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"標準を推奨します。アグレッシブでは前走車に近づき、加減速も積極的になります。" +"リラックスでは前走車との距離を保ちます。対応車種ではステアリングの車間ボタン" +"でこれらの性格を切り替えられます。" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "システムが応答しません" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "すぐに手動介入してください" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "温度" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "対象ブランチ" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "テザリングのパスワード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "トグル" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#, python-format +msgid "UNINSTALL" +msgstr "アンインストール" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Uninstall" +msgstr "アンインストール" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "不明" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "アップデートは車両の電源が切れている間のみダウンロードされます。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "今すぐアップグレード" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"ドライバー向きカメラのデータをアップロードしてモニタリングアルゴリズムの改善" +"に協力してください。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "メートル法を使用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"ACCと車線維持支援にopenpilotを使用します。本機能の使用中は常に注意が必要で" +"す。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "車両" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "表示" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "開始待機中" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"警告: これはGitHub設定内のすべての公開鍵にSSHアクセスを与えます。自分以外の" +"GitHubユーザー名を絶対に入力しないでください。comma の従業員が自分のGitHub" +"ユーザー名を追加するよう求めることは決してありません。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "openpilotへようこそ" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "有効にすると、アクセルを踏むとopenpilotが解除されます。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "Wi‑Fiネットワーク(従量課金)" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "パスワードが違います" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "openpilotを使用するには、利用規約に同意する必要があります。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"openpilotを使用するには利用規約に同意する必要があります。続行する前に " +"https://comma.ai/terms の最新の規約をお読みください。" + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "カメラを起動中" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "既定" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "down" +msgstr "下" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#, python-format +msgid "failed to check for update" +msgstr "アップデートの確認に失敗しました" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "「{}」向け" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "km/h" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "自動設定の場合は空欄のままにしてください" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "left" +msgstr "左" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "従量" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "mph" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#, python-format +msgid "never" +msgstr "なし" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#, python-format +msgid "now" +msgstr "今" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "openpilot 縦制御(アルファ)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilotは利用できません" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilotは既定でチルモードで走行します。実験モードでは、チルモードにはまだ準" +"備ができていないアルファレベルの機能が有効になります。実験的な機能は以下のと" +"おりです:

エンドツーエンド縦制御


運転モデルがアクセルとブレー" +"キを制御します。openpilotは人間のように走行し、赤信号や一時停止でも停止しま" +"す。走行速度は運転モデルが決めるため、設定速度は上限としてのみ機能します。こ" +"れはアルファ品質の機能であり、誤動作が発生する可能性があります。

新し" +"い運転ビジュアライゼーション


低速時には道路向きの広角カメラに切り替わ" +"り、一部の曲がりをより良く表示します。画面右上には実験モードのロゴも表示され" +"ます。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +"openpilotは継続的にキャリブレーションを行っており、リセットが必要になることは" +"稀です。車が起動中にキャリブレーションをリセットするとopenpilotが再起動しま" +"す。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilotは、あなたのような人間の運転を見て運転を学習します。\n" +"\n" +"Firehoseモードを使うと、学習データのアップロードを最大化してopenpilotの運転モ" +"デルを改善できます。データが増えるほどモデルが大きくなり、実験モードがより良" +"くなります。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "openpilotの縦制御は将来のアップデートで提供される可能性があります。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" +"openpilotでは、デバイスの取り付け角度が左右±4°、上方向5°以内、下方向9°以内で" +"ある必要があります。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "right" +msgstr "右" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "非従量" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "up" +msgstr "上" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#, python-format +msgid "up to date, last checked never" +msgstr "最新です。最終確認: なし" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#, python-format +msgid "up to date, last checked {}" +msgstr "最新です。最終確認: {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "update available" +msgstr "更新があります" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{}件のアラート" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "{}日前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "{}時間前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "{}分前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "" +"これまでにあなたの走行の{}セグメントが学習データセットに含まれています。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ 登録済み" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Firehoseモード 🔥" diff --git a/selfdrive/ui/translations/app_ko.po b/selfdrive/ui/translations/app_ko.po new file mode 100644 index 0000000000..c07d449013 --- /dev/null +++ b/selfdrive/ui/translations/app_ko.po @@ -0,0 +1,1209 @@ +# Korean translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"PO-Revision-Date: 2025-10-22 16:32-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " 스티어링 토크 응답 보정이 완료되었습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " 스티어링 토크 응답 보정이 {}% 완료되었습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " 장치는 {:.1f}° {} 및 {:.1f}° {} 방향을 가리키고 있습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "주행 데이터 1년 보관" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "연중무휴 LTE 연결" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"경고: 이 차량에서 openpilot의 종방향 제어는 알파 버전이며 자동 긴급 제동" +"(AEB)을 비활성화합니다.

이 차량에서는 openpilot 종방향 제어 대신 " +"차량 내장 ACC가 기본으로 사용됩니다. openpilot 종방향 제어로 전환하려면 이 설" +"정을 켜세요. 종방향 제어 알파를 켤 때는 실험 모드 사용을 권장합니다. 차량 전" +"원이 켜져 있는 경우 이 설정을 변경하면 openpilot이 재시작됩니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "

스티어링 지연 보정이 완료되었습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "

스티어링 지연 보정이 {}% 완료되었습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "활성" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB(Android Debug Bridge)를 사용하면 USB 또는 네트워크로 장치에 연결할 수 있" +"습니다. 자세한 내용은 https://docs.comma.ai/how-to/connect-to-comma 를 참고하" +"세요." + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "추가" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "APN 설정" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "과도한 작동을 확인" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "고급" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "공격적" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "동의" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "항상 켜짐 운전자 모니터링" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" +"openpilot 종방향 제어 알파 버전은 실험 모드와 함께 비릴리스 브랜치에서 테스트" +"할 수 있습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "정말 전원을 끄시겠습니까?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "정말 재시작하시겠습니까?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "정말 보정을 재설정하시겠습니까?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "정말 제거하시겠습니까?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "뒤로" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "connect.comma.ai에서 comma prime 회원이 되세요" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "connect.comma.ai를 홈 화면에 추가하여 앱처럼 사용하세요" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "변경" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#, python-format +msgid "CHECK" +msgstr "확인" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "칠 모드 켜짐" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "연결" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "연결 중..." + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "취소" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "종량제 셀룰러" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "언어 변경" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "차량 전원이 켜져 있으면 이 설정을 변경할 때 openpilot이 재시작됩니다." + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "\"add new device\"를 눌러 오른쪽의 QR 코드를 스캔하세요" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "닫기" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Current Version" +msgstr "현재 버전" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#, python-format +msgid "DOWNLOAD" +msgstr "다운로드" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "거부" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "거부하고 openpilot 제거" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "개발자" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "장치" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "가속 페달로 해제" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#, python-format +msgid "Disengage to Power Off" +msgstr "해제 후 전원 끄기" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#, python-format +msgid "Disengage to Reboot" +msgstr "해제 후 재시작" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "해제 후 보정 재설정" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "속도를 mph 대신 km/h로 표시합니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "동글 ID" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#, python-format +msgid "Download" +msgstr "다운로드" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "운전자 카메라" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "주행 성향" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "편집" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "오류" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "실험 모드 켜짐" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "사용" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "ADB 사용" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "차선 이탈 경고 사용" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "로밍 사용" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "SSH 사용" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "테더링 사용" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "openpilot이 작동 중이 아닐 때도 운전자 모니터링을 사용합니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "openpilot 사용" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "실험 모드를 사용하려면 openpilot 종방향 제어(알파) 토글을 켜세요." + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "APN 입력" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "SSID 입력" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "새 테더링 비밀번호 입력" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "비밀번호 입력" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "GitHub 사용자 이름 입력" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#, python-format +msgid "Error" +msgstr "오류" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "실험 모드" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" +"이 차량은 종방향 제어에 순정 ACC를 사용하므로 현재 실험 모드를 사용할 수 없습" +"니다." + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "삭제 중..." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "설정 완료" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Firehose 모드" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"최대의 효과를 위해 주 1회는 장치를 실내로 가져와 품질 좋은 USB‑C 어댑터와 " +"Wi‑Fi에 연결하세요.\n" +"\n" +"핫스팟이나 무제한 SIM에 연결되어 있다면 주행 중에도 Firehose 모드가 동작합니" +"다.\n" +"\n" +"\n" +"자주 묻는 질문\n" +"\n" +"어떻게, 어디서 운전하는지가 중요한가요? 아니요. 평소처럼 운전하세요.\n" +"\n" +"Firehose 모드에서 모든 세그먼트가 가져가지나요? 아니요. 일부 세그먼트만 선택" +"적으로 가져갑니다.\n" +"\n" +"좋은 USB‑C 어댑터는 무엇인가요? 빠른 휴대폰 또는 노트북 충전기면 충분합니" +"다.\n" +"\n" +"어떤 소프트웨어를 실행하는지가 중요한가요? 예. 학습에는 업스트림 " +"openpilot(및 일부 포크)만 사용할 수 있습니다." + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "삭제" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "Wi‑Fi 네트워크 \"{}\"를 삭제하시겠습니까?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "양호" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "휴대폰에서 https://connect.comma.ai 에 접속하세요" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "높음" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "숨겨진 네트워크" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "비활성: 비종량제 네트워크에 연결하세요" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#, python-format +msgid "INSTALL" +msgstr "설치" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "IP 주소" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#, python-format +msgid "Install Update" +msgstr "업데이트 설치" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "조이스틱 디버그 모드" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "로딩 중" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "종방향 매뉴버 모드" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "최대" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "학습 데이터 업로드를 최대화하여 openpilot의 주행 모델을 개선하세요." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "해당 없음" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "아니오" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "네트워크" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "SSH 키를 찾을 수 없습니다" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "사용자 '{}'의 SSH 키를 찾을 수 없습니다" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "릴리스 노트가 없습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "오프라인" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "확인" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "온라인" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "열기" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "페어링" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "미리보기" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "prime 기능:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "장치 페어링" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "장치 페어링" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "장치를 귀하의 comma 계정에 페어링하세요" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"장치를 comma connect(connect.comma.ai)와 페어링하고 comma prime 혜택을 받으세" +"요." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "초기 페어링을 완료하려면 Wi‑Fi에 연결하세요" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Power Off" +msgstr "전원 끄기" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "종량제 Wi‑Fi 연결 시 대용량 업로드 방지" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "종량제 셀룰러 연결 시 대용량 업로드 방지" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" +"운전자 모니터링의 가시성을 확인하기 위해 운전자 카메라를 미리 봅니다. (차량" +"은 꺼져 있어야 합니다)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "QR 코드 오류" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "제거" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "재설정" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "검토" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Reboot" +msgstr "재시작" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "장치 재시작" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "재시작 및 업데이트" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"시속 31mph(50km/h) 이상에서 방향지시등 없이 감지된 차선 밖으로 벗어나면 차선" +"으로 복귀하라는 경고를 받습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "운전자 카메라 기록 및 업로드" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "마이크 오디오 기록 및 업로드" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"주행 중 마이크 오디오를 기록하고 저장합니다. 오디오는 comma connect의 대시캠 " +"영상에 포함됩니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "규제 정보" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "편안함" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "원격 액세스" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "원격 스냅샷" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "요청 시간이 초과되었습니다" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Reset" +msgstr "재설정" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "보정 재설정" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "학습 가이드 검토" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "openpilot의 규칙, 기능 및 제한을 검토" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "선택" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "SSH 키" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "Wi‑Fi 네트워크 검색 중..." + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#, python-format +msgid "Select" +msgstr "선택" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "브랜치 선택" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "언어 선택" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "시리얼" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "업데이트 나중에 알림" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "소프트웨어" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "표준" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"표준을 권장합니다. 공격적 모드에서는 앞차를 더 가깝게 따라가고 가감속이 더 적" +"극적입니다. 편안함 모드에서는 앞차와 거리를 더 둡니다. 지원 차량에서는 스티어" +"링의 차간 버튼으로 이 성향들을 전환할 수 있습니다." + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "시스템 응답 없음" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "즉시 수동 조작하세요" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "온도" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "대상 브랜치" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "테더링 비밀번호" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "토글" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#, python-format +msgid "UNINSTALL" +msgstr "제거" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "업데이트" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Uninstall" +msgstr "제거" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "알 수 없음" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "업데이트는 차량 전원이 꺼져 있을 때만 다운로드됩니다." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "지금 업그레이드" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" +"운전자 방향 카메라 데이터를 업로드하여 운전자 모니터링 알고리즘 개선에 도움" +"을 주세요." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "미터법 사용" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"ACC 및 차선 유지 보조에 openpilot을 사용합니다. 이 기능을 사용할 때는 항상 주" +"의가 필요합니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "차량" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "보기" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "시작 대기 중" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"경고: 이는 GitHub 설정의 모든 공개 키에 SSH 액세스를 부여합니다. 자신의 것이 " +"아닌 GitHub 사용자 이름을 절대 입력하지 마세요. comma 직원이 본인의 GitHub 사" +"용자 이름 추가를 요구하는 일은 결코 없습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "openpilot에 오신 것을 환영합니다" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "이 옵션을 켜면 가속 페달을 밟을 때 openpilot이 해제됩니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "Wi‑Fi 네트워크 종량제" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "비밀번호가 올바르지 않습니다" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "openpilot을 사용하려면 약관에 동의해야 합니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"openpilot을 사용하려면 약관에 동의해야 합니다. 계속하기 전에 https://comma." +"ai/terms 에서 최신 약관을 읽어주세요." + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "카메라 시작 중" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "기본값" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "down" +msgstr "아래" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#, python-format +msgid "failed to check for update" +msgstr "업데이트 확인 실패" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "\"{}\"용" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "km/h" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "자동 구성을 사용하려면 비워 두세요" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "left" +msgstr "왼쪽" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "종량제" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "mph" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#, python-format +msgid "never" +msgstr "없음" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#, python-format +msgid "now" +msgstr "지금" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "openpilot 종방향 제어(알파)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot 사용 불가" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilot은 기본적으로 칠 모드로 주행합니다. 실험 모드를 사용하면 칠 모드에 " +"아직 준비되지 않은 알파 수준의 기능이 활성화됩니다. 실험 기능은 아래와 같습니" +"다:

엔드투엔드 종방향 제어


주행 모델이 가속과 제동을 제어합니" +"다. openpilot은 빨간 신호 및 정지 표지에서의 정지를 포함해 사람이 운전한다고 " +"판단하는 방식으로 주행합니다. 주행 속도는 모델이 결정하므로 설정 속도는 상한" +"으로만 동작합니다. 알파 품질 기능이므로 오작동이 발생할 수 있습니다.

" +"새로운 주행 시각화


저속에서는 도로 방향의 광각 카메라로 전환되어 일" +"부 회전을 더 잘 보여줍니다. 화면 오른쪽 위에는 실험 모드 로고도 표시됩니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +"openpilot은 지속적으로 보정을 진행하므로 재설정이 필요한 경우는 드뭅니다. 차" +"량 전원이 켜져 있을 때 보정을 재설정하면 openpilot이 재시작됩니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilot은 당신과 같은 사람의 운전을 보며 운전을 학습합니다.\n" +"\n" +"Firehose 모드는 학습 데이터 업로드를 최대화하여 openpilot의 주행 모델을 개선" +"할 수 있게 해줍니다. 데이터가 많을수록 모델은 커지고, 실험 모드는 더 좋아집니" +"다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "openpilot 종방향 제어는 향후 업데이트에서 제공될 수 있습니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "openpilot은 장치를 좌우 4°, 위쪽 5°, 아래쪽 9° 이내로 장착해야 합니다." + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "right" +msgstr "오른쪽" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "비종량제" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "up" +msgstr "위" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#, python-format +msgid "up to date, last checked never" +msgstr "최신입니다. 마지막 확인: 없음" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#, python-format +msgid "up to date, last checked {}" +msgstr "최신입니다. 마지막 확인: {}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "update available" +msgstr "업데이트 가능" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{}건의 알림" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "{}일 전" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "{}시간 전" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "{}분 전" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "현재까지 귀하의 주행 {}세그먼트가 학습 데이터셋에 포함되었습니다." + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ 구독됨" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Firehose 모드 🔥" diff --git a/selfdrive/ui/translations/app_pt-BR.po b/selfdrive/ui/translations/app_pt-BR.po index 1829dccd83..a14304bb8b 100644 --- a/selfdrive/ui/translations/app_pt-BR.po +++ b/selfdrive/ui/translations/app_pt-BR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" "PO-Revision-Date: 2025-10-21 00:00-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,17 +17,17 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " A calibração da resposta de torque da direção foi concluída." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " A calibração da resposta de torque da direção está {}% concluída." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Seu dispositivo está apontado {:.1f}° {} e {:.1f}° {}." @@ -75,12 +75,12 @@ msgstr "" "longitudinal do openpilot. Recomenda-se ativar o Modo Experimental ao ativar " "o controle longitudinal do openpilot em alpha." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" @@ -143,22 +143,22 @@ msgstr "" "Uma versão alpha do controle longitudinal do openpilot pode ser testada, " "junto com o Modo Experimental, em ramificações fora de release." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Tem certeza de que deseja desligar?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Tem certeza de que deseja reiniciar?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Tem certeza de que deseja redefinir a calibração?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Tem certeza de que deseja desinstalar?" @@ -184,10 +184,10 @@ msgstr "Adicione connect.comma.ai à tela inicial para usá-lo como um app" msgid "CHANGE" msgstr "ALTERAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "VERIFICAR" @@ -212,7 +212,7 @@ msgid "CONNECTING..." msgstr "CONECTAR" #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 #: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 #: /home/batman/openpilot/system/ui/widgets/network.py:318 #, python-format @@ -245,12 +245,12 @@ msgstr "Toque em \"adicionar novo dispositivo\" e escaneie o QR code à direita" msgid "Close" msgstr "Fechar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Versão Atual" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "BAIXAR" @@ -278,17 +278,17 @@ msgstr "Dispositivo" msgid "Disengage on Accelerator Pedal" msgstr "Desativar ao pressionar o acelerador" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Desativar para Desligar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Desativar para Reiniciar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Desativar para Redefinir Calibração" @@ -302,7 +302,7 @@ msgstr "Exibir velocidade em km/h em vez de mph." msgid "Dongle ID" msgstr "ID do Dongle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Baixar" @@ -412,8 +412,8 @@ msgstr "" msgid "Enter your GitHub username" msgstr "Digite seu nome de usuário do GitHub" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" @@ -528,8 +528,8 @@ msgstr "Rede" msgid "INACTIVE: connect to an unmetered network" msgstr "INATIVO: conecte a uma rede sem franquia" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALAR" @@ -539,7 +539,7 @@ msgstr "INSTALAR" msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Instalar Atualização" @@ -609,7 +609,7 @@ msgstr "Sem notas de versão disponíveis." msgid "OFFLINE" msgstr "OFFLINE" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 #: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 #, python-format @@ -677,7 +677,7 @@ msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Conecte-se ao Wi‑Fi para concluir o emparelhamento inicial" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Desligar" @@ -721,7 +721,7 @@ msgid "REVIEW" msgstr "REVISAR" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Reiniciar" @@ -788,7 +788,7 @@ msgstr "Capturas remotas" msgid "Request timed out" msgstr "Tempo da solicitação esgotado" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Redefinir" @@ -807,6 +807,11 @@ msgstr "Revisar Guia de Treinamento" msgid "Review the rules, features, and limitations of openpilot" msgstr "Revise as regras, recursos e limitações do openpilot" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" @@ -817,11 +822,16 @@ msgstr "" msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" @@ -877,6 +887,11 @@ msgstr "ASSUMA O CONTROLE IMEDIATAMENTE" msgid "TEMP" msgstr "TEMP" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "" + #: /home/batman/openpilot/system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" @@ -886,7 +901,7 @@ msgstr "" msgid "Toggles" msgstr "Alternâncias" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "DESINSTALAR" @@ -896,8 +911,8 @@ msgstr "DESINSTALAR" msgid "UPDATE" msgstr "ATUALIZAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Desinstalar" @@ -906,7 +921,7 @@ msgstr "Desinstalar" msgid "Unknown" msgstr "Desconhecido" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Atualizações são baixadas apenas com o carro desligado." @@ -1017,12 +1032,12 @@ msgstr "comma prime" msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "para baixo" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "falha ao verificar atualização" @@ -1043,7 +1058,7 @@ msgstr "km/h" msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "à esquerda" @@ -1058,12 +1073,12 @@ msgstr "" msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "nunca" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "agora" @@ -1106,7 +1121,7 @@ msgstr "" "curvas. O logotipo do Modo Experimental também será exibido no canto " "superior direito." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1142,7 +1157,7 @@ msgstr "" "o openpilot requer que o dispositivo seja montado dentro de 4° para a " "esquerda ou direita e dentro de 5° para cima ou 9° para baixo." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "à direita" @@ -1152,22 +1167,22 @@ msgstr "à direita" msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "para cima" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "atualizado, última verificação: nunca" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "atualizado, última verificação: {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "atualização disponível" @@ -1179,21 +1194,21 @@ msgid_plural "{} ALERTS" msgstr[0] "{} ALERTA" msgstr[1] "{} ALERTAS" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{} dia atrás" msgstr[1] "{} dias atrás" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{} hora atrás" msgstr[1] "{} horas atrás" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" diff --git a/selfdrive/ui/translations/app_th.po b/selfdrive/ui/translations/app_th.po new file mode 100644 index 0000000000..87420ed07a --- /dev/null +++ b/selfdrive/ui/translations/app_th.po @@ -0,0 +1,1148 @@ +# Thai translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"PO-Revision-Date: 2025-10-22 16:32-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: th\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#, python-format +msgid "CHECK" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Current Version" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#, python-format +msgid "DOWNLOAD" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#, python-format +msgid "Disengage to Power Off" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#, python-format +msgid "Disengage to Reboot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#, python-format +msgid "Download" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#, python-format +msgid "Error" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#, python-format +msgid "INSTALL" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#, python-format +msgid "Install Update" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Power Off" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Reboot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Reset" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#, python-format +msgid "Select" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#, python-format +msgid "UNINSTALL" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Uninstall" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "down" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#, python-format +msgid "failed to check for update" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "left" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#, python-format +msgid "never" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#, python-format +msgid "now" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "right" +msgstr "" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "up" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#, python-format +msgid "up to date, last checked never" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#, python-format +msgid "up to date, last checked {}" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "update available" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "" +msgstr[1] "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "" diff --git a/selfdrive/ui/translations/app_tr.po b/selfdrive/ui/translations/app_tr.po index 29d3b9b9fe..022621f579 100644 --- a/selfdrive/ui/translations/app_tr.po +++ b/selfdrive/ui/translations/app_tr.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 15:30-0700\n" +"POT-Creation-Date: 2025-10-22 18:57-0700\n" "PO-Revision-Date: 2025-10-20 18:19-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,17 +17,17 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:159 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " Direksiyon tork tepkisi kalibrasyonu tamamlandı." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:157 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " Direksiyon tork tepkisi kalibrasyonu {}% tamamlandı." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Cihazınız {:.1f}° {} ve {:.1f}° {} yönünde konumlandırılmış." @@ -75,12 +75,12 @@ msgstr "" "etkinleştirin. openpilot boylamsal kontrol alfayı etkinleştirirken Deneysel " "modu etkinleştirmeniz önerilir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:147 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:145 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" @@ -143,22 +143,22 @@ msgstr "" "openpilot boylamsal kontrolünün alfa sürümü, Deneysel mod ile birlikte, " "yayın dışı dallarda test edilebilir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Kapatmak istediğinizden emin misiniz?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Yeniden başlatmak istediğinizden emin misiniz?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Kalibrasyonu sıfırlamak istediğinizden emin misiniz?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Kaldırmak istediğinizden emin misiniz?" @@ -185,10 +185,10 @@ msgstr "" msgid "CHANGE" msgstr "DEĞİŞTİR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:142 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "KONTROL ET" @@ -213,7 +213,7 @@ msgid "CONNECTING..." msgstr "BAĞLAN" #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:34 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 #: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 #: /home/batman/openpilot/system/ui/widgets/network.py:318 #, python-format @@ -246,12 +246,12 @@ msgstr "\"yeni cihaz ekle\"ye tıklayın ve sağdaki QR kodunu tarayın" msgid "Close" msgstr "Kapat" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Geçerli Sürüm" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "İNDİR" @@ -279,17 +279,17 @@ msgstr "Cihaz" msgid "Disengage on Accelerator Pedal" msgstr "Gaz Pedalında Devreden Çık" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:183 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Kapatmak için Devreden Çıkın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:171 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Yeniden Başlatmak için Devreden Çıkın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:102 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Kalibrasyonu Sıfırlamak için Devreden Çıkın" @@ -303,7 +303,7 @@ msgstr "Hızı mph yerine km/h olarak göster." msgid "Dongle ID" msgstr "Dongle ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "İndir" @@ -411,8 +411,8 @@ msgstr "" msgid "Enter your GitHub username" msgstr "GitHub kullanıcı adınızı girin" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:115 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:153 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" @@ -528,8 +528,8 @@ msgstr "Ağ" msgid "INACTIVE: connect to an unmetered network" msgstr "PASİF: sınırsız bir ağa bağlanın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:131 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "YÜKLE" @@ -539,7 +539,7 @@ msgstr "YÜKLE" msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:52 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Güncellemeyi Yükle" @@ -609,7 +609,7 @@ msgstr "Sürüm notu mevcut değil." msgid "OFFLINE" msgstr "ÇEVRİMDIŞI" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:247 +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 #: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 #: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 #, python-format @@ -677,7 +677,7 @@ msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "İlk eşleştirmeyi tamamlamak için lütfen Wi‑Fi'a bağlanın" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:186 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Kapat" @@ -720,7 +720,7 @@ msgid "REVIEW" msgstr "GÖZDEN GEÇİR" #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:174 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Yeniden Başlat" @@ -787,7 +787,7 @@ msgstr "Uzaktan anlık görüntüler" msgid "Request timed out" msgstr "İstek zaman aşımına uğradı" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Sıfırla" @@ -807,6 +807,11 @@ msgid "Review the rules, features, and limitations of openpilot" msgstr "" "openpilot'un kurallarını, özelliklerini ve sınırlamalarını gözden geçirin" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" @@ -817,11 +822,16 @@ msgstr "" msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "" + #: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" @@ -876,6 +886,11 @@ msgstr "HEMEN KONTROLÜ DEVRALIN" msgid "TEMP" msgstr "TEMP" +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "" + #: /home/batman/openpilot/system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" @@ -885,7 +900,7 @@ msgstr "" msgid "Toggles" msgstr "Seçenekler" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "KALDIR" @@ -895,8 +910,8 @@ msgstr "KALDIR" msgid "UPDATE" msgstr "GÜNCELLE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:70 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:158 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Kaldır" @@ -905,7 +920,7 @@ msgstr "Kaldır" msgid "Unknown" msgstr "Bilinmiyor" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:47 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Güncellemeler yalnızca araç kapalıyken indirilir." @@ -1015,12 +1030,12 @@ msgstr "comma prime" msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "aşağı" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:105 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "güncelleme kontrolü başarısız" @@ -1041,7 +1056,7 @@ msgstr "km/h" msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "sol" @@ -1056,12 +1071,12 @@ msgstr "" msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:19 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "asla" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:30 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "şimdi" @@ -1103,7 +1118,7 @@ msgstr "" "göstermek için yola bakan geniş açılı kameraya geçer. Deneysel mod logosu " "sağ üst köşede de gösterilecektir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:164 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1138,7 +1153,7 @@ msgstr "" "openpilot, cihazın sağa/sola 4° ve yukarı 5° veya aşağı 9° içinde monte " "edilmesini gerektirir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "sağ" @@ -1148,22 +1163,22 @@ msgstr "sağ" msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:132 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "yukarı" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:116 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "güncel, son kontrol asla" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:114 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "güncel, son kontrol {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:108 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "güncelleme mevcut" @@ -1175,21 +1190,21 @@ msgid_plural "{} ALERTS" msgstr[0] "{} UYARI" msgstr[1] "{} UYARILAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:39 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{} gün önce" msgstr[1] "{} gün önce" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:36 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{} saat önce" msgstr[1] "{} saat önce" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:33 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" diff --git a/selfdrive/ui/translations/app_zh-CHS.po b/selfdrive/ui/translations/app_zh-CHS.po new file mode 100644 index 0000000000..d09add97d8 --- /dev/null +++ b/selfdrive/ui/translations/app_zh-CHS.po @@ -0,0 +1,1193 @@ +# Language zh-CHS translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 19:09-0700\n" +"PO-Revision-Date: 2025-10-22 16:32-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: zh-CHS\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " 转向扭矩响应校准完成。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " 转向扭矩响应校准已完成 {}%。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " 您的设备朝向 {:.1f}° {} 与 {:.1f}° {}。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "1 年行驶数据存储" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "全天候 LTE 连接" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"警告:此车型的 openpilot 纵向控制仍为 alpha,将会停用自动紧急制动 (AEB)。" +"

在此车型上,openpilot 默认使用车载 ACC,而非 openpilot 的纵向控" +"制。启用此选项可切换为 openpilot 纵向控制。建议同时启用实验模式。若车辆通电," +"更改此设置将会重启 openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "

转向延迟校准完成。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "

转向延迟校准已完成 {}%。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "已启用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB(Android 调试桥)可通过 USB 或网络连接到您的设备。详见 https://docs." +"comma.ai/how-to/connect-to-comma。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "添加" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "APN 设置" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "确认过度作动" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "高级" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "激进" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "同意" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "始终启用驾驶员监控" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "openpilot 纵向控制的 alpha 版本可在非发布分支搭配实验模式进行测试。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "确定要关机吗?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "确定要重启吗?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "确定要重置校准吗?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "确定要卸载吗?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "返回" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "前往 connect.comma.ai 成为 comma prime 会员" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "将 connect.comma.ai 添加到主屏幕,像应用一样使用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "更改" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#, python-format +msgid "CHECK" +msgstr "检查" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "安稳模式已开启" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "CONNECT" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "CONNECTING..." + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "取消" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "蜂窝计量" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "更改语言" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "若车辆通电,更改此设置将重启 openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "点击“添加新设备”,扫描右侧二维码" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "关闭" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Current Version" +msgstr "当前版本" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#, python-format +msgid "DOWNLOAD" +msgstr "下载" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "拒绝" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "拒绝并卸载 openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "开发者" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "设备" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "踩下加速踏板时脱离" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#, python-format +msgid "Disengage to Power Off" +msgstr "脱离以关机" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#, python-format +msgid "Disengage to Reboot" +msgstr "脱离以重启" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "脱离以重置校准" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "以 km/h 显示速度(非 mph)。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "Dongle ID" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#, python-format +msgid "Download" +msgstr "下载" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "车内摄像头" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "驾驶风格" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "编辑" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "错误" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "实验模式已开启" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "启用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "启用 ADB" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "启用车道偏离警示" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "启用漫游" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "启用 SSH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "启用网络共享" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "即使未启用 openpilot 也启用驾驶员监控。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "启用 openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "启用 openpilot 纵向控制(alpha)开关,以使用实验模式。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "输入 APN" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "输入 SSID" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "输入新的网络共享密码" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "输入密码" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "输入您的 GitHub 用户名" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#, python-format +msgid "Error" +msgstr "错误" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "实验模式" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "此车型当前无法使用实验模式,因为纵向控制使用的是原厂 ACC。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "正在遗忘..." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "完成设置" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Firehose 模式" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"为达到最佳效果,请将设备带到室内,并每周连接优质 USB‑C 充电器与 Wi‑Fi。\n" +"\n" +"若连接热点或不限流量卡,行车中也可使用 Firehose 模式。\n" +"\n" +"\n" +"常见问题\n" +"\n" +"我怎么开、在哪开有区别吗?没有,平常怎么开就怎么开。\n" +"\n" +"Firehose 模式会拉取我所有片段吗?不会,我们会选择性拉取部分片段。\n" +"\n" +"什么是好的 USB‑C 充电器?任何快速的手机或笔电充电器都可以。\n" +"\n" +"我跑什么软件有区别吗?有,只有上游 openpilot(及特定分支)可用于训练。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "忘记" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "要忘记 Wi‑Fi 网络“{}”吗?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "良好" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "在手机上前往 https://connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "高" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "隐藏网络" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "未启用:请连接不限流量网络" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#, python-format +msgid "INSTALL" +msgstr "安装" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "IP 地址" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#, python-format +msgid "Install Update" +msgstr "安装更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "摇杆调试模式" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "加载中" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "纵向操作模式" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "最大" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "最大化上传训练数据,以改进 openpilot 的驾驶模型。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "无" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "否" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "网络" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "未找到 SSH 密钥" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "未找到用户“{}”的 SSH 密钥" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "暂无发行说明。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "离线" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "确定" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "在线" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "打开" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "配对" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "预览" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "PRIME 功能:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "配对设备" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "配对设备" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "将设备配对到您的 comma 账号" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"将设备与 comma connect(connect.comma.ai)配对,领取您的 comma prime 优惠。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "请连接 Wi‑Fi 以完成初始配对" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Power Off" +msgstr "关机" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "在计量制 Wi‑Fi 连接时避免大量上传" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "在计量制蜂窝网络时避免大量上传" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "预览车内摄像头以确保驾驶员监控视野良好。(车辆必须熄火)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "二维码错误" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "移除" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "重置" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "查看" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Reboot" +msgstr "重启" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "重启设备" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "重启并更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"当车辆以超过 31 mph(50 km/h)行驶且未打转向灯越过检测到的车道线时,接收引导" +"回车道的警报。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "录制并上传车内摄像头" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "录制并上传麦克风音频" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"行驶时录制并保存麦克风音频。音频将包含在 comma connect 的行车记录视频中。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "法规" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "从容" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "远程访问" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "远程快照" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "请求超时" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Reset" +msgstr "重置" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "重置校准" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "查看训练指南" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "查看 openpilot 的规则、功能与限制" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "选择" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "SSH 密钥" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "正在扫描 Wi‑Fi 网络…" + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#, python-format +msgid "Select" +msgstr "选择" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "选择分支" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "选择语言" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "序列号" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "延后更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "软件" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "标准" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"建议使用标准模式。激进模式下,openpilot 会更贴近前车,油门与刹车更为激进;从" +"容模式下,会与前车保持更远距离。在支持的车型上,可用方向盘距离按钮切换这些风" +"格。" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "系统无响应" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "请立即接管控制" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "温度" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "目标分支" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "网络共享密码" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "切换" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#, python-format +msgid "UNINSTALL" +msgstr "卸载" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Uninstall" +msgstr "卸载" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "未知" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "仅在车辆熄火时下载更新。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "立即升级" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "上传车内摄像头数据,帮助改进驾驶员监控算法。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "使用公制" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"使用 openpilot 进行自适应巡航与车道保持辅助。使用此功能时,您必须始终保持专" +"注。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "车辆" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "查看" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "等待开始" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"警告:这将授予对您 GitHub 设置中所有公钥的 SSH 访问权限。请勿输入非您本人的 " +"GitHub 用户名。comma 员工绝不会要求您添加他们的用户名。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "欢迎使用 openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "启用后,踩下加速踏板将会脱离 openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "Wi‑Fi 计量网络" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "密码错误" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "您必须接受条款与条件才能使用 openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"您必须接受条款与条件才能使用 openpilot。继续前请阅读 https://comma.ai/terms " +"上的最新条款。" + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "相机启动中" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "默认" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "down" +msgstr "下" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#, python-format +msgid "failed to check for update" +msgstr "检查更新失败" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "用于“{}”" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "公里/时" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "留空以自动配置" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "left" +msgstr "左" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "计量" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "英里/时" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#, python-format +msgid "never" +msgstr "从不" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#, python-format +msgid "now" +msgstr "现在" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "openpilot 纵向控制(Alpha)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot 无法使用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilot 默认以安稳模式行驶。实验模式会启用尚未准备好用于安稳模式的 Alpha 级" +"功能。实验功能如下:

端到端纵向控制


让驾驶模型控制油门与刹车。" +"openpilot 会像人类一样驾驶,包括在红灯与停牌前停车。由于驾驶模型决定行驶速" +"度,设定速度仅作为上限。这是 Alpha 质量功能;预期会有错误。

全新驾驶可" +"视化


在低速时,驾驶可视化将切换至面向道路的广角摄像头以更好显示部分转" +"弯。右上角也会显示实验模式图标。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +"openpilot 持续进行校准,通常无需重置。若车辆通电,重置校准将会重启 " +"openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilot 通过观察人类(例如您)的驾驶来学习。\n" +"\n" +"Firehose 模式可让您最大化上传训练数据,以改进 openpilot 的驾驶模型。更多数据" +"意味着更大的模型,也意味着更好的实验模式。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "openpilot 纵向控制可能会在未来更新中提供。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "openpilot 要求设备安装在左右 4°、上 5° 或下 9° 以内。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "right" +msgstr "右" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "不限流量" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "up" +msgstr "上" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#, python-format +msgid "up to date, last checked never" +msgstr "已是最新,最后检查:从未" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#, python-format +msgid "up to date, last checked {}" +msgstr "已是最新,最后检查:{}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "update available" +msgstr "有可用更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} 条警报" +msgstr[1] "{} 条警报" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "{} 天前" +msgstr[1] "{} 天前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "{} 小时前" +msgstr[1] "{} 小时前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "{} 分钟前" +msgstr[1] "{} 分钟前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "目前已有 {} 个您的驾驶片段被纳入训练数据集。" +msgstr[1] "目前已有 {} 个您的驾驶片段被纳入训练数据集。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ 已订阅" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Firehose 模式 🔥" diff --git a/selfdrive/ui/translations/app_zh-CHT.po b/selfdrive/ui/translations/app_zh-CHT.po new file mode 100644 index 0000000000..1e9e2c0948 --- /dev/null +++ b/selfdrive/ui/translations/app_zh-CHT.po @@ -0,0 +1,1192 @@ +# Language zh-CHT translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Automatically generated, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-22 19:09-0700\n" +"PO-Revision-Date: 2025-10-22 16:32-0700\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: zh-CHT\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#, python-format +msgid " Steering torque response calibration is complete." +msgstr " 轉向扭矩回應校正完成。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#, python-format +msgid " Steering torque response calibration is {}% complete." +msgstr " 轉向扭矩回應校正已完成 {}%。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." +msgstr " 您的裝置朝向 {:.1f}° {} 與 {:.1f}° {}。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +msgid "--" +msgstr "--" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "1 year of drive storage" +msgstr "1 年行駛資料儲存" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "24/7 LTE connectivity" +msgstr "全年無休 LTE 連線" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +msgid "2G" +msgstr "2G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +msgid "3G" +msgstr "3G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +msgid "5G" +msgstr "5G" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +msgid "" +"WARNING: openpilot longitudinal control is in alpha for this car and will " +"disable Automatic Emergency Braking (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. Changing this setting will restart openpilot if the car is " +"powered on." +msgstr "" +"警告:此車款的 openpilot 縱向控制仍為 alpha,將會停用自動緊急煞車 (AEB)。" +"

在此車款上,openpilot 預設使用車載 ACC,而非 openpilot 的縱向控" +"制。啟用此選項可切換為 openpilot 縱向控制。建議同時啟用實驗模式。若車輛通電," +"變更此設定將會重新啟動 openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#, python-format +msgid "

Steering lag calibration is complete." +msgstr "

轉向延遲校正完成。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#, python-format +msgid "

Steering lag calibration is {}% complete." +msgstr "

轉向延遲校正已完成 {}%。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#, python-format +msgid "ACTIVE" +msgstr "啟用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +msgid "" +"ADB (Android Debug Bridge) allows connecting to your device over USB or over " +"the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." +msgstr "" +"ADB (Android Debug Bridge) 可透過 USB 或網路連線至您的裝置。詳見 https://" +"docs.comma.ai/how-to/connect-to-comma。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +msgid "ADD" +msgstr "新增" + +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "APN Setting" +msgstr "APN 設定" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#, python-format +msgid "Acknowledge Excessive Actuation" +msgstr "確認過度作動" + +#: /home/batman/openpilot/system/ui/widgets/network.py:74 +#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#, python-format +msgid "Advanced" +msgstr "進階" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Aggressive" +msgstr "積極" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#, python-format +msgid "Agree" +msgstr "同意" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#, python-format +msgid "Always-On Driver Monitoring" +msgstr "持續啟用駕駛監控" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#, python-format +msgid "" +"An alpha version of openpilot longitudinal control can be tested, along with " +"Experimental mode, on non-release branches." +msgstr "openpilot 縱向控制的 alpha 版本可於非發行分支搭配實驗模式進行測試。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Are you sure you want to power off?" +msgstr "確定要關機嗎?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Are you sure you want to reboot?" +msgstr "確定要重新啟動嗎?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Are you sure you want to reset calibration?" +msgstr "確定要重設校正嗎?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Are you sure you want to uninstall?" +msgstr "確定要解除安裝嗎?" + +#: /home/batman/openpilot/system/ui/widgets/network.py:99 +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#, python-format +msgid "Back" +msgstr "返回" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#, python-format +msgid "Become a comma prime member at connect.comma.ai" +msgstr "前往 connect.comma.ai 成為 comma prime 會員" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#, python-format +msgid "Bookmark connect.comma.ai to your home screen to use it like an app" +msgstr "將 connect.comma.ai 加到主畫面,像 App 一樣使用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "CHANGE" +msgstr "變更" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#, python-format +msgid "CHECK" +msgstr "檢查" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "CHILL MODE ON" +msgstr "安穩模式已開啟" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#, python-format +msgid "CONNECT" +msgstr "CONNECT" + +#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#, python-format +msgid "CONNECTING..." +msgstr "CONNECTING..." + +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 +#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#, python-format +msgid "Cancel" +msgstr "取消" + +#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#, python-format +msgid "Cellular Metered" +msgstr "行動網路計量" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#, python-format +msgid "Change Language" +msgstr "變更語言" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#, python-format +msgid "Changing this setting will restart openpilot if the car is powered on." +msgstr "若車輛通電,變更此設定將重新啟動 openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#, python-format +msgid "Click \"add new device\" and scan the QR code on the right" +msgstr "點選「新增裝置」,掃描右側 QR 碼" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#, python-format +msgid "Close" +msgstr "關閉" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#, python-format +msgid "Current Version" +msgstr "目前版本" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#, python-format +msgid "DOWNLOAD" +msgstr "下載" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#, python-format +msgid "Decline" +msgstr "拒絕" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#, python-format +msgid "Decline, uninstall openpilot" +msgstr "拒絕並解除安裝 openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +msgid "Developer" +msgstr "開發人員" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +msgid "Device" +msgstr "裝置" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#, python-format +msgid "Disengage on Accelerator Pedal" +msgstr "踩下加速踏板時脫離" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#, python-format +msgid "Disengage to Power Off" +msgstr "脫離以關機" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#, python-format +msgid "Disengage to Reboot" +msgstr "脫離以重新啟動" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#, python-format +msgid "Disengage to Reset Calibration" +msgstr "脫離以重設校正" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +msgid "Display speed in km/h instead of mph." +msgstr "以 km/h 顯示速度(非 mph)。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#, python-format +msgid "Dongle ID" +msgstr "Dongle ID" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#, python-format +msgid "Download" +msgstr "下載" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "Driver Camera" +msgstr "車內鏡頭" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#, python-format +msgid "Driving Personality" +msgstr "駕駛風格" + +#: /home/batman/openpilot/system/ui/widgets/network.py:123 +#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#, python-format +msgid "EDIT" +msgstr "編輯" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +msgid "ERROR" +msgstr "錯誤" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +msgid "ETH" +msgstr "ETH" + +#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#, python-format +msgid "EXPERIMENTAL MODE ON" +msgstr "實驗模式已開啟" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#, python-format +msgid "Enable" +msgstr "啟用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#, python-format +msgid "Enable ADB" +msgstr "啟用 ADB" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#, python-format +msgid "Enable Lane Departure Warnings" +msgstr "啟用偏離車道警示" + +#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#, python-format +msgid "Enable Roaming" +msgstr "啟用漫遊" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#, python-format +msgid "Enable SSH" +msgstr "啟用 SSH" + +#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#, python-format +msgid "Enable Tethering" +msgstr "啟用網路共享" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +msgid "Enable driver monitoring even when openpilot is not engaged." +msgstr "即使未啟動 openpilot 亦啟用駕駛監控。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#, python-format +msgid "Enable openpilot" +msgstr "啟用 openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#, python-format +msgid "" +"Enable the openpilot longitudinal control (alpha) toggle to allow " +"Experimental mode." +msgstr "啟用 openpilot 縱向控制(alpha)切換,以使用實驗模式。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "Enter APN" +msgstr "輸入 APN" + +#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#, python-format +msgid "Enter SSID" +msgstr "輸入 SSID" + +#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#, python-format +msgid "Enter new tethering password" +msgstr "輸入新的網路共享密碼" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Enter password" +msgstr "輸入密碼" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#, python-format +msgid "Enter your GitHub username" +msgstr "輸入您的 GitHub 使用者名稱" + +#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 +#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#, python-format +msgid "Error" +msgstr "錯誤" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#, python-format +msgid "Experimental Mode" +msgstr "實驗模式" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#, python-format +msgid "" +"Experimental mode is currently unavailable on this car since the car's stock " +"ACC is used for longitudinal control." +msgstr "此車款目前無法使用實驗模式,因為縱向控制使用的是原廠 ACC。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#, python-format +msgid "FORGETTING..." +msgstr "正在遺忘..." + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#, python-format +msgid "Finish Setup" +msgstr "完成設定" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +msgid "Firehose" +msgstr "Firehose" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +msgid "Firehose Mode" +msgstr "Firehose 模式" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +msgid "" +"For maximum effectiveness, bring your device inside and connect to a good " +"USB-C adapter and Wi-Fi weekly.\n" +"\n" +"Firehose Mode can also work while you're driving if connected to a hotspot " +"or unlimited SIM card.\n" +"\n" +"\n" +"Frequently Asked Questions\n" +"\n" +"Does it matter how or where I drive? Nope, just drive as you normally " +"would.\n" +"\n" +"Do all of my segments get pulled in Firehose Mode? No, we selectively pull a " +"subset of your segments.\n" +"\n" +"What's a good USB-C adapter? Any fast phone or laptop charger should be " +"fine.\n" +"\n" +"Does it matter which software I run? Yes, only upstream openpilot (and " +"particular forks) are able to be used for training." +msgstr "" +"為達最佳效果,請將裝置帶到室內,並每週連接優質 USB‑C 充電器與 Wi‑Fi。\n" +"\n" +"若連上熱點或吃到飽門號,行車中也可使用 Firehose 模式。\n" +"\n" +"\n" +"常見問題\n" +"\n" +"我怎麼開、在哪裡開有差嗎?沒有,平常怎麼開就怎麼開。\n" +"\n" +"Firehose 模式會拉取我所有片段嗎?不會,我們會選擇性拉取部分片段。\n" +"\n" +"什麼是好的 USB‑C 充電器?任何快速的手機或筆電充電器都可以。\n" +"\n" +"我跑什麼軟體有差嗎?有,只有上游 openpilot(及特定分支)可用於訓練。" + +#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#, python-format +msgid "Forget" +msgstr "忘記" + +#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#, python-format +msgid "Forget Wi-Fi Network \"{}\"?" +msgstr "要忘記 Wi‑Fi 網路「{}」嗎?" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +msgid "GOOD" +msgstr "良好" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#, python-format +msgid "Go to https://connect.comma.ai on your phone" +msgstr "在手機上前往 https://connect.comma.ai" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "HIGH" +msgstr "高" + +#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#, python-format +msgid "Hidden Network" +msgstr "隱藏網路" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#, python-format +msgid "INACTIVE: connect to an unmetered network" +msgstr "未啟用:請連接不限流量網路" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#, python-format +msgid "INSTALL" +msgstr "安裝" + +#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#, python-format +msgid "IP Address" +msgstr "IP 位址" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#, python-format +msgid "Install Update" +msgstr "安裝更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#, python-format +msgid "Joystick Debug Mode" +msgstr "搖桿除錯模式" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +msgid "LOADING" +msgstr "載入中" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +msgid "LTE" +msgstr "LTE" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#, python-format +msgid "Longitudinal Maneuver Mode" +msgstr "縱向操作模式" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#, python-format +msgid "MAX" +msgstr "最大" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#, python-format +msgid "" +"Maximize your training data uploads to improve openpilot's driving models." +msgstr "最大化上傳訓練資料,以改進 openpilot 的駕駛模型。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "N/A" +msgstr "無" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "NO" +msgstr "否" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +msgid "Network" +msgstr "網路" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#, python-format +msgid "No SSH keys found" +msgstr "找不到 SSH 金鑰" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#, python-format +msgid "No SSH keys found for user '{}'" +msgstr "找不到使用者 '{}' 的 SSH 金鑰" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#, python-format +msgid "No release notes available." +msgstr "無可用發行說明。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +msgid "OFFLINE" +msgstr "離線" + +#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 +#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#, python-format +msgid "OK" +msgstr "確定" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "ONLINE" +msgstr "線上" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#, python-format +msgid "Open" +msgstr "開啟" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "PAIR" +msgstr "配對" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +msgid "PANDA" +msgstr "PANDA" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#, python-format +msgid "PREVIEW" +msgstr "預覽" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#, python-format +msgid "PRIME FEATURES:" +msgstr "PRIME 功能:" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#, python-format +msgid "Pair Device" +msgstr "配對裝置" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#, python-format +msgid "Pair device" +msgstr "配對裝置" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#, python-format +msgid "Pair your device to your comma account" +msgstr "將裝置配對至您的 comma 帳號" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#, python-format +msgid "" +"Pair your device with comma connect (connect.comma.ai) and claim your comma " +"prime offer." +msgstr "" +"將裝置與 comma connect(connect.comma.ai)配對,領取您的 comma prime 優惠。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#, python-format +msgid "Please connect to Wi-Fi to complete initial pairing" +msgstr "請連線至 Wi‑Fi 以完成初始化配對" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#, python-format +msgid "Power Off" +msgstr "關機" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Prevent large data uploads when on a metered Wi-Fi connection" +msgstr "在計量制 Wi‑Fi 連線時避免大量上傳" + +#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#, python-format +msgid "Prevent large data uploads when on a metered cellular connection" +msgstr "在計量制行動網路時避免大量上傳" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +msgid "" +"Preview the driver facing camera to ensure that driver monitoring has good " +"visibility. (vehicle must be off)" +msgstr "預覽車內鏡頭以確保駕駛監控視野良好。(車輛須熄火)" + +#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#, python-format +msgid "QR Code Error" +msgstr "QR 碼錯誤" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +msgid "REMOVE" +msgstr "移除" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "RESET" +msgstr "重設" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "REVIEW" +msgstr "檢視" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#, python-format +msgid "Reboot" +msgstr "重新啟動" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#, python-format +msgid "Reboot Device" +msgstr "重新啟動裝置" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#, python-format +msgid "Reboot and Update" +msgstr "重新啟動並更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +msgid "" +"Receive alerts to steer back into the lane when your vehicle drifts over a " +"detected lane line without a turn signal activated while driving over 31 mph " +"(50 km/h)." +msgstr "" +"當車輛以超過 31 mph(50 km/h)行駛且未打方向燈越過偵測到的車道線時,接收轉向" +"回車道的警示。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#, python-format +msgid "Record and Upload Driver Camera" +msgstr "錄製並上傳車內鏡頭" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#, python-format +msgid "Record and Upload Microphone Audio" +msgstr "錄製並上傳麥克風音訊" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +msgid "" +"Record and store microphone audio while driving. The audio will be included " +"in the dashcam video in comma connect." +msgstr "" +"行車時錄製並儲存麥克風音訊。音訊將包含在 comma connect 的行車紀錄影片中。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "Regulatory" +msgstr "法規" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Relaxed" +msgstr "從容" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote access" +msgstr "遠端存取" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#, python-format +msgid "Remote snapshots" +msgstr "遠端擷圖" + +#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#, python-format +msgid "Request timed out" +msgstr "要求逾時" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#, python-format +msgid "Reset" +msgstr "重設" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#, python-format +msgid "Reset Calibration" +msgstr "重設校正" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#, python-format +msgid "Review Training Guide" +msgstr "檢視訓練指南" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +msgid "Review the rules, features, and limitations of openpilot" +msgstr "檢視 openpilot 的規則、功能與限制" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "SELECT" +msgstr "選取" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#, python-format +msgid "SSH Keys" +msgstr "SSH 金鑰" + +#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#, python-format +msgid "Scanning Wi-Fi networks..." +msgstr "正在掃描 Wi‑Fi 網路…" + +#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#, python-format +msgid "Select" +msgstr "選取" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#, python-format +msgid "Select a branch" +msgstr "選取分支" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#, python-format +msgid "Select a language" +msgstr "選取語言" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#, python-format +msgid "Serial" +msgstr "序號" + +#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#, python-format +msgid "Snooze Update" +msgstr "延後更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +msgid "Software" +msgstr "軟體" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#, python-format +msgid "Standard" +msgstr "標準" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +msgid "" +"Standard is recommended. In aggressive mode, openpilot will follow lead cars " +"closer and be more aggressive with the gas and brake. In relaxed mode " +"openpilot will stay further away from lead cars. On supported cars, you can " +"cycle through these personalities with your steering wheel distance button." +msgstr "" +"建議使用標準模式。積極模式下,openpilot 會更貼近前車,油門與煞車反應更積極;" +"從容模式下,會與前車保持更遠距離。於支援車款,可用方向盤距離按鈕切換這些風" +"格。" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#, python-format +msgid "System Unresponsive" +msgstr "系統無回應" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#, python-format +msgid "TAKE CONTROL IMMEDIATELY" +msgstr "請立刻接手控制" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +msgid "TEMP" +msgstr "溫度" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#, python-format +msgid "Target Branch" +msgstr "目標分支" + +#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#, python-format +msgid "Tethering Password" +msgstr "網路共享密碼" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +msgid "Toggles" +msgstr "切換" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#, python-format +msgid "UNINSTALL" +msgstr "解除安裝" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#, python-format +msgid "UPDATE" +msgstr "更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#, python-format +msgid "Uninstall" +msgstr "解除安裝" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +msgid "Unknown" +msgstr "未知" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#, python-format +msgid "Updates are only downloaded while the car is off." +msgstr "僅在車輛熄火時下載更新。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#, python-format +msgid "Upgrade Now" +msgstr "立即升級" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +msgid "" +"Upload data from the driver facing camera and help improve the driver " +"monitoring algorithm." +msgstr "上傳車內鏡頭資料,協助改善駕駛監控演算法。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#, python-format +msgid "Use Metric System" +msgstr "使用公制" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +msgid "" +"Use the openpilot system for adaptive cruise control and lane keep driver " +"assistance. Your attention is required at all times to use this feature." +msgstr "" +"使用 openpilot 進行 ACC 與車道維持輔助。使用此功能時,您必須始終保持專注。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +msgid "VEHICLE" +msgstr "車輛" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#, python-format +msgid "VIEW" +msgstr "檢視" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#, python-format +msgid "Waiting to start" +msgstr "等待開始" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +msgid "" +"Warning: This grants SSH access to all public keys in your GitHub settings. " +"Never enter a GitHub username other than your own. A comma employee will " +"NEVER ask you to add their GitHub username." +msgstr "" +"警告:這將授予對您 GitHub 設定中所有公開金鑰的 SSH 存取權。請勿輸入非您本人" +"的 GitHub 帳號。comma 員工絕不會要求您新增他們的帳號。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#, python-format +msgid "Welcome to openpilot" +msgstr "歡迎使用 openpilot" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +msgid "When enabled, pressing the accelerator pedal will disengage openpilot." +msgstr "啟用後,踩下加速踏板將會脫離 openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +msgid "Wi-Fi" +msgstr "Wi‑Fi" + +#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#, python-format +msgid "Wi-Fi Network Metered" +msgstr "Wi‑Fi 計量網路" + +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "Wrong password" +msgstr "密碼錯誤" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#, python-format +msgid "You must accept the Terms and Conditions in order to use openpilot." +msgstr "您必須接受條款與細則才能使用 openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#, python-format +msgid "" +"You must accept the Terms and Conditions to use openpilot. Read the latest " +"terms at https://comma.ai/terms before continuing." +msgstr "" +"您必須接受條款與細則才能使用 openpilot。繼續前請閱讀 https://comma.ai/terms " +"上的最新條款。" + +#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#, python-format +msgid "camera starting" +msgstr "相機啟動中" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#, python-format +msgid "comma prime" +msgstr "comma prime" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "default" +msgstr "預設" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "down" +msgstr "下" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#, python-format +msgid "failed to check for update" +msgstr "檢查更新失敗" + +#: /home/batman/openpilot/system/ui/widgets/network.py:237 +#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#, python-format +msgid "for \"{}\"" +msgstr "適用於「{}」" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "km/h" +msgstr "公里/時" + +#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#, python-format +msgid "leave blank for automatic configuration" +msgstr "留空以自動設定" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "left" +msgstr "左" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "metered" +msgstr "計量" + +#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#, python-format +msgid "mph" +msgstr "英里/時" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#, python-format +msgid "never" +msgstr "從不" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#, python-format +msgid "now" +msgstr "現在" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#, python-format +msgid "openpilot Longitudinal Control (Alpha)" +msgstr "openpilot 縱向控制(Alpha)" + +#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#, python-format +msgid "openpilot Unavailable" +msgstr "openpilot 無法使用" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#, python-format +msgid "" +"openpilot defaults to driving in chill mode. Experimental mode enables alpha-" +"level features that aren't ready for chill mode. Experimental features are " +"listed below:

End-to-End Longitudinal Control


Let the driving " +"model control the gas and brakes. openpilot will drive as it thinks a human " +"would, including stopping for red lights and stop signs. Since the driving " +"model decides the speed to drive, the set speed will only act as an upper " +"bound. This is an alpha quality feature; mistakes should be expected." +"

New Driving Visualization


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." +msgstr "" +"openpilot 預設以安穩模式行駛。實驗模式啟用尚未準備好進入安穩模式的 Alpha 等級" +"功能。實驗功能如下:

端到端縱向控制


讓駕駛模型控制油門與煞車。" +"openpilot 會如同人類駕駛般行駛,包括在紅燈與停車標誌前停車。由於駕駛模型決定" +"行駛速度,設定速度僅作為上限。此為 Alpha 品質功能;預期會有失誤。

全新" +"駕駛視覺化


在低速時,駕駛視覺化將切換至面向道路的廣角鏡頭以更好呈現部" +"分轉彎。右上角亦會顯示實驗模式圖示。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#, python-format +msgid "" +"openpilot is continuously calibrating, resetting is rarely required. " +"Resetting calibration will restart openpilot if the car is powered on." +msgstr "" +"openpilot 會持續校正,通常不需重設。若車輛通電,重設校正將重新啟動 " +"openpilot。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +msgid "" +"openpilot learns to drive by watching humans, like you, drive.\n" +"\n" +"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." +msgstr "" +"openpilot 透過觀察人類(也就是您)的駕駛方式來學習。\n" +"\n" +"Firehose 模式可讓您最大化上傳訓練資料,以改進 openpilot 的駕駛模型。更多資料" +"代表更大的模型,也就代表更好的實驗模式。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#, python-format +msgid "openpilot longitudinal control may come in a future update." +msgstr "openpilot 縱向控制可能於未來更新提供。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +msgid "" +"openpilot requires the device to be mounted within 4° left or right and " +"within 5° up or 9° down." +msgstr "openpilot 要求裝置安裝在左右 4°、上 5° 或下 9° 以內。" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#, python-format +msgid "right" +msgstr "右" + +#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#, python-format +msgid "unmetered" +msgstr "不限流量" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#, python-format +msgid "up" +msgstr "上" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#, python-format +msgid "up to date, last checked never" +msgstr "已為最新,最後檢查:從未" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#, python-format +msgid "up to date, last checked {}" +msgstr "已為最新,最後檢查:{}" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#, python-format +msgid "update available" +msgstr "有可用更新" + +#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#, python-format +msgid "{} ALERT" +msgid_plural "{} ALERTS" +msgstr[0] "{} 則警示" +msgstr[1] "{} 則警示" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#, python-format +msgid "{} day ago" +msgid_plural "{} days ago" +msgstr[0] "{} 天前" +msgstr[1] "{} 天前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#, python-format +msgid "{} hour ago" +msgid_plural "{} hours ago" +msgstr[0] "{} 小時前" +msgstr[1] "{} 小時前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#, python-format +msgid "{} minute ago" +msgid_plural "{} minutes ago" +msgstr[0] "{} 分鐘前" +msgstr[1] "{} 分鐘前" + +#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#, python-format +msgid "{} segment of your driving is in the training dataset so far." +msgid_plural "{} segments of your driving is in the training dataset so far." +msgstr[0] "目前已有 {} 個您的駕駛片段納入訓練資料集。" +msgstr[1] "目前已有 {} 個您的駕駛片段納入訓練資料集。" + +#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#, python-format +msgid "✓ SUBSCRIBED" +msgstr "✓ 已訂閱" + +#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#, python-format +msgid "🔥 Firehose Mode 🔥" +msgstr "🔥 Firehose 模式 🔥" diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 476a33a99d..68e1fdc44a 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -51,6 +51,14 @@ class FontWeight(StrEnum): BOLD = "Inter-Bold.ttf" EXTRA_BOLD = "Inter-ExtraBold.ttf" BLACK = "Inter-Black.ttf" + UNIFONT = "unifont.otf" + + +def font_fallback(font: rl.Font) -> rl.Font: + """Fall back to unifont for languages that require it.""" + if multilang.requires_unifont(): + return gui_app.font(FontWeight.UNIFONT) + return font @dataclass @@ -335,7 +343,7 @@ class GuiApplication: except KeyboardInterrupt: pass - def font(self, font_weight: FontWeight = FontWeight.NORMAL): + def font(self, font_weight: FontWeight = FontWeight.NORMAL) -> rl.Font: return self._fonts[font_weight] @property @@ -356,14 +364,16 @@ class GuiApplication: all_chars |= set("–‑✓×°§•") # Load only the characters used in translations - for language in multilang.codes: + for language, code in multilang.languages.items(): + all_chars |= set(language) try: - with open(os.path.join(TRANSLATIONS_DIR, f"app_{language}.po")) as f: + with open(os.path.join(TRANSLATIONS_DIR, f"app_{code}.po")) as f: all_chars |= set(f.read()) except FileNotFoundError: - cloudlog.warning(f"Translation file for language '{language}' not found when loading fonts.") + cloudlog.warning(f"Translation file for language '{code}' not found when loading fonts.") all_chars = "".join(all_chars) + cloudlog.debug(f"Loading fonts with {len(all_chars)} glyphs.") codepoint_count = rl.ffi.new("int *", 1) codepoints = rl.load_codepoints(all_chars, codepoint_count) @@ -390,6 +400,7 @@ class GuiApplication: rl._orig_draw_text_ex = rl.draw_text_ex def _draw_text_ex_scaled(font, text, position, font_size, spacing, tint): + font = font_fallback(font) return rl._orig_draw_text_ex(font, text, position, font_size * FONT_SCALE, spacing, tint) rl.draw_text_ex = _draw_text_ex_scaled diff --git a/system/ui/lib/multilang.py b/system/ui/lib/multilang.py index 7670808924..03d519e601 100644 --- a/system/ui/lib/multilang.py +++ b/system/ui/lib/multilang.py @@ -3,25 +3,27 @@ import json import gettext from openpilot.common.params import Params from openpilot.common.basedir import BASEDIR +from openpilot.common.swaglog import cloudlog SYSTEM_UI_DIR = os.path.join(BASEDIR, "system", "ui") UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui") TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations") LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json") -SUPPORTED_LANGUAGES = [ - "en", - "de", - "fr", - "pt-BR", - "es", - "tr", +UNIFONT_LANGUAGES = [ + "ar", + "th", + "zh-CHT", + "zh-CHS", + "ko", + "ja", ] class Multilang: def __init__(self): self._params = Params() + self._language: str = "en" self.languages = {} self.codes = {} self._translation: gettext.NullTranslations | gettext.GNUTranslations = gettext.NullTranslations() @@ -29,28 +31,28 @@ class Multilang: @property def language(self) -> str: - lang = str(self._params.get("LanguageSetting")).strip("main_") - if lang not in SUPPORTED_LANGUAGES: - lang = "en" - return lang + return self._language + + def requires_unifont(self) -> bool: + """Certain languages require unifont to render their glyphs.""" + return self._language in UNIFONT_LANGUAGES def setup(self): - language = self.language try: - with open(os.path.join(TRANSLATIONS_DIR, f'app_{language}.mo'), 'rb') as fh: + with open(os.path.join(TRANSLATIONS_DIR, f'app_{self._language}.mo'), 'rb') as fh: translation = gettext.GNUTranslations(fh) translation.install() self._translation = translation - print(f"Loaded translations for language: {language}") + cloudlog.warning(f"Loaded translations for language: {self._language}") except FileNotFoundError: - print(f"No translation file found for language: {language}, using default.") + cloudlog.error(f"No translation file found for language: {self._language}, using default.") gettext.install('app') self._translation = gettext.NullTranslations() - return None def change_language(self, language_code: str) -> None: # Reinstall gettext with the selected language self._params.put("LanguageSetting", language_code) + self._language = language_code self.setup() def tr(self, text: str) -> str: @@ -61,8 +63,12 @@ class Multilang: def _load_languages(self): with open(LANGUAGES_FILE, encoding='utf-8') as f: - self.languages = {k: v for k, v in json.load(f).items() if v in SUPPORTED_LANGUAGES} - self.codes = {v: k for k, v in self.languages.items() if v in SUPPORTED_LANGUAGES} + self.languages = json.load(f) + self.codes = {v: k for k, v in self.languages.items()} + + lang = str(self._params.get("LanguageSetting")).removeprefix("main_") + if lang in self.codes: + self._language = lang multilang = Multilang() diff --git a/system/ui/lib/text_measure.py b/system/ui/lib/text_measure.py index b91090e051..ea6fa63365 100644 --- a/system/ui/lib/text_measure.py +++ b/system/ui/lib/text_measure.py @@ -1,5 +1,5 @@ import pyray as rl -from openpilot.system.ui.lib.application import FONT_SCALE +from openpilot.system.ui.lib.application import FONT_SCALE, font_fallback from openpilot.system.ui.lib.emoji import find_emoji _cache: dict[int, rl.Vector2] = {} @@ -7,6 +7,7 @@ _cache: dict[int, rl.Vector2] = {} def measure_text_cached(font: rl.Font, text: str, font_size: int, spacing: int = 0) -> rl.Vector2: """Caches text measurements to avoid redundant calculations.""" + font = font_fallback(font) key = hash((font.texture.id, text, font_size, spacing)) if key in _cache: return _cache[key] diff --git a/system/ui/lib/wrap_text.py b/system/ui/lib/wrap_text.py index f6caa3c5f0..d51975087a 100644 --- a/system/ui/lib/wrap_text.py +++ b/system/ui/lib/wrap_text.py @@ -1,5 +1,6 @@ import pyray as rl from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.application import font_fallback def _break_long_word(font: rl.Font, word: str, font_size: int, max_width: int) -> list[str]: @@ -40,6 +41,7 @@ _cache: dict[int, list[str]] = {} def wrap_text(font: rl.Font, text: str, font_size: int, max_width: int) -> list[str]: + font = font_fallback(font) key = hash((font.texture.id, text, font_size, max_width)) if key in _cache: return _cache[key] diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 10f6f1400d..4259208107 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -134,9 +134,6 @@ class Label(Widget): self._font_size = size self._update_text(self._text) - def _update_layout_rects(self): - self._update_text(self._text) - def _update_text(self, text): self._emojis = [] self._text_size = [] @@ -170,6 +167,10 @@ class Label(Widget): self._text_size.append(measure_text_cached(self._font, t, self._font_size)) def _render(self, _): + # Text can be a callable + # TODO: cache until text changed + self._update_text(self._text) + text_size = self._text_size[0] if self._text_size else rl.Vector2(0.0, 0.0) if self._text_alignment_vertical == rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE: text_pos = rl.Vector2(self._rect.x, (self._rect.y + (self._rect.height - text_size.y) // 2)) diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index fc42b48081..3b2201164a 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -17,7 +17,7 @@ LIST_ITEM_SPACING = 25 class MultiOptionDialog(Widget): - def __init__(self, title, options, current=""): + def __init__(self, title, options, current="", option_font_weight=FontWeight.MEDIUM): super().__init__() self.title = title self.options = options @@ -27,6 +27,7 @@ class MultiOptionDialog(Widget): # Create scroller with option buttons self.option_buttons = [Button(option, click_callback=lambda opt=option: self._on_option_clicked(opt), + font_weight=option_font_weight, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.NORMAL, text_padding=50, elide_right=True) for option in options] self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING) From 4f52f3f3c5b0bd31375650b68cd78eb1e2d0a668 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Wed, 22 Oct 2025 21:48:44 -0500 Subject: [PATCH 238/341] raylib: match QT colors for danger button style (#36431) match colors for DANGER style --- system/ui/widgets/button.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index bd6517ad49..5b9c51886d 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -47,7 +47,7 @@ BUTTON_DISABLED_TEXT_COLORS = { BUTTON_BACKGROUND_COLORS = { ButtonStyle.NORMAL: rl.Color(51, 51, 51, 255), ButtonStyle.PRIMARY: rl.Color(70, 91, 234, 255), - ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), + ButtonStyle.DANGER: rl.Color(226, 44, 44, 255), ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.BLACK, @@ -61,7 +61,7 @@ BUTTON_BACKGROUND_COLORS = { BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.NORMAL: rl.Color(74, 74, 74, 255), ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255), - ButtonStyle.DANGER: rl.Color(204, 0, 0, 255), + ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK, ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.BLANK, From 378212e5ab9d460f6f47c047f52fcc0b52df22b0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 23 Oct 2025 12:24:07 +0800 Subject: [PATCH 239/341] cabana: remove dependency on selfdrive/ui (#36434) remove dependency on selfdrive/ui --- tools/cabana/SConscript | 64 +++++++++++- tools/cabana/cabana.cc | 1 - tools/cabana/detailwidget.cc | 1 + tools/cabana/detailwidget.h | 2 +- tools/cabana/streams/routes.h | 3 +- tools/cabana/utils/api.cc | 161 ++++++++++++++++++++++++++++++ tools/cabana/utils/api.h | 47 +++++++++ tools/cabana/utils/elidedlabel.cc | 29 ++++++ tools/cabana/utils/elidedlabel.h | 25 +++++ tools/cabana/utils/util.cc | 86 +++++++++++++++- tools/cabana/utils/util.h | 2 + 11 files changed, 410 insertions(+), 11 deletions(-) create mode 100644 tools/cabana/utils/api.cc create mode 100644 tools/cabana/utils/api.h create mode 100644 tools/cabana/utils/elidedlabel.cc create mode 100644 tools/cabana/utils/elidedlabel.h diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 89be3cceb2..e3674452d5 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -1,7 +1,61 @@ -Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'replay_lib', 'cereal', 'widgets') +import subprocess +import os + +Import('env', 'arch', 'common', 'messaging', 'visionipc', 'replay_lib', 'cereal') + +qt_env = env.Clone() +qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "DBus", "Xml"] + +qt_libs = [] +if arch == "Darwin": + brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip() + qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5" + qt_dirs = [ + os.path.join(qt_env['QTDIR'], "include"), + ] + qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules] + qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")] + qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"] + qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin")) +else: + qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip() + qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip() + + qt_env['QTDIR'] = qt_install_prefix + qt_dirs = [ + f"{qt_install_headers}", + ] + + qt_gui_path = os.path.join(qt_install_headers, "QtGui") + qt_gui_dirs = [d for d in os.listdir(qt_gui_path) if os.path.isdir(os.path.join(qt_gui_path, d))] + qt_dirs += [f"{qt_install_headers}/QtGui/{qt_gui_dirs[0]}/QtGui", ] if qt_gui_dirs else [] + qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules] + + qt_libs = [f"Qt5{m}" for m in qt_modules] + if arch == "larch64": + qt_libs += ["GLESv2", "wayland-client"] + qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath) + elif arch != "Darwin": + qt_libs += ["GL"] +qt_env['QT3DIR'] = qt_env['QTDIR'] +qt_env.Tool('qt3') + +qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"] +qt_flags = [ + "-D_REENTRANT", + "-DQT_NO_DEBUG", + "-DQT_WIDGETS_LIB", + "-DQT_GUI_LIB", + "-DQT_CORE_LIB", + "-DQT_MESSAGELOGCONTEXT", +] +qt_env['CXXFLAGS'] += qt_flags +qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] +qt_env['LIBPATH'] += ['#selfdrive/ui', ] +qt_env['LIBS'] = qt_libs base_frameworks = qt_env['FRAMEWORKS'] -base_libs = [common, messaging, cereal, visionipc, 'qt_util', 'm', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] +base_libs = [common, messaging, cereal, visionipc, 'm', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] if arch == "Darwin": base_frameworks.append('OpenCL') @@ -12,11 +66,11 @@ else: base_libs.append('Qt5Charts') base_libs.append('Qt5SerialBus') -qt_libs = ['qt_util'] + base_libs +qt_libs = base_libs cabana_env = qt_env.Clone() -cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'zstd', 'curl', 'yuv', 'usb-1.0'] + qt_libs +cabana_libs = [cereal, messaging, visionipc, replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'zstd', 'curl', 'yuv', 'usb-1.0'] + qt_libs opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../opendbc/dbc").abspath) cabana_env['CXXFLAGS'] += [opendbc_path] @@ -28,7 +82,7 @@ cabana_env.Depends(assets, Glob('/assets/*', exclude=[assets, assets_src, "asset cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/socketcanstream.cc', 'streams/pandastream.cc', 'streams/devicestream.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'historylog.cc', 'videowidget.cc', 'signalview.cc', 'streams/routes.cc', 'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc', - 'utils/export.cc', 'utils/util.cc', + 'utils/export.cc', 'utils/util.cc', 'utils/elidedlabel.cc', 'utils/api.cc', 'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc', 'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'panda.cc', 'cameraview.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc', 'tools/routeinfo.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index 97f178b1f3..bc50afc03a 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -1,7 +1,6 @@ #include #include -#include "selfdrive/ui/qt/util.h" #include "tools/cabana/mainwin.h" #include "tools/cabana/streams/devicestream.h" #include "tools/cabana/streams/pandastream.h" diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 2e213988bc..4eda46f37b 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "tools/cabana/commands.h" diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 47304fbdc5..6df164b442 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -6,11 +6,11 @@ #include #include -#include "selfdrive/ui/qt/widgets/controls.h" #include "tools/cabana/binaryview.h" #include "tools/cabana/chart/chartswidget.h" #include "tools/cabana/historylog.h" #include "tools/cabana/signalview.h" +#include "tools/cabana/utils/elidedlabel.h" class EditMessageDialog : public QDialog { public: diff --git a/tools/cabana/streams/routes.h b/tools/cabana/streams/routes.h index ee50712212..045dc67220 100644 --- a/tools/cabana/streams/routes.h +++ b/tools/cabana/streams/routes.h @@ -2,8 +2,7 @@ #include #include - -#include "selfdrive/ui/qt/api.h" +#include "tools/cabana/utils/api.h" class RouteListWidget; class OneShotHttpRequest; diff --git a/tools/cabana/utils/api.cc b/tools/cabana/utils/api.cc new file mode 100644 index 0000000000..99e904c490 --- /dev/null +++ b/tools/cabana/utils/api.cc @@ -0,0 +1,161 @@ +#include "selfdrive/ui/qt/api.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common/util.h" +#include "system/hardware/hw.h" +#include "selfdrive/ui/qt/util.h" + +QString getVersion() { + static QString version = QString::fromStdString(Params().get("Version")); + return version; +} + +QString getUserAgent() { + return "openpilot-" + getVersion(); +} + +std::optional getDongleId() { + std::string id = Params().get("DongleId"); + + if (!id.empty() && (id != "UnregisteredDevice")) { + return QString::fromStdString(id); + } else { + return {}; + } +} + +namespace CommaApi { + +RSA *get_rsa_private_key() { + static std::unique_ptr rsa_private(nullptr, RSA_free); + if (!rsa_private) { + FILE *fp = fopen(Path::rsa_file().c_str(), "rb"); + if (!fp) { + qDebug() << "No RSA private key found, please run manager.py or registration.py"; + return nullptr; + } + rsa_private.reset(PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL)); + fclose(fp); + } + return rsa_private.get(); +} + +QByteArray rsa_sign(const QByteArray &data) { + RSA *rsa_private = get_rsa_private_key(); + if (!rsa_private) return {}; + + QByteArray sig(RSA_size(rsa_private), Qt::Uninitialized); + unsigned int sig_len; + int ret = RSA_sign(NID_sha256, (unsigned char*)data.data(), data.size(), (unsigned char*)sig.data(), &sig_len, rsa_private); + assert(ret == 1); + assert(sig.size() == sig_len); + return sig; +} + +QString create_jwt(const QJsonObject &payloads, int expiry) { + QJsonObject header = {{"alg", "RS256"}}; + + auto t = QDateTime::currentSecsSinceEpoch(); + QJsonObject payload = {{"identity", getDongleId().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 + "." + rsa_sign(hash).toBase64(b64_opts); +} + +} // namespace CommaApi + +HttpRequest::HttpRequest(QObject *parent, bool create_jwt, int timeout) : create_jwt(create_jwt), QObject(parent) { + networkTimer = new QTimer(this); + networkTimer->setSingleShot(true); + networkTimer->setInterval(timeout); + connect(networkTimer, &QTimer::timeout, this, &HttpRequest::requestTimeout); +} + +bool HttpRequest::active() const { + return reply != nullptr; +} + +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; + } + QString token; + if (create_jwt) { + token = CommaApi::create_jwt(); + } 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()); + + if (!token.isEmpty()) { + request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8()); + } + + if (method == HttpRequest::Method::GET) { + reply = nam()->get(request); + } else if (method == HttpRequest::Method::DELETE) { + reply = nam()->deleteResource(request); + } + + networkTimer->start(); + connect(reply, &QNetworkReply::finished, this, &HttpRequest::requestFinished); +} + +void HttpRequest::requestTimeout() { + reply->abort(); +} + +void HttpRequest::requestFinished() { + networkTimer->stop(); + + if (reply->error() == QNetworkReply::NoError) { + emit requestDone(reply->readAll(), true, reply->error()); + } else { + QString error; + if (reply->error() == QNetworkReply::OperationCanceledError) { + nam()->clearAccessCache(); + nam()->clearConnectionCache(); + error = "Request timed out"; + } else { + error = reply->errorString(); + } + emit requestDone(error, false, reply->error()); + } + + reply->deleteLater(); + reply = nullptr; +} + +QNetworkAccessManager *HttpRequest::nam() { + static QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager(qApp); + return networkAccessManager; +} diff --git a/tools/cabana/utils/api.h b/tools/cabana/utils/api.h new file mode 100644 index 0000000000..ad64d7e722 --- /dev/null +++ b/tools/cabana/utils/api.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +#include "common/util.h" + +namespace CommaApi { + +const QString BASE_URL = util::getenv("API_HOST", "https://api.commadotai.com").c_str(); +QByteArray rsa_sign(const QByteArray &data); +QString create_jwt(const QJsonObject &payloads = {}, int expiry = 3600); + +} // namespace CommaApi + +/** + * Makes a request to the request endpoint. + */ + +class HttpRequest : public QObject { + Q_OBJECT + +public: + enum class Method {GET, DELETE}; + + explicit HttpRequest(QObject* parent, bool create_jwt = true, int timeout = 20000); + void sendRequest(const QString &requestURL, const Method method = Method::GET); + bool active() const; + bool timeout() const; + +signals: + void requestDone(const QString &response, bool success, QNetworkReply::NetworkError error); + +protected: + QNetworkReply *reply = nullptr; + +private: + static QNetworkAccessManager *nam(); + QTimer *networkTimer = nullptr; + bool create_jwt; + +private slots: + void requestTimeout(); + void requestFinished(); +}; diff --git a/tools/cabana/utils/elidedlabel.cc b/tools/cabana/utils/elidedlabel.cc new file mode 100644 index 0000000000..629a108b16 --- /dev/null +++ b/tools/cabana/utils/elidedlabel.cc @@ -0,0 +1,29 @@ +#include "tools/cabana/utils/elidedlabel.h" +#include +#include + +ElidedLabel::ElidedLabel(QWidget *parent) : ElidedLabel({}, parent) {} + +ElidedLabel::ElidedLabel(const QString &text, QWidget *parent) : QLabel(text.trimmed(), parent) { + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + setMinimumWidth(1); +} + +void ElidedLabel::resizeEvent(QResizeEvent* event) { + QLabel::resizeEvent(event); + lastText_ = elidedText_ = ""; +} + +void ElidedLabel::paintEvent(QPaintEvent *event) { + const QString curText = text(); + if (curText != lastText_) { + elidedText_ = fontMetrics().elidedText(curText, Qt::ElideRight, contentsRect().width()); + lastText_ = curText; + } + + QPainter painter(this); + drawFrame(&painter); + QStyleOption opt; + opt.initFrom(this); + style()->drawItemText(&painter, contentsRect(), alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole()); +} diff --git a/tools/cabana/utils/elidedlabel.h b/tools/cabana/utils/elidedlabel.h new file mode 100644 index 0000000000..577eea12a0 --- /dev/null +++ b/tools/cabana/utils/elidedlabel.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +class ElidedLabel : public QLabel { + Q_OBJECT + +public: + explicit ElidedLabel(QWidget *parent = 0); + explicit ElidedLabel(const QString &text, QWidget *parent = 0); + +signals: + void clicked(); + +protected: + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent* event) override; + void mouseReleaseEvent(QMouseEvent *event) override { + if (rect().contains(event->pos())) { + emit clicked(); + } + } + QString lastText_, elidedText_; +}; diff --git a/tools/cabana/utils/util.cc b/tools/cabana/utils/util.cc index f27df4bf1e..ca069b358d 100644 --- a/tools/cabana/utils/util.cc +++ b/tools/cabana/utils/util.cc @@ -10,11 +10,16 @@ #include #include +#include #include #include #include - -#include "selfdrive/ui/qt/util.h" +#include +#include +#include +#include +#include +#include "common/util.h" // SegmentTree @@ -276,3 +281,80 @@ QString signalToolTip(const cabana::Signal *sig) { )").arg(sig->name).arg(sig->start_bit).arg(sig->size).arg(sig->msb).arg(sig->lsb) .arg(sig->is_little_endian ? "Y" : "N").arg(sig->is_signed ? "Y" : "N"); } + +void setSurfaceFormat() { + QSurfaceFormat fmt; +#ifdef __APPLE__ + fmt.setVersion(3, 2); + fmt.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); + fmt.setRenderableType(QSurfaceFormat::OpenGL); +#else + fmt.setRenderableType(QSurfaceFormat::OpenGLES); +#endif + fmt.setSamples(16); + fmt.setStencilBufferSize(1); + QSurfaceFormat::setDefaultFormat(fmt); +} + +void sigTermHandler(int s) { + std::signal(s, SIG_DFL); + qApp->quit(); +} + +void initApp(int argc, char *argv[], bool disable_hidpi) { + // setup signal handlers to exit gracefully + std::signal(SIGINT, sigTermHandler); + std::signal(SIGTERM, sigTermHandler); + + QString app_dir; +#ifdef __APPLE__ + // Get the devicePixelRatio, and scale accordingly to maintain 1:1 rendering + QApplication tmp(argc, argv); + app_dir = QCoreApplication::applicationDirPath(); + if (disable_hidpi) { + qputenv("QT_SCALE_FACTOR", QString::number(1.0 / tmp.devicePixelRatio()).toLocal8Bit()); + } +#else + app_dir = QFileInfo(util::readlink("/proc/self/exe").c_str()).path(); +#endif + + qputenv("QT_DBL_CLICK_DIST", QByteArray::number(150)); + // ensure the current dir matches the exectuable's directory + QDir::setCurrent(app_dir); + + setSurfaceFormat(); +} + +static QHash load_bootstrap_icons() { + QHash icons; + + QFile f(":/bootstrap-icons.svg"); + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QDomDocument xml; + xml.setContent(&f); + QDomNode n = xml.documentElement().firstChild(); + while (!n.isNull()) { + QDomElement e = n.toElement(); + if (!e.isNull() && e.hasAttribute("id")) { + QString svg_str; + QTextStream stream(&svg_str); + n.save(stream, 0); + svg_str.replace("", ""); + icons[e.attribute("id")] = svg_str.toUtf8(); + } + n = n.nextSibling(); + } + } + return icons; +} + +QPixmap bootstrapPixmap(const QString &id) { + static QHash icons = load_bootstrap_icons(); + + QPixmap pixmap; + if (auto it = icons.find(id); it != icons.end()) { + pixmap.loadFromData(it.value(), "svg"); + } + return pixmap; +} diff --git a/tools/cabana/utils/util.h b/tools/cabana/utils/util.h index 5c1cbe2d69..f839ffe7fe 100644 --- a/tools/cabana/utils/util.h +++ b/tools/cabana/utils/util.h @@ -166,3 +166,5 @@ private: int num_decimals(double num); QString signalToolTip(const cabana::Signal *sig); inline QString toHexString(int value) { return QString("0x%1").arg(QString::number(value, 16).toUpper(), 2, '0'); } +void initApp(int argc, char *argv[], bool disable_hidpi = true); +QPixmap bootstrapPixmap(const QString &id); From 1e73025f86d7f6cbf745a1b8507dde4bf47ec0d5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 22 Oct 2025 22:18:07 -0700 Subject: [PATCH 240/341] Remove Qt (#36427) * rm qt from ui scons * rm qt translation litter * rm ccs * more * fix cabana * more * more * more --- .github/workflows/selfdrive_tests.yaml | 38 - .github/workflows/ui_preview.yaml | 174 --- .gitignore | 1 - SConstruct | 52 +- pyproject.toml | 2 +- selfdrive/ui/SConscript | 128 +- selfdrive/ui/main.cc | 30 - selfdrive/ui/qt/api.cc | 142 --- selfdrive/ui/qt/api.h | 47 - selfdrive/ui/qt/body.cc | 161 --- selfdrive/ui/qt/body.h | 38 - selfdrive/ui/qt/home.cc | 243 ---- selfdrive/ui/qt/home.h | 73 -- selfdrive/ui/qt/network/networking.cc | 413 ------ selfdrive/ui/qt/network/networking.h | 105 -- selfdrive/ui/qt/network/networkmanager.h | 48 - selfdrive/ui/qt/network/wifi_manager.cc | 539 -------- selfdrive/ui/qt/network/wifi_manager.h | 111 -- selfdrive/ui/qt/offroad/developer_panel.cc | 95 -- selfdrive/ui/qt/offroad/developer_panel.h | 22 - selfdrive/ui/qt/offroad/driverview.cc | 82 -- selfdrive/ui/qt/offroad/driverview.h | 23 - selfdrive/ui/qt/offroad/experimental_mode.cc | 76 -- selfdrive/ui/qt/offroad/experimental_mode.h | 31 - selfdrive/ui/qt/offroad/firehose.cc | 112 -- selfdrive/ui/qt/offroad/firehose.h | 27 - selfdrive/ui/qt/offroad/onboarding.cc | 211 ---- selfdrive/ui/qt/offroad/onboarding.h | 107 -- selfdrive/ui/qt/offroad/settings.cc | 535 -------- selfdrive/ui/qt/offroad/settings.h | 106 -- selfdrive/ui/qt/offroad/software_settings.cc | 156 --- selfdrive/ui/qt/onroad/alerts.cc | 112 -- selfdrive/ui/qt/onroad/alerts.h | 39 - selfdrive/ui/qt/onroad/annotated_camera.cc | 157 --- selfdrive/ui/qt/onroad/annotated_camera.h | 37 - selfdrive/ui/qt/onroad/buttons.cc | 49 - selfdrive/ui/qt/onroad/buttons.h | 28 - selfdrive/ui/qt/onroad/driver_monitoring.cc | 107 -- selfdrive/ui/qt/onroad/driver_monitoring.h | 24 - selfdrive/ui/qt/onroad/hud.cc | 112 -- selfdrive/ui/qt/onroad/hud.h | 26 - selfdrive/ui/qt/onroad/model.cc | 250 ---- selfdrive/ui/qt/onroad/model.h | 39 - selfdrive/ui/qt/onroad/onroad_home.cc | 65 - selfdrive/ui/qt/onroad/onroad_home.h | 22 - selfdrive/ui/qt/prime_state.cc | 48 - selfdrive/ui/qt/prime_state.h | 33 - selfdrive/ui/qt/qt_window.cc | 36 - selfdrive/ui/qt/qt_window.h | 20 - selfdrive/ui/qt/request_repeater.cc | 27 - selfdrive/ui/qt/request_repeater.h | 15 - selfdrive/ui/qt/sidebar.cc | 165 --- selfdrive/ui/qt/sidebar.h | 66 - selfdrive/ui/qt/util.cc | 228 ---- selfdrive/ui/qt/util.h | 54 - selfdrive/ui/qt/widgets/cameraview.cc | 365 ------ selfdrive/ui/qt/widgets/cameraview.h | 89 -- selfdrive/ui/qt/widgets/controls.cc | 141 --- selfdrive/ui/qt/widgets/controls.h | 319 ----- selfdrive/ui/qt/widgets/input.cc | 336 ----- selfdrive/ui/qt/widgets/input.h | 71 -- selfdrive/ui/qt/widgets/keyboard.cc | 182 --- selfdrive/ui/qt/widgets/keyboard.h | 42 - selfdrive/ui/qt/widgets/offroad_alerts.cc | 138 -- selfdrive/ui/qt/widgets/offroad_alerts.h | 44 - selfdrive/ui/qt/widgets/prime.cc | 265 ---- selfdrive/ui/qt/widgets/prime.h | 70 - selfdrive/ui/qt/widgets/scrollview.cc | 49 - selfdrive/ui/qt/widgets/scrollview.h | 12 - selfdrive/ui/qt/widgets/ssh_keys.cc | 64 - selfdrive/ui/qt/widgets/ssh_keys.h | 32 - selfdrive/ui/qt/widgets/toggle.cc | 83 -- selfdrive/ui/qt/widgets/toggle.h | 44 - selfdrive/ui/qt/widgets/wifi.cc | 45 - selfdrive/ui/qt/widgets/wifi.h | 14 - selfdrive/ui/qt/window.cc | 98 -- selfdrive/ui/qt/window.h | 25 - .../ui/tests/create_test_translations.sh | 18 - selfdrive/ui/tests/test_runner.cc | 26 - selfdrive/ui/tests/test_translations.cc | 48 - selfdrive/ui/tests/test_translations.py | 4 +- selfdrive/ui/tests/test_ui/run.py | 310 ----- selfdrive/ui/translations/README.md | 71 -- selfdrive/ui/translations/ar.ts | 1090 ---------------- selfdrive/ui/translations/create_badges.py | 62 - selfdrive/ui/translations/de.ts | 1072 ---------------- selfdrive/ui/translations/en.ts | 48 - selfdrive/ui/translations/es.ts | 1074 ---------------- selfdrive/ui/translations/fr.ts | 1068 ---------------- selfdrive/ui/translations/ja.ts | 1069 ---------------- selfdrive/ui/translations/ko.ts | 1069 ---------------- selfdrive/ui/translations/nl.ts | 1120 ---------------- selfdrive/ui/translations/pl.ts | 1124 ----------------- selfdrive/ui/translations/pt-BR.ts | 1074 ---------------- selfdrive/ui/translations/th.ts | 1065 ---------------- selfdrive/ui/translations/tr.ts | 1062 ---------------- selfdrive/ui/translations/zh-CHS.ts | 1069 ---------------- selfdrive/ui/translations/zh-CHT.ts | 1069 ---------------- selfdrive/ui/ui.cc | 199 --- selfdrive/ui/ui.h | 132 -- selfdrive/ui/update_translations.py | 66 +- selfdrive/ui/update_translations_raylib.py | 38 - system/manager/process_config.py | 1 - third_party/qt5/larch64/bin/lrelease | 3 - third_party/qt5/larch64/bin/lupdate | 3 - tools/cabana/utils/api.cc | 5 +- 106 files changed, 70 insertions(+), 23204 deletions(-) delete mode 100644 .github/workflows/ui_preview.yaml delete mode 100644 selfdrive/ui/main.cc delete mode 100644 selfdrive/ui/qt/api.cc delete mode 100644 selfdrive/ui/qt/api.h delete mode 100644 selfdrive/ui/qt/body.cc delete mode 100644 selfdrive/ui/qt/body.h delete mode 100644 selfdrive/ui/qt/home.cc delete mode 100644 selfdrive/ui/qt/home.h delete mode 100644 selfdrive/ui/qt/network/networking.cc delete mode 100644 selfdrive/ui/qt/network/networking.h delete mode 100644 selfdrive/ui/qt/network/networkmanager.h delete mode 100644 selfdrive/ui/qt/network/wifi_manager.cc delete mode 100644 selfdrive/ui/qt/network/wifi_manager.h delete mode 100644 selfdrive/ui/qt/offroad/developer_panel.cc delete mode 100644 selfdrive/ui/qt/offroad/developer_panel.h delete mode 100644 selfdrive/ui/qt/offroad/driverview.cc delete mode 100644 selfdrive/ui/qt/offroad/driverview.h delete mode 100644 selfdrive/ui/qt/offroad/experimental_mode.cc delete mode 100644 selfdrive/ui/qt/offroad/experimental_mode.h delete mode 100644 selfdrive/ui/qt/offroad/firehose.cc delete mode 100644 selfdrive/ui/qt/offroad/firehose.h delete mode 100644 selfdrive/ui/qt/offroad/onboarding.cc delete mode 100644 selfdrive/ui/qt/offroad/onboarding.h delete mode 100644 selfdrive/ui/qt/offroad/settings.cc delete mode 100644 selfdrive/ui/qt/offroad/settings.h delete mode 100644 selfdrive/ui/qt/offroad/software_settings.cc delete mode 100644 selfdrive/ui/qt/onroad/alerts.cc delete mode 100644 selfdrive/ui/qt/onroad/alerts.h delete mode 100644 selfdrive/ui/qt/onroad/annotated_camera.cc delete mode 100644 selfdrive/ui/qt/onroad/annotated_camera.h delete mode 100644 selfdrive/ui/qt/onroad/buttons.cc delete mode 100644 selfdrive/ui/qt/onroad/buttons.h delete mode 100644 selfdrive/ui/qt/onroad/driver_monitoring.cc delete mode 100644 selfdrive/ui/qt/onroad/driver_monitoring.h delete mode 100644 selfdrive/ui/qt/onroad/hud.cc delete mode 100644 selfdrive/ui/qt/onroad/hud.h delete mode 100644 selfdrive/ui/qt/onroad/model.cc delete mode 100644 selfdrive/ui/qt/onroad/model.h delete mode 100644 selfdrive/ui/qt/onroad/onroad_home.cc delete mode 100644 selfdrive/ui/qt/onroad/onroad_home.h delete mode 100644 selfdrive/ui/qt/prime_state.cc delete mode 100644 selfdrive/ui/qt/prime_state.h delete mode 100644 selfdrive/ui/qt/qt_window.cc delete mode 100644 selfdrive/ui/qt/qt_window.h delete mode 100644 selfdrive/ui/qt/request_repeater.cc delete mode 100644 selfdrive/ui/qt/request_repeater.h delete mode 100644 selfdrive/ui/qt/sidebar.cc delete mode 100644 selfdrive/ui/qt/sidebar.h delete mode 100644 selfdrive/ui/qt/util.cc delete mode 100644 selfdrive/ui/qt/util.h delete mode 100644 selfdrive/ui/qt/widgets/cameraview.cc delete mode 100644 selfdrive/ui/qt/widgets/cameraview.h delete mode 100644 selfdrive/ui/qt/widgets/controls.cc delete mode 100644 selfdrive/ui/qt/widgets/controls.h delete mode 100644 selfdrive/ui/qt/widgets/input.cc delete mode 100644 selfdrive/ui/qt/widgets/input.h delete mode 100644 selfdrive/ui/qt/widgets/keyboard.cc delete mode 100644 selfdrive/ui/qt/widgets/keyboard.h delete mode 100644 selfdrive/ui/qt/widgets/offroad_alerts.cc delete mode 100644 selfdrive/ui/qt/widgets/offroad_alerts.h delete mode 100644 selfdrive/ui/qt/widgets/prime.cc delete mode 100644 selfdrive/ui/qt/widgets/prime.h delete mode 100644 selfdrive/ui/qt/widgets/scrollview.cc delete mode 100644 selfdrive/ui/qt/widgets/scrollview.h delete mode 100644 selfdrive/ui/qt/widgets/ssh_keys.cc delete mode 100644 selfdrive/ui/qt/widgets/ssh_keys.h delete mode 100644 selfdrive/ui/qt/widgets/toggle.cc delete mode 100644 selfdrive/ui/qt/widgets/toggle.h delete mode 100644 selfdrive/ui/qt/widgets/wifi.cc delete mode 100644 selfdrive/ui/qt/widgets/wifi.h delete mode 100644 selfdrive/ui/qt/window.cc delete mode 100644 selfdrive/ui/qt/window.h delete mode 100755 selfdrive/ui/tests/create_test_translations.sh delete mode 100644 selfdrive/ui/tests/test_runner.cc delete mode 100644 selfdrive/ui/tests/test_translations.cc delete mode 100755 selfdrive/ui/tests/test_ui/run.py delete mode 100644 selfdrive/ui/translations/README.md delete mode 100644 selfdrive/ui/translations/ar.ts delete mode 100755 selfdrive/ui/translations/create_badges.py delete mode 100644 selfdrive/ui/translations/de.ts delete mode 100644 selfdrive/ui/translations/en.ts delete mode 100644 selfdrive/ui/translations/es.ts delete mode 100644 selfdrive/ui/translations/fr.ts delete mode 100644 selfdrive/ui/translations/ja.ts delete mode 100644 selfdrive/ui/translations/ko.ts delete mode 100644 selfdrive/ui/translations/nl.ts delete mode 100644 selfdrive/ui/translations/pl.ts delete mode 100644 selfdrive/ui/translations/pt-BR.ts delete mode 100644 selfdrive/ui/translations/th.ts delete mode 100644 selfdrive/ui/translations/tr.ts delete mode 100644 selfdrive/ui/translations/zh-CHS.ts delete mode 100644 selfdrive/ui/translations/zh-CHT.ts delete mode 100644 selfdrive/ui/ui.cc delete mode 100644 selfdrive/ui/ui.h delete mode 100755 selfdrive/ui/update_translations_raylib.py delete mode 100755 third_party/qt5/larch64/bin/lrelease delete mode 100755 third_party/qt5/larch64/bin/lupdate diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 35ced1e38b..3f60fab73d 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -164,8 +164,6 @@ jobs: # Pre-compile Python bytecode so each pytest worker doesn't need to $PYTEST --collect-only -m 'not slow' -qq && \ MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \ - ./selfdrive/ui/tests/create_test_translations.sh && \ - QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ chmod -R 777 /tmp/comma_download_cache" process_replay: @@ -242,42 +240,6 @@ jobs: source selfdrive/test/setup_vsound.sh && \ CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py" - create_ui_report: - # This job name needs to be the same as UI_JOB_NAME in ui_preview.yaml - name: Create UI Report - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} - if: false # FIXME: FrameReader is broken on CI runners - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - uses: ./.github/workflows/setup-with-retry - - name: caching frames - id: frames-cache - uses: actions/cache@v4 - with: - path: .ci_cache/comma_download_cache - key: ui_screenshots_test_${{ hashFiles('selfdrive/ui/tests/test_ui/run.py') }} - - name: Build openpilot - run: ${{ env.RUN }} "scons -j$(nproc)" - - name: Create Test Report - timeout-minutes: ${{ ((steps.frames-cache.outputs.cache-hit == 'true') && 1 || 3) }} - run: > - ${{ env.RUN }} "PYTHONWARNINGS=ignore && - source selfdrive/test/setup_xvfb.sh && - CACHE_ROOT=/tmp/comma_download_cache python3 selfdrive/ui/tests/test_ui/run.py && - chmod -R 777 /tmp/comma_download_cache" - - name: Upload Test Report - uses: actions/upload-artifact@v4 - with: - name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} - path: selfdrive/ui/tests/test_ui/report_1/screenshots - create_raylib_ui_report: name: Create raylib UI Report runs-on: ${{ diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml deleted file mode 100644 index 9ec7a59223..0000000000 --- a/.github/workflows/ui_preview.yaml +++ /dev/null @@ -1,174 +0,0 @@ -name: "ui preview" -on: - push: - branches: - - master - pull_request_target: - types: [assigned, opened, synchronize, reopened, edited] - branches: - - 'master' - paths: - - 'selfdrive/ui/**' - workflow_dispatch: - -env: - UI_JOB_NAME: "Create UI Report" - REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} - SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }} - BRANCH_NAME: "openpilot/pr-${{ github.event.number }}" - -jobs: - preview: - #if: github.repository == 'commaai/openpilot' - if: false # FIXME: FrameReader is broken on CI runners - name: preview - runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - pull-requests: write - actions: read - steps: - - name: Waiting for ui generation to start - run: sleep 30 - - - name: Waiting for ui generation to end - uses: lewagon/wait-on-check-action@v1.3.4 - with: - ref: ${{ env.SHA }} - check-name: ${{ env.UI_JOB_NAME }} - repo-token: ${{ secrets.GITHUB_TOKEN }} - allowed-conclusions: success - wait-interval: 20 - - - name: Getting workflow run ID - id: get_run_id - run: | - echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?[0-9]+)") | .number')" >> $GITHUB_OUTPUT - - - name: Getting proposed ui - id: download-artifact - uses: dawidd6/action-download-artifact@v6 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - run_id: ${{ steps.get_run_id.outputs.run_id }} - search_artifacts: true - name: report-1-${{ env.REPORT_NAME }} - path: ${{ github.workspace }}/pr_ui - - - name: Getting master ui - uses: actions/checkout@v4 - with: - repository: commaai/ci-artifacts - ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} - path: ${{ github.workspace }}/master_ui - ref: openpilot_master_ui - - - name: Saving new master ui - if: github.ref == 'refs/heads/master' && github.event_name == 'push' - working-directory: ${{ github.workspace }}/master_ui - run: | - git checkout --orphan=new_master_ui - git rm -rf * - git branch -D openpilot_master_ui - git branch -m openpilot_master_ui - git config user.name "GitHub Actions Bot" - git config user.email "<>" - mv ${{ github.workspace }}/pr_ui/*.png . - git add . - git commit -m "screenshots for commit ${{ env.SHA }}" - git push origin openpilot_master_ui --force - - - name: Finding diff - if: github.event_name == 'pull_request_target' - id: find_diff - run: >- - sudo apt-get update && sudo apt-get install -y imagemagick - - scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') - A=($scenes) - - DIFF="" - TABLE="
All Screenshots" - TABLE="${TABLE}" - - 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{cyan}\\text{NEW}}\$\$" - DIFF="${DIFF}
" - - DIFF="${DIFF}" - DIFF="${DIFF} " - 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 - - mv ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png - - DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}
master proposed
diff composite diff
" - DIFF="${DIFF}
" - else - rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png - fi - - INDEX=$(($i % 2)) - if [[ $INDEX -eq 0 ]]; then - TABLE="${TABLE}" - fi - TABLE="${TABLE} " - if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then - TABLE="${TABLE}" - fi - done - - TABLE="${TABLE}" - - echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" - - - name: Saving proposed ui - if: github.event_name == 'pull_request_target' - working-directory: ${{ github.workspace }}/master_ui - run: | - git config user.name "GitHub Actions Bot" - git config user.email "<>" - git checkout --orphan=${{ env.BRANCH_NAME }} - git rm -rf * - mv ${{ github.workspace }}/pr_ui/* . - git add . - git commit -m "screenshots for PR #${{ github.event.number }}" - git push origin ${{ env.BRANCH_NAME }} --force - - - name: Comment Screenshots on PR - if: github.event_name == 'pull_request_target' - uses: thollander/actions-comment-pull-request@v2 - with: - message: | - - ## UI Preview - ${{ steps.find_diff.outputs.DIFF }} - comment_tag: run_id_screenshots - pr_number: ${{ github.event.number }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index d3306a11ba..b700df0c88 100644 --- a/.gitignore +++ b/.gitignore @@ -36,7 +36,6 @@ a.out *.class *.pyxbldc *.vcd -*.qm *.mo *_pyx.cpp config.json diff --git a/SConstruct b/SConstruct index 80273db106..648afdae57 100644 --- a/SConstruct +++ b/SConstruct @@ -165,57 +165,7 @@ else: np_version = SCons.Script.Value(np.__version__) Export('envCython', 'np_version') -# ********** Qt build environment ********** -qt_env = env.Clone() -qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "DBus", "Xml"] - -qt_libs = [] -if arch == "Darwin": - qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5" - qt_dirs = [ - os.path.join(qt_env['QTDIR'], "include"), - ] - qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules] - qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")] - qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"] - qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin")) -else: - qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip() - qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip() - - qt_env['QTDIR'] = qt_install_prefix - qt_dirs = [ - f"{qt_install_headers}", - ] - - qt_gui_path = os.path.join(qt_install_headers, "QtGui") - qt_gui_dirs = [d for d in os.listdir(qt_gui_path) if os.path.isdir(os.path.join(qt_gui_path, d))] - qt_dirs += [f"{qt_install_headers}/QtGui/{qt_gui_dirs[0]}/QtGui", ] if qt_gui_dirs else [] - qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules] - - qt_libs = [f"Qt5{m}" for m in qt_modules] - if arch == "larch64": - qt_libs += ["GLESv2", "wayland-client"] - qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath) - elif arch != "Darwin": - qt_libs += ["GL"] -qt_env['QT3DIR'] = qt_env['QTDIR'] -qt_env.Tool('qt3') - -qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"] -qt_flags = [ - "-D_REENTRANT", - "-DQT_NO_DEBUG", - "-DQT_WIDGETS_LIB", - "-DQT_GUI_LIB", - "-DQT_CORE_LIB", - "-DQT_MESSAGELOGCONTEXT", -] -qt_env['CXXFLAGS'] += qt_flags -qt_env['LIBPATH'] += ['#selfdrive/ui', ] -qt_env['LIBS'] = qt_libs - -Export('env', 'qt_env', 'arch') +Export('env', 'arch') # Setup cache dir cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache' diff --git a/pyproject.toml b/pyproject.toml index 6cc6298eb3..8cb877c351 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -177,7 +177,7 @@ quiet-level = 3 # if you've got a short variable name that's getting flagged, add it here ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite" builtin = "clear,rare,informal,code,names,en-GB_to_en-US" -skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*" +skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*" [tool.mypy] python_version = "3.11" diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index a7670b2482..a9b40f0d24 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,6 +1,9 @@ import os import json -Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') +Import('env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') + +raylib_env = env.Clone() + # compile gettext .po -> .mo translations with open(File("translations/languages.json").abspath) as f: @@ -11,101 +14,40 @@ po_sources = [src for src in po_sources if os.path.exists(File(src).abspath)] mo_targets = [src.replace(".po", ".mo") for src in po_sources] mo_build = [] for src, tgt in zip(po_sources, mo_targets): - mo_build.append(qt_env.Command(tgt, src, "msgfmt -o $TARGET $SOURCE")) -mo_alias = qt_env.Alias('mo', mo_build) -qt_env.AlwaysBuild(mo_alias) + mo_build.append(raylib_env.Command(tgt, src, "msgfmt -o $TARGET $SOURCE")) +mo_alias = raylib_env.Alias('mo', mo_build) +raylib_env.AlwaysBuild(mo_alias) if GetOption('extras'): - base_libs = [common, messaging, visionipc, transformations, - 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] + # build installers + if arch != "Darwin": + raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] + raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') - if arch == 'larch64': - base_libs.append('EGL') + raylib_libs = common + ["raylib"] + if arch == "larch64": + raylib_libs += ["GLESv2", "EGL", "gbm", "drm"] + else: + raylib_libs += ["GL"] - if arch == "Darwin": - del base_libs[base_libs.index('OpenCL')] - qt_env['FRAMEWORKS'] += ['OpenCL'] + release = "release3" + installers = [ + ("openpilot", release), + ("openpilot_test", f"{release}-staging"), + ("openpilot_nightly", "nightly"), + ("openpilot_internal", "nightly-dev"), + ] - # FIXME: remove this once we're on 5.15 (24.04) - qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] + cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh", + "ld -r -b binary -o $TARGET $SOURCE") + inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf", + "ld -r -b binary -o $TARGET $SOURCE") + for name, branch in installers: + d = {'BRANCH': f"'\"{branch}\"'"} + if "internal" in name: + d['INTERNAL'] = "1" - qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs) - widgets_src = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc", - "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", - "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", - "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", - "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] - - widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs) - Export('widgets') - qt_libs = [widgets, qt_util] + base_libs - - qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc", - "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", - "qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", - "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc", - "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc", - "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] - - # build translation files - translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.values()] - translation_targets = [src.replace(".ts", ".qm") for src in translation_sources] - lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease' - - lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES") - qt_env.NoClean(translation_sources) - qt_env.Precious(translation_sources) - - # create qrc file for compiled translations to include with assets - translations_assets_src = "#selfdrive/assets/translations_assets.qrc" - with open(File(translations_assets_src).abspath, 'w') as f: - f.write('\n\n') - f.write('\n'.join([f'../ui/translations/{l}.qm' for l in languages.values()])) - f.write('\n\n') - - # build assets - assets = "#selfdrive/assets/assets.cc" - assets_src = "#selfdrive/assets/assets.qrc" - qt_env.Command(assets, [assets_src, translations_assets_src], f"rcc $SOURCES -o $TARGET") - qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, translations_assets_src, "#selfdrive/assets/assets.o"]) + [lrelease]) - asset_obj = qt_env.Object("assets", assets) - - # build main UI - qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs) - if GetOption('extras'): - qt_src.remove("main.cc") # replaced by test_runner - qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs) - - # build installers - if arch != "Darwin": - raylib_env = env.Clone() - raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] - raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') - - raylib_libs = common + ["raylib"] - if arch == "larch64": - raylib_libs += ["GLESv2", "EGL", "gbm", "drm"] - else: - raylib_libs += ["GL"] - - release = "release3" - installers = [ - ("openpilot", release), - ("openpilot_test", f"{release}-staging"), - ("openpilot_nightly", "nightly"), - ("openpilot_internal", "nightly-dev"), - ] - - cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh", - "ld -r -b binary -o $TARGET $SOURCE") - inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf", - "ld -r -b binary -o $TARGET $SOURCE") - for name, branch in installers: - d = {'BRANCH': f"'\"{branch}\"'"} - if "internal" in name: - d['INTERNAL'] = "1" - - obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) - f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) - # keep installers small - assert f[0].get_size() < 1900*1e3, f[0].get_size() + obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) + f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) + # keep installers small + assert f[0].get_size() < 1900*1e3, f[0].get_size() diff --git a/selfdrive/ui/main.cc b/selfdrive/ui/main.cc deleted file mode 100644 index 4903a3db3d..0000000000 --- a/selfdrive/ui/main.cc +++ /dev/null @@ -1,30 +0,0 @@ -#include - -#include -#include - -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/window.h" - -int main(int argc, char *argv[]) { - setpriority(PRIO_PROCESS, 0, -20); - - qInstallMessageHandler(swagLogMessageHandler); - initApp(argc, argv); - - QTranslator translator; - QString translation_file = QString::fromStdString(Params().get("LanguageSetting")); - if (!translator.load(QString(":/%1").arg(translation_file)) && translation_file.length()) { - qCritical() << "Failed to load translation file:" << translation_file; - } - - QApplication a(argc, argv); - a.installTranslator(&translator); - - MainWindow w; - setMainWindow(&w); - a.installEventFilter(&w); - return a.exec(); -} diff --git a/selfdrive/ui/qt/api.cc b/selfdrive/ui/qt/api.cc deleted file mode 100644 index 6889b40e51..0000000000 --- a/selfdrive/ui/qt/api.cc +++ /dev/null @@ -1,142 +0,0 @@ -#include "selfdrive/ui/qt/api.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "common/util.h" -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/util.h" - -namespace CommaApi { - -RSA *get_rsa_private_key() { - static std::unique_ptr rsa_private(nullptr, RSA_free); - if (!rsa_private) { - FILE *fp = fopen(Path::rsa_file().c_str(), "rb"); - if (!fp) { - qDebug() << "No RSA private key found, please run manager.py or registration.py"; - return nullptr; - } - rsa_private.reset(PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL)); - fclose(fp); - } - return rsa_private.get(); -} - -QByteArray rsa_sign(const QByteArray &data) { - RSA *rsa_private = get_rsa_private_key(); - if (!rsa_private) return {}; - - QByteArray sig(RSA_size(rsa_private), Qt::Uninitialized); - unsigned int sig_len; - int ret = RSA_sign(NID_sha256, (unsigned char*)data.data(), data.size(), (unsigned char*)sig.data(), &sig_len, rsa_private); - assert(ret == 1); - assert(sig.size() == sig_len); - return sig; -} - -QString create_jwt(const QJsonObject &payloads, int expiry) { - QJsonObject header = {{"alg", "RS256"}}; - - auto t = QDateTime::currentSecsSinceEpoch(); - QJsonObject payload = {{"identity", getDongleId().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 + "." + rsa_sign(hash).toBase64(b64_opts); -} - -} // namespace CommaApi - -HttpRequest::HttpRequest(QObject *parent, bool create_jwt, int timeout) : create_jwt(create_jwt), QObject(parent) { - networkTimer = new QTimer(this); - networkTimer->setSingleShot(true); - networkTimer->setInterval(timeout); - connect(networkTimer, &QTimer::timeout, this, &HttpRequest::requestTimeout); -} - -bool HttpRequest::active() const { - return reply != nullptr; -} - -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; - } - QString token; - if (create_jwt) { - token = CommaApi::create_jwt(); - } 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()); - - if (!token.isEmpty()) { - request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8()); - } - - if (method == HttpRequest::Method::GET) { - reply = nam()->get(request); - } else if (method == HttpRequest::Method::DELETE) { - reply = nam()->deleteResource(request); - } - - networkTimer->start(); - connect(reply, &QNetworkReply::finished, this, &HttpRequest::requestFinished); -} - -void HttpRequest::requestTimeout() { - reply->abort(); -} - -void HttpRequest::requestFinished() { - networkTimer->stop(); - - if (reply->error() == QNetworkReply::NoError) { - emit requestDone(reply->readAll(), true, reply->error()); - } else { - QString error; - if (reply->error() == QNetworkReply::OperationCanceledError) { - nam()->clearAccessCache(); - nam()->clearConnectionCache(); - error = "Request timed out"; - } else { - error = reply->errorString(); - } - emit requestDone(error, false, reply->error()); - } - - reply->deleteLater(); - reply = nullptr; -} - -QNetworkAccessManager *HttpRequest::nam() { - static QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager(qApp); - return networkAccessManager; -} diff --git a/selfdrive/ui/qt/api.h b/selfdrive/ui/qt/api.h deleted file mode 100644 index ad64d7e722..0000000000 --- a/selfdrive/ui/qt/api.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "common/util.h" - -namespace CommaApi { - -const QString BASE_URL = util::getenv("API_HOST", "https://api.commadotai.com").c_str(); -QByteArray rsa_sign(const QByteArray &data); -QString create_jwt(const QJsonObject &payloads = {}, int expiry = 3600); - -} // namespace CommaApi - -/** - * Makes a request to the request endpoint. - */ - -class HttpRequest : public QObject { - Q_OBJECT - -public: - enum class Method {GET, DELETE}; - - explicit HttpRequest(QObject* parent, bool create_jwt = true, int timeout = 20000); - void sendRequest(const QString &requestURL, const Method method = Method::GET); - bool active() const; - bool timeout() const; - -signals: - void requestDone(const QString &response, bool success, QNetworkReply::NetworkError error); - -protected: - QNetworkReply *reply = nullptr; - -private: - static QNetworkAccessManager *nam(); - QTimer *networkTimer = nullptr; - bool create_jwt; - -private slots: - void requestTimeout(); - void requestFinished(); -}; diff --git a/selfdrive/ui/qt/body.cc b/selfdrive/ui/qt/body.cc deleted file mode 100644 index e01adbe063..0000000000 --- a/selfdrive/ui/qt/body.cc +++ /dev/null @@ -1,161 +0,0 @@ -#include "selfdrive/ui/qt/body.h" - -#include -#include - -#include -#include - -#include "common/params.h" -#include "common/timing.h" - -RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) { - setCheckable(true); - setChecked(false); - setFixedSize(148, 148); - - QObject::connect(this, &QPushButton::toggled, [=]() { - setEnabled(false); - }); -} - -void RecordButton::paintEvent(QPaintEvent *event) { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - - QPoint center(width() / 2, height() / 2); - - QColor bg(isChecked() ? "#FFFFFF" : "#737373"); - QColor accent(isChecked() ? "#FF0000" : "#FFFFFF"); - if (!isEnabled()) { - bg = QColor("#404040"); - accent = QColor("#FFFFFF"); - } - - if (isDown()) { - accent.setAlphaF(0.7); - } - - p.setPen(Qt::NoPen); - p.setBrush(bg); - p.drawEllipse(center, 74, 74); - - p.setPen(QPen(accent, 6)); - p.setBrush(Qt::NoBrush); - p.drawEllipse(center, 42, 42); - - p.setPen(Qt::NoPen); - p.setBrush(accent); - p.drawEllipse(center, 22, 22); -} - - -BodyWindow::BodyWindow(QWidget *parent) : fuel_filter(1.0, 5., 1. / UI_FREQ), QWidget(parent) { - QStackedLayout *layout = new QStackedLayout(this); - layout->setStackingMode(QStackedLayout::StackAll); - - QWidget *w = new QWidget; - QVBoxLayout *vlayout = new QVBoxLayout(w); - vlayout->setMargin(45); - layout->addWidget(w); - - // face - face = new QLabel(); - face->setAlignment(Qt::AlignCenter); - layout->addWidget(face); - awake = new QMovie("../assets/body/awake.gif", {}, this); - awake->setCacheMode(QMovie::CacheAll); - sleep = new QMovie("../assets/body/sleep.gif", {}, this); - sleep->setCacheMode(QMovie::CacheAll); - - // record button - btn = new RecordButton(this); - vlayout->addWidget(btn, 0, Qt::AlignBottom | Qt::AlignRight); - QObject::connect(btn, &QPushButton::clicked, [=](bool checked) { - btn->setEnabled(false); - Params().putBool("DisableLogging", !checked); - last_button = nanos_since_boot(); - }); - w->raise(); - - QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState); -} - -void BodyWindow::paintEvent(QPaintEvent *event) { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - - p.fillRect(rect(), QColor(0, 0, 0)); - - // battery outline + detail - p.translate(width() - 136, 16); - const QColor gray = QColor("#737373"); - p.setBrush(Qt::NoBrush); - p.setPen(QPen(gray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); - p.drawRoundedRect(2, 2, 78, 36, 8, 8); - - p.setPen(Qt::NoPen); - p.setBrush(gray); - p.drawRoundedRect(84, 12, 6, 16, 4, 4); - p.drawRect(84, 12, 3, 16); - - // battery level - double fuel = std::clamp(fuel_filter.x(), 0.2f, 1.0f); - const int m = 5; // manual margin since we can't do an inner border - p.setPen(Qt::NoPen); - p.setBrush(fuel > 0.25 ? QColor("#32D74B") : QColor("#FF453A")); - p.drawRoundedRect(2 + m, 2 + m, (78 - 2*m)*fuel, 36 - 2*m, 4, 4); - - // charging status - if (charging) { - p.setPen(Qt::NoPen); - p.setBrush(Qt::white); - const QPolygonF charger({ - QPointF(12.31, 0), - QPointF(12.31, 16.92), - QPointF(18.46, 16.92), - QPointF(6.15, 40), - QPointF(6.15, 23.08), - QPointF(0, 23.08), - }); - p.drawPolygon(charger.translated(98, 0)); - } -} - -void BodyWindow::offroadTransition(bool offroad) { - btn->setChecked(true); - btn->setEnabled(true); - fuel_filter.reset(1.0); -} - -void BodyWindow::updateState(const UIState &s) { - if (!isVisible()) { - return; - } - - const SubMaster &sm = *(s.sm); - auto cs = sm["carState"].getCarState(); - - charging = cs.getCharging(); - fuel_filter.update(cs.getFuelGauge()); - - // TODO: use carState.standstill when that's fixed - const bool standstill = std::abs(cs.getVEgo()) < 0.01; - QMovie *m = standstill ? sleep : awake; - if (m != face->movie()) { - face->setMovie(m); - face->movie()->start(); - } - - // update record button state - if (sm.updated("managerState") && (sm.rcv_time("managerState") - last_button)*1e-9 > 0.5) { - for (auto proc : sm["managerState"].getManagerState().getProcesses()) { - if (proc.getName() == "loggerd") { - btn->setEnabled(true); - btn->setChecked(proc.getRunning()); - } - } - } - - update(); -} diff --git a/selfdrive/ui/qt/body.h b/selfdrive/ui/qt/body.h deleted file mode 100644 index 567a54d49b..0000000000 --- a/selfdrive/ui/qt/body.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "common/util.h" -#include "selfdrive/ui/ui.h" - -class RecordButton : public QPushButton { - Q_OBJECT - -public: - RecordButton(QWidget* parent = 0); - -private: - void paintEvent(QPaintEvent*) override; -}; - -class BodyWindow : public QWidget { - Q_OBJECT - -public: - BodyWindow(QWidget* parent = 0); - -private: - bool charging = false; - uint64_t last_button = 0; - FirstOrderFilter fuel_filter; - QLabel *face; - QMovie *awake, *sleep; - RecordButton *btn; - void paintEvent(QPaintEvent*) override; - -private slots: - void updateState(const UIState &s); - void offroadTransition(bool onroad); -}; diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc deleted file mode 100644 index 8b1fdf474c..0000000000 --- a/selfdrive/ui/qt/home.cc +++ /dev/null @@ -1,243 +0,0 @@ -#include "selfdrive/ui/qt/home.h" - -#include -#include -#include -#include - -#include "selfdrive/ui/qt/offroad/experimental_mode.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/prime.h" - -// HomeWindow: the container for the offroad and onroad UIs - -HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) { - QHBoxLayout *main_layout = new QHBoxLayout(this); - main_layout->setMargin(0); - main_layout->setSpacing(0); - - sidebar = new Sidebar(this); - main_layout->addWidget(sidebar); - QObject::connect(sidebar, &Sidebar::openSettings, this, &HomeWindow::openSettings); - - slayout = new QStackedLayout(); - main_layout->addLayout(slayout); - - home = new OffroadHome(this); - QObject::connect(home, &OffroadHome::openSettings, this, &HomeWindow::openSettings); - slayout->addWidget(home); - - onroad = new OnroadWindow(this); - slayout->addWidget(onroad); - - body = new BodyWindow(this); - slayout->addWidget(body); - - driver_view = new DriverViewWindow(this); - connect(driver_view, &DriverViewWindow::done, [=] { - showDriverView(false); - }); - slayout->addWidget(driver_view); - setAttribute(Qt::WA_NoSystemBackground); - QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState); - QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition); - QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition); -} - -void HomeWindow::showSidebar(bool show) { - sidebar->setVisible(show); -} - -void HomeWindow::updateState(const UIState &s) { - const SubMaster &sm = *(s.sm); - - // switch to the generic robot UI - if (onroad->isVisible() && !body->isEnabled() && sm["carParams"].getCarParams().getNotCar()) { - body->setEnabled(true); - slayout->setCurrentWidget(body); - } -} - -void HomeWindow::offroadTransition(bool offroad) { - body->setEnabled(false); - sidebar->setVisible(offroad); - if (offroad) { - slayout->setCurrentWidget(home); - } else { - slayout->setCurrentWidget(onroad); - } -} - -void HomeWindow::showDriverView(bool show) { - if (show) { - emit closeSettings(); - slayout->setCurrentWidget(driver_view); - } else { - slayout->setCurrentWidget(home); - } - sidebar->setVisible(show == false); -} - -void HomeWindow::mousePressEvent(QMouseEvent* e) { - // Handle sidebar collapsing - if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) { - sidebar->setVisible(!sidebar->isVisible()); - } -} - -void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) { - HomeWindow::mousePressEvent(e); - const SubMaster &sm = *(uiState()->sm); - if (sm["carParams"].getCarParams().getNotCar()) { - if (onroad->isVisible()) { - slayout->setCurrentWidget(body); - } else if (body->isVisible()) { - slayout->setCurrentWidget(onroad); - } - showSidebar(false); - } -} - -// OffroadHome: the offroad home page - -OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { - QVBoxLayout* main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(40, 40, 40, 40); - - // top header - QHBoxLayout* header_layout = new QHBoxLayout(); - header_layout->setContentsMargins(0, 0, 0, 0); - header_layout->setSpacing(16); - - update_notif = new QPushButton(tr("UPDATE")); - update_notif->setVisible(false); - update_notif->setStyleSheet("background-color: #364DEF;"); - QObject::connect(update_notif, &QPushButton::clicked, [=]() { center_layout->setCurrentIndex(1); }); - header_layout->addWidget(update_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); - - alert_notif = new QPushButton(); - alert_notif->setVisible(false); - alert_notif->setStyleSheet("background-color: #E22C2C;"); - QObject::connect(alert_notif, &QPushButton::clicked, [=] { center_layout->setCurrentIndex(2); }); - header_layout->addWidget(alert_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); - - version = new ElidedLabel(); - header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight); - - main_layout->addLayout(header_layout); - - // main content - main_layout->addSpacing(25); - center_layout = new QStackedLayout(); - - QWidget *home_widget = new QWidget(this); - { - QHBoxLayout *home_layout = new QHBoxLayout(home_widget); - home_layout->setContentsMargins(0, 0, 0, 0); - home_layout->setSpacing(30); - - // left: PrimeAdWidget - QStackedWidget *left_widget = new QStackedWidget(this); - QVBoxLayout *left_prime_layout = new QVBoxLayout(); - left_prime_layout->setContentsMargins(0, 0, 0, 0); - QWidget *prime_user = new PrimeUserWidget(); - prime_user->setStyleSheet(R"( - border-radius: 10px; - background-color: #333333; - )"); - left_prime_layout->addWidget(prime_user); - left_prime_layout->addStretch(); - left_widget->addWidget(new LayoutWidget(left_prime_layout)); - left_widget->addWidget(new PrimeAdWidget); - left_widget->setStyleSheet("border-radius: 10px;"); - - connect(uiState()->prime_state, &PrimeState::changed, [left_widget]() { - left_widget->setCurrentIndex(uiState()->prime_state->isSubscribed() ? 0 : 1); - }); - - home_layout->addWidget(left_widget, 1); - - // right: ExperimentalModeButton, SetupWidget - QWidget* right_widget = new QWidget(this); - QVBoxLayout* right_column = new QVBoxLayout(right_widget); - right_column->setContentsMargins(0, 0, 0, 0); - right_widget->setFixedWidth(750); - right_column->setSpacing(30); - - ExperimentalModeButton *experimental_mode = new ExperimentalModeButton(this); - QObject::connect(experimental_mode, &ExperimentalModeButton::openSettings, this, &OffroadHome::openSettings); - right_column->addWidget(experimental_mode, 1); - - SetupWidget *setup_widget = new SetupWidget; - QObject::connect(setup_widget, &SetupWidget::openSettings, this, &OffroadHome::openSettings); - right_column->addWidget(setup_widget, 1); - - home_layout->addWidget(right_widget, 1); - } - center_layout->addWidget(home_widget); - - // add update & alerts widgets - update_widget = new UpdateAlert(); - QObject::connect(update_widget, &UpdateAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); - center_layout->addWidget(update_widget); - alerts_widget = new OffroadAlert(); - QObject::connect(alerts_widget, &OffroadAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); - center_layout->addWidget(alerts_widget); - - main_layout->addLayout(center_layout, 1); - - // set up refresh timer - timer = new QTimer(this); - timer->callOnTimeout(this, &OffroadHome::refresh); - - setStyleSheet(R"( - * { - color: white; - } - OffroadHome { - background-color: black; - } - OffroadHome > QPushButton { - padding: 15px 30px; - border-radius: 5px; - font-size: 40px; - font-weight: 500; - } - OffroadHome > QLabel { - font-size: 55px; - } - )"); -} - -void OffroadHome::showEvent(QShowEvent *event) { - refresh(); - timer->start(10 * 1000); -} - -void OffroadHome::hideEvent(QHideEvent *event) { - timer->stop(); -} - -void OffroadHome::refresh() { - version->setText(getBrand() + " " + QString::fromStdString(params.get("UpdaterCurrentDescription"))); - - bool updateAvailable = update_widget->refresh(); - int alerts = alerts_widget->refresh(); - - // pop-up new notification - int idx = center_layout->currentIndex(); - if (!updateAvailable && !alerts) { - idx = 0; - } else if (updateAvailable && (!update_notif->isVisible() || (!alerts && idx == 2))) { - idx = 1; - } else if (alerts && (!alert_notif->isVisible() || (!updateAvailable && idx == 1))) { - idx = 2; - } - center_layout->setCurrentIndex(idx); - - update_notif->setVisible(updateAvailable); - alert_notif->setVisible(alerts); - if (alerts) { - alert_notif->setText(QString::number(alerts) + (alerts > 1 ? tr(" ALERTS") : tr(" ALERT"))); - } -} diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h deleted file mode 100644 index a19b70ac3f..0000000000 --- a/selfdrive/ui/qt/home.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "common/params.h" -#include "selfdrive/ui/qt/offroad/driverview.h" -#include "selfdrive/ui/qt/body.h" -#include "selfdrive/ui/qt/onroad/onroad_home.h" -#include "selfdrive/ui/qt/sidebar.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/offroad_alerts.h" -#include "selfdrive/ui/ui.h" - -class OffroadHome : public QFrame { - Q_OBJECT - -public: - explicit OffroadHome(QWidget* parent = 0); - -signals: - void openSettings(int index = 0, const QString ¶m = ""); - -private: - void showEvent(QShowEvent *event) override; - void hideEvent(QHideEvent *event) override; - void refresh(); - - Params params; - - QTimer* timer; - ElidedLabel* version; - QStackedLayout* center_layout; - UpdateAlert *update_widget; - OffroadAlert* alerts_widget; - QPushButton* alert_notif; - QPushButton* update_notif; -}; - -class HomeWindow : public QWidget { - Q_OBJECT - -public: - explicit HomeWindow(QWidget* parent = 0); - -signals: - void openSettings(int index = 0, const QString ¶m = ""); - void closeSettings(); - -public slots: - void offroadTransition(bool offroad); - void showDriverView(bool show); - void showSidebar(bool show); - -protected: - void mousePressEvent(QMouseEvent* e) override; - void mouseDoubleClickEvent(QMouseEvent* e) override; - -private: - Sidebar *sidebar; - OffroadHome *home; - OnroadWindow *onroad; - BodyWindow *body; - DriverViewWindow *driver_view; - QStackedLayout *slayout; - -private slots: - void updateState(const UIState &s); -}; diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc deleted file mode 100644 index f19786a46f..0000000000 --- a/selfdrive/ui/qt/network/networking.cc +++ /dev/null @@ -1,413 +0,0 @@ -#include "selfdrive/ui/qt/network/networking.h" - -#include - -#include -#include -#include - -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -static const int ICON_WIDTH = 49; - -// Networking functions - -Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) { - main_layout = new QStackedLayout(this); - - wifi = new WifiManager(this); - connect(wifi, &WifiManager::refreshSignal, this, &Networking::refresh); - connect(wifi, &WifiManager::wrongPassword, this, &Networking::wrongPassword); - - wifiScreen = new QWidget(this); - QVBoxLayout* vlayout = new QVBoxLayout(wifiScreen); - vlayout->setContentsMargins(20, 20, 20, 20); - if (show_advanced) { - QPushButton* advancedSettings = new QPushButton(tr("Advanced")); - advancedSettings->setObjectName("advanced_btn"); - advancedSettings->setStyleSheet("margin-right: 30px;"); - advancedSettings->setFixedSize(400, 100); - connect(advancedSettings, &QPushButton::clicked, [=]() { main_layout->setCurrentWidget(an); }); - vlayout->addSpacing(10); - vlayout->addWidget(advancedSettings, 0, Qt::AlignRight); - vlayout->addSpacing(10); - } - - wifiWidget = new WifiUI(this, wifi); - wifiWidget->setObjectName("wifiWidget"); - connect(wifiWidget, &WifiUI::connectToNetwork, this, &Networking::connectToNetwork); - - ScrollView *wifiScroller = new ScrollView(wifiWidget, this); - wifiScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - vlayout->addWidget(wifiScroller, 1); - main_layout->addWidget(wifiScreen); - - an = new AdvancedNetworking(this, wifi); - connect(an, &AdvancedNetworking::backPress, [=]() { main_layout->setCurrentWidget(wifiScreen); }); - connect(an, &AdvancedNetworking::requestWifiScreen, [=]() { main_layout->setCurrentWidget(wifiScreen); }); - main_layout->addWidget(an); - - QPalette pal = palette(); - pal.setColor(QPalette::Window, QColor(0x29, 0x29, 0x29)); - setAutoFillBackground(true); - setPalette(pal); - - setStyleSheet(R"( - #wifiWidget > QPushButton, #back_btn, #advanced_btn { - font-size: 50px; - margin: 0px; - padding: 15px; - border-width: 0; - border-radius: 30px; - color: #dddddd; - background-color: #393939; - } - #back_btn:pressed, #advanced_btn:pressed { - background-color: #4a4a4a; - } - )"); - main_layout->setCurrentWidget(wifiScreen); -} - -void Networking::setPrimeType(PrimeState::Type type) { - an->setGsmVisible(type == PrimeState::PRIME_TYPE_NONE || type == PrimeState::PRIME_TYPE_LITE); - wifi->ipv4_forward = (type == PrimeState::PRIME_TYPE_NONE || type == PrimeState::PRIME_TYPE_LITE); -} - -void Networking::refresh() { - wifiWidget->refresh(); - an->refresh(); -} - -void Networking::connectToNetwork(const Network n) { - if (wifi->isKnownConnection(n.ssid)) { - wifi->activateWifiConnection(n.ssid); - } else if (n.security_type == SecurityType::OPEN) { - wifi->connect(n, false); - } else if (n.security_type == SecurityType::WPA) { - QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); - if (!pass.isEmpty()) { - wifi->connect(n, false, pass); - } - } -} - -void Networking::wrongPassword(const QString &ssid) { - if (wifi->seenNetworks.contains(ssid)) { - const Network &n = wifi->seenNetworks.value(ssid); - QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); - if (!pass.isEmpty()) { - wifi->connect(n, false, pass); - } - } -} - -void Networking::showEvent(QShowEvent *event) { - wifi->start(); -} - -void Networking::hideEvent(QHideEvent *event) { - main_layout->setCurrentWidget(wifiScreen); - wifi->stop(); -} - -// AdvancedNetworking functions - -AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi) { - - QVBoxLayout* main_layout = new QVBoxLayout(this); - main_layout->setMargin(40); - main_layout->setSpacing(20); - - // Back button - QPushButton* back = new QPushButton(tr("Back")); - back->setObjectName("back_btn"); - back->setFixedSize(400, 100); - connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); - main_layout->addWidget(back, 0, Qt::AlignLeft); - - ListWidget *list = new ListWidget(this); - // Enable tethering layout - tetheringToggle = new ToggleControl(tr("Enable Tethering"), "", "", wifi->isTetheringEnabled()); - list->addItem(tetheringToggle); - QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering); - - // Change tethering password - ButtonControl *editPasswordButton = new ButtonControl(tr("Tethering Password"), tr("EDIT")); - connect(editPasswordButton, &ButtonControl::clicked, [=]() { - QString pass = InputDialog::getText(tr("Enter new tethering password"), this, "", true, 8, wifi->getTetheringPassword()); - if (!pass.isEmpty()) { - wifi->changeTetheringPassword(pass); - } - }); - list->addItem(editPasswordButton); - - // IP address - ipLabel = new LabelControl(tr("IP Address"), wifi->ipv4_address); - list->addItem(ipLabel); - - // Roaming toggle - const bool roamingEnabled = params.getBool("GsmRoaming"); - roamingToggle = new ToggleControl(tr("Enable Roaming"), "", "", roamingEnabled); - QObject::connect(roamingToggle, &ToggleControl::toggleFlipped, [=](bool state) { - params.putBool("GsmRoaming", state); - wifi->updateGsmSettings(state, QString::fromStdString(params.get("GsmApn")), params.getBool("GsmMetered")); - }); - list->addItem(roamingToggle); - - // APN settings - editApnButton = new ButtonControl(tr("APN Setting"), tr("EDIT")); - connect(editApnButton, &ButtonControl::clicked, [=]() { - const QString cur_apn = QString::fromStdString(params.get("GsmApn")); - QString apn = InputDialog::getText(tr("Enter APN"), this, tr("leave blank for automatic configuration"), false, -1, cur_apn).trimmed(); - - if (apn.isEmpty()) { - params.remove("GsmApn"); - } else { - params.put("GsmApn", apn.toStdString()); - } - wifi->updateGsmSettings(params.getBool("GsmRoaming"), apn, params.getBool("GsmMetered")); - }); - list->addItem(editApnButton); - - // Cellular metered toggle (prime lite or none) - const bool metered = params.getBool("GsmMetered"); - cellularMeteredToggle = new ToggleControl(tr("Cellular Metered"), tr("Prevent large data uploads when on a metered cellular connection"), "", metered); - QObject::connect(cellularMeteredToggle, &SshToggle::toggleFlipped, [=](bool state) { - params.putBool("GsmMetered", state); - wifi->updateGsmSettings(params.getBool("GsmRoaming"), QString::fromStdString(params.get("GsmApn")), state); - }); - list->addItem(cellularMeteredToggle); - - // Wi-Fi metered toggle - std::vector metered_button_texts{tr("default"), tr("metered"), tr("unmetered")}; - wifiMeteredToggle = new MultiButtonControl(tr("Wi-Fi Network Metered"), tr("Prevent large data uploads when on a metered Wi-Fi connection"), "", metered_button_texts); - QObject::connect(wifiMeteredToggle, &MultiButtonControl::buttonClicked, [=](int id) { - wifiMeteredToggle->setEnabled(false); - MeteredType metered = MeteredType::UNKNOWN; - if (id == NM_METERED_YES) { - metered = MeteredType::YES; - } else if (id == NM_METERED_NO) { - metered = MeteredType::NO; - } - auto pending_call = wifi->setCurrentNetworkMetered(metered); - if (pending_call) { - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(*pending_call); - QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [=]() { - refresh(); - watcher->deleteLater(); - }); - } - }); - list->addItem(wifiMeteredToggle); - - // Hidden Network - hiddenNetworkButton = new ButtonControl(tr("Hidden Network"), tr("CONNECT")); - connect(hiddenNetworkButton, &ButtonControl::clicked, [=]() { - QString ssid = InputDialog::getText(tr("Enter SSID"), this, "", false, 1); - if (!ssid.isEmpty()) { - QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(ssid), true, -1); - Network hidden_network; - hidden_network.ssid = ssid.toUtf8(); - if (!pass.isEmpty()) { - hidden_network.security_type = SecurityType::WPA; - wifi->connect(hidden_network, true, pass); - } else { - wifi->connect(hidden_network, true); - } - emit requestWifiScreen(); - } - }); - list->addItem(hiddenNetworkButton); - - // Set initial config - wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn")), metered); - - main_layout->addWidget(new ScrollView(list, this)); - main_layout->addStretch(1); -} - -void AdvancedNetworking::setGsmVisible(bool visible) { - roamingToggle->setVisible(visible); - editApnButton->setVisible(visible); - cellularMeteredToggle->setVisible(visible); -} - -void AdvancedNetworking::refresh() { - ipLabel->setText(wifi->ipv4_address); - tetheringToggle->setEnabled(true); - - if (wifi->isTetheringEnabled() || wifi->ipv4_address == "") { - wifiMeteredToggle->setEnabled(false); - wifiMeteredToggle->setCheckedButton(0); - } else if (wifi->ipv4_address != "") { - MeteredType metered = wifi->currentNetworkMetered(); - wifiMeteredToggle->setEnabled(true); - wifiMeteredToggle->setCheckedButton(static_cast(metered)); - } - - update(); -} - -void AdvancedNetworking::toggleTethering(bool enabled) { - wifi->setTetheringEnabled(enabled); - tetheringToggle->setEnabled(false); - if (enabled) { - wifiMeteredToggle->setEnabled(false); - wifiMeteredToggle->setCheckedButton(0); - } -} - -// WifiUI functions - -WifiUI::WifiUI(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - main_layout->setSpacing(0); - - // load imgs - for (const auto &s : {"low", "medium", "high", "full"}) { - QPixmap pix(ASSET_PATH + "/icons/wifi_strength_" + s + ".svg"); - strengths.push_back(pix.scaledToHeight(68, Qt::SmoothTransformation)); - } - lock = QPixmap(ASSET_PATH + "icons/lock_closed.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); - checkmark = QPixmap(ASSET_PATH + "icons/checkmark.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); - circled_slash = QPixmap(ASSET_PATH + "icons/circled_slash.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); - - scanningLabel = new QLabel(tr("Scanning for networks...")); - scanningLabel->setStyleSheet("font-size: 65px;"); - main_layout->addWidget(scanningLabel, 0, Qt::AlignCenter); - - wifi_list_widget = new ListWidget(this); - wifi_list_widget->setVisible(false); - main_layout->addWidget(wifi_list_widget); - - setStyleSheet(R"( - QScrollBar::handle:vertical { - min-height: 0px; - border-radius: 4px; - background-color: #8A8A8A; - } - #forgetBtn { - font-size: 32px; - font-weight: 600; - color: #292929; - background-color: #BDBDBD; - border-width: 1px solid #828282; - border-radius: 5px; - padding: 40px; - padding-bottom: 16px; - padding-top: 16px; - } - #forgetBtn:pressed { - background-color: #828282; - } - #connecting { - font-size: 32px; - font-weight: 600; - color: white; - border-radius: 0; - padding: 27px; - padding-left: 43px; - padding-right: 43px; - background-color: black; - } - #ssidLabel { - text-align: left; - border: none; - padding-top: 50px; - padding-bottom: 50px; - } - #ssidLabel:disabled { - color: #696969; - } - )"); -} - -void WifiUI::refresh() { - bool is_empty = wifi->seenNetworks.isEmpty(); - scanningLabel->setVisible(is_empty); - wifi_list_widget->setVisible(!is_empty); - if (is_empty) return; - - setUpdatesEnabled(false); - - const bool is_tethering_enabled = wifi->isTetheringEnabled(); - QList sortedNetworks = wifi->seenNetworks.values(); - std::sort(sortedNetworks.begin(), sortedNetworks.end(), compare_by_strength); - - int n = 0; - for (Network &network : sortedNetworks) { - QPixmap status_icon; - if (network.connected == ConnectedType::CONNECTED) { - status_icon = checkmark; - } else if (network.security_type == SecurityType::UNSUPPORTED) { - status_icon = circled_slash; - } else if (network.security_type == SecurityType::WPA) { - status_icon = lock; - } - bool show_forget_btn = wifi->isKnownConnection(network.ssid) && !is_tethering_enabled; - QPixmap strength = strengths[strengthLevel(network.strength)]; - - auto item = getItem(n++); - item->setItem(network, status_icon, show_forget_btn, strength); - item->setVisible(true); - } - for (; n < wifi_items.size(); ++n) wifi_items[n]->setVisible(false); - - setUpdatesEnabled(true); -} - -WifiItem *WifiUI::getItem(int n) { - auto item = n < wifi_items.size() ? wifi_items[n] : wifi_items.emplace_back(new WifiItem(tr("CONNECTING..."), tr("FORGET"))); - if (!item->parentWidget()) { - QObject::connect(item, &WifiItem::connectToNetwork, this, &WifiUI::connectToNetwork); - QObject::connect(item, &WifiItem::forgotNetwork, [this](const Network n) { - if (ConfirmationDialog::confirm(tr("Forget Wi-Fi Network \"%1\"?").arg(QString::fromUtf8(n.ssid)), tr("Forget"), this)) - wifi->forgetConnection(n.ssid); - }); - wifi_list_widget->addItem(item); - } - return item; -} - -// WifiItem - -WifiItem::WifiItem(const QString &connecting_text, const QString &forget_text, QWidget *parent) : QWidget(parent) { - QHBoxLayout *hlayout = new QHBoxLayout(this); - hlayout->setContentsMargins(44, 0, 73, 0); - hlayout->setSpacing(50); - - hlayout->addWidget(ssidLabel = new ElidedLabel()); - ssidLabel->setObjectName("ssidLabel"); - ssidLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - hlayout->addWidget(connecting = new QPushButton(connecting_text), 0, Qt::AlignRight); - connecting->setObjectName("connecting"); - hlayout->addWidget(forgetBtn = new QPushButton(forget_text), 0, Qt::AlignRight); - forgetBtn->setObjectName("forgetBtn"); - hlayout->addWidget(iconLabel = new QLabel(), 0, Qt::AlignRight); - hlayout->addWidget(strengthLabel = new QLabel(), 0, Qt::AlignRight); - - iconLabel->setFixedWidth(ICON_WIDTH); - QObject::connect(forgetBtn, &QPushButton::clicked, [this]() { emit forgotNetwork(network); }); - QObject::connect(ssidLabel, &ElidedLabel::clicked, [this]() { - if (network.connected == ConnectedType::DISCONNECTED) emit connectToNetwork(network); - }); -} - -void WifiItem::setItem(const Network &n, const QPixmap &status_icon, bool show_forget_btn, const QPixmap &strength_icon) { - network = n; - - ssidLabel->setText(n.ssid); - ssidLabel->setEnabled(n.security_type != SecurityType::UNSUPPORTED); - ssidLabel->setFont(InterFont(55, network.connected == ConnectedType::DISCONNECTED ? QFont::Normal : QFont::Bold)); - - connecting->setVisible(n.connected == ConnectedType::CONNECTING); - forgetBtn->setVisible(show_forget_btn); - - iconLabel->setPixmap(status_icon); - strengthLabel->setPixmap(strength_icon); -} diff --git a/selfdrive/ui/qt/network/networking.h b/selfdrive/ui/qt/network/networking.h deleted file mode 100644 index 0bdf64e459..0000000000 --- a/selfdrive/ui/qt/network/networking.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include - -#include "selfdrive/ui/qt/network/wifi_manager.h" -#include "selfdrive/ui/qt/prime_state.h" -#include "selfdrive/ui/qt/widgets/input.h" -#include "selfdrive/ui/qt/widgets/ssh_keys.h" -#include "selfdrive/ui/qt/widgets/toggle.h" - -class WifiItem : public QWidget { - Q_OBJECT -public: - explicit WifiItem(const QString &connecting_text, const QString &forget_text, QWidget* parent = nullptr); - void setItem(const Network& n, const QPixmap &icon, bool show_forget_btn, const QPixmap &strength); - -signals: - // Cannot pass Network by reference. it may change after the signal is sent. - void connectToNetwork(const Network n); - void forgotNetwork(const Network n); - -protected: - ElidedLabel* ssidLabel; - QPushButton* connecting; - QPushButton* forgetBtn; - QLabel* iconLabel; - QLabel* strengthLabel; - Network network; -}; - -class WifiUI : public QWidget { - Q_OBJECT - -public: - explicit WifiUI(QWidget *parent = 0, WifiManager* wifi = 0); - -private: - WifiItem *getItem(int n); - - WifiManager *wifi = nullptr; - QLabel *scanningLabel = nullptr; - QPixmap lock; - QPixmap checkmark; - QPixmap circled_slash; - QVector strengths; - ListWidget *wifi_list_widget = nullptr; - std::vector wifi_items; - -signals: - void connectToNetwork(const Network n); - -public slots: - void refresh(); -}; - -class AdvancedNetworking : public QWidget { - Q_OBJECT -public: - explicit AdvancedNetworking(QWidget* parent = 0, WifiManager* wifi = 0); - void setGsmVisible(bool visible); - -private: - LabelControl* ipLabel; - ToggleControl* tetheringToggle; - ToggleControl* roamingToggle; - ButtonControl* editApnButton; - ButtonControl* hiddenNetworkButton; - ToggleControl* cellularMeteredToggle; - MultiButtonControl* wifiMeteredToggle; - WifiManager* wifi = nullptr; - Params params; - -signals: - void backPress(); - void requestWifiScreen(); - -public slots: - void toggleTethering(bool enabled); - void refresh(); -}; - -class Networking : public QFrame { - Q_OBJECT - -public: - explicit Networking(QWidget* parent = 0, bool show_advanced = true); - void setPrimeType(PrimeState::Type type); - WifiManager* wifi = nullptr; - -private: - QStackedLayout* main_layout = nullptr; - QWidget* wifiScreen = nullptr; - AdvancedNetworking* an = nullptr; - WifiUI* wifiWidget; - - void showEvent(QShowEvent* event) override; - void hideEvent(QHideEvent* event) override; - -public slots: - void refresh(); - -private slots: - void connectToNetwork(const Network n); - void wrongPassword(const QString &ssid); -}; diff --git a/selfdrive/ui/qt/network/networkmanager.h b/selfdrive/ui/qt/network/networkmanager.h deleted file mode 100644 index 8bdeaf3bbd..0000000000 --- a/selfdrive/ui/qt/network/networkmanager.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -/** - * We are using a NetworkManager DBUS API : https://developer.gnome.org/NetworkManager/1.26/spec.html - * */ - -// https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NM80211ApFlags -const int NM_802_11_AP_FLAGS_NONE = 0x00000000; -const int NM_802_11_AP_FLAGS_PRIVACY = 0x00000001; -const int NM_802_11_AP_FLAGS_WPS = 0x00000002; - -// https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NM80211ApSecurityFlags -const int NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001; -const int NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002; -const int NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010; -const int NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020; -const int NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100; -const int NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200; - -const QString NM_DBUS_PATH = "/org/freedesktop/NetworkManager"; -const QString NM_DBUS_PATH_SETTINGS = "/org/freedesktop/NetworkManager/Settings"; - -const QString NM_DBUS_INTERFACE = "org.freedesktop.NetworkManager"; -const QString NM_DBUS_INTERFACE_PROPERTIES = "org.freedesktop.DBus.Properties"; -const QString NM_DBUS_INTERFACE_SETTINGS = "org.freedesktop.NetworkManager.Settings"; -const QString NM_DBUS_INTERFACE_SETTINGS_CONNECTION = "org.freedesktop.NetworkManager.Settings.Connection"; -const QString NM_DBUS_INTERFACE_DEVICE = "org.freedesktop.NetworkManager.Device"; -const QString NM_DBUS_INTERFACE_DEVICE_WIRELESS = "org.freedesktop.NetworkManager.Device.Wireless"; -const QString NM_DBUS_INTERFACE_ACCESS_POINT = "org.freedesktop.NetworkManager.AccessPoint"; -const QString NM_DBUS_INTERFACE_ACTIVE_CONNECTION = "org.freedesktop.NetworkManager.Connection.Active"; -const QString NM_DBUS_INTERFACE_IP4_CONFIG = "org.freedesktop.NetworkManager.IP4Config"; - -const QString NM_DBUS_SERVICE = "org.freedesktop.NetworkManager"; - -const int NM_DEVICE_STATE_UNKNOWN = 0; -const int NM_DEVICE_STATE_ACTIVATED = 100; -const int NM_DEVICE_STATE_NEED_AUTH = 60; -const int NM_DEVICE_TYPE_WIFI = 2; -const int NM_DEVICE_TYPE_MODEM = 8; -const int NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8; -const int DBUS_TIMEOUT = 100; - -// https://developer-old.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NMMetered -const int NM_METERED_UNKNOWN = 0; -const int NM_METERED_YES = 1; -const int NM_METERED_NO = 2; -const int NM_METERED_GUESS_YES = 3; -const int NM_METERED_GUESS_NO = 4; diff --git a/selfdrive/ui/qt/network/wifi_manager.cc b/selfdrive/ui/qt/network/wifi_manager.cc deleted file mode 100644 index d4ad8974b0..0000000000 --- a/selfdrive/ui/qt/network/wifi_manager.cc +++ /dev/null @@ -1,539 +0,0 @@ -#include "selfdrive/ui/qt/network/wifi_manager.h" - -#include - -#include "common/swaglog.h" -#include "selfdrive/ui/qt/util.h" - -bool compare_by_strength(const Network &a, const Network &b) { - return std::tuple(a.connected, strengthLevel(a.strength), b.ssid) > - std::tuple(b.connected, strengthLevel(b.strength), a.ssid); -} - -template -T call(const QString &path, const QString &interface, const QString &method, Args &&...args) { - QDBusInterface nm(NM_DBUS_SERVICE, path, interface, QDBusConnection::systemBus()); - nm.setTimeout(DBUS_TIMEOUT); - - QDBusMessage response = nm.call(method, std::forward(args)...); - if (response.type() == QDBusMessage::ErrorMessage) { - qCritical() << "DBus call error:" << response.errorMessage(); - return T(); - } - - if constexpr (std::is_same_v) { - return response; - } else if (response.arguments().count() >= 1) { - QVariant vFirst = response.arguments().at(0).value().variant(); - if (vFirst.canConvert()) { - return vFirst.value(); - } - QDebug critical = qCritical(); - critical << "Variant unpacking failure :" << method << ','; - (critical << ... << args); - } - return T(); -} - -template -QDBusPendingCall asyncCall(const QString &path, const QString &interface, const QString &method, Args &&...args) { - QDBusInterface nm = QDBusInterface(NM_DBUS_SERVICE, path, interface, QDBusConnection::systemBus()); - return nm.asyncCall(method, args...); -} - -bool emptyPath(const QString &path) { - return path == "" || path == "/"; -} - -WifiManager::WifiManager(QObject *parent) : QObject(parent) { - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - - // Set tethering ssid as "weedle" + first 4 characters of a dongle id - tethering_ssid = "weedle"; - if (auto dongle_id = getDongleId()) { - tethering_ssid += "-" + dongle_id->left(4); - } - - adapter = getAdapter(); - if (!adapter.isEmpty()) { - setup(); - } else { - QDBusConnection::systemBus().connect(NM_DBUS_SERVICE, NM_DBUS_PATH, NM_DBUS_INTERFACE, "DeviceAdded", this, SLOT(deviceAdded(QDBusObjectPath))); - } - - timer.callOnTimeout(this, &WifiManager::requestScan); - - initConnections(); -} - -void WifiManager::setup() { - auto bus = QDBusConnection::systemBus(); - bus.connect(NM_DBUS_SERVICE, adapter, NM_DBUS_INTERFACE_DEVICE, "StateChanged", this, SLOT(stateChange(unsigned int, unsigned int, unsigned int))); - bus.connect(NM_DBUS_SERVICE, adapter, NM_DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", this, SLOT(propertyChange(QString, QVariantMap, QStringList))); - - bus.connect(NM_DBUS_SERVICE, NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "ConnectionRemoved", this, SLOT(connectionRemoved(QDBusObjectPath))); - bus.connect(NM_DBUS_SERVICE, NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "NewConnection", this, SLOT(newConnection(QDBusObjectPath))); - - raw_adapter_state = call(adapter, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_DEVICE, "State"); - activeAp = call(adapter, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_DEVICE_WIRELESS, "ActiveAccessPoint").path(); - - requestScan(); -} - -void WifiManager::start() { - timer.start(5000); - refreshNetworks(); -} - -void WifiManager::stop() { - timer.stop(); -} - -void WifiManager::refreshNetworks() { - if (adapter.isEmpty() || !timer.isActive()) return; - - QDBusPendingCall pending_call = asyncCall(adapter, NM_DBUS_INTERFACE_DEVICE_WIRELESS, "GetAllAccessPoints"); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending_call); - QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &WifiManager::refreshFinished); -} - -void WifiManager::refreshFinished(QDBusPendingCallWatcher *watcher) { - ipv4_address = getIp4Address(); - seenNetworks.clear(); - - const QDBusReply> watcher_reply = *watcher; - if (!watcher_reply.isValid()) { - qCritical() << "Failed to refresh"; - watcher->deleteLater(); - return; - } - - for (const QDBusObjectPath &path : watcher_reply.value()) { - QDBusReply reply = call(path.path(), NM_DBUS_INTERFACE_PROPERTIES, "GetAll", NM_DBUS_INTERFACE_ACCESS_POINT); - if (!reply.isValid()) { - qCritical() << "Failed to retrieve properties for path:" << path.path(); - continue; - } - - auto properties = reply.value(); - const QByteArray ssid = properties["Ssid"].toByteArray(); - if (ssid.isEmpty()) continue; - - // May be multiple access points for each SSID. - // Use first for ssid and security type, then update connected status and strength using all - if (!seenNetworks.contains(ssid)) { - seenNetworks[ssid] = {ssid, 0U, ConnectedType::DISCONNECTED, getSecurityType(properties)}; - } - - if (path.path() == activeAp) { - seenNetworks[ssid].connected = (ssid == connecting_to_network) ? ConnectedType::CONNECTING : ConnectedType::CONNECTED; - } - - uint32_t strength = properties["Strength"].toUInt(); - if (seenNetworks[ssid].strength < strength) { - seenNetworks[ssid].strength = strength; - } - } - - emit refreshSignal(); - watcher->deleteLater(); -} - -QString WifiManager::getIp4Address() { - if (raw_adapter_state != NM_DEVICE_STATE_ACTIVATED) return ""; - - for (const auto &p : getActiveConnections()) { - QString type = call(p.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); - if (type == "802-11-wireless") { - auto ip4config = call(p.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Ip4Config"); - const auto &arr = call(ip4config.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_IP4_CONFIG, "AddressData"); - QVariantMap path; - arr.beginArray(); - while (!arr.atEnd()) { - arr >> path; - arr.endArray(); - return path.value("address").value(); - } - arr.endArray(); - } - } - return ""; -} - -SecurityType WifiManager::getSecurityType(const QVariantMap &properties) { - int sflag = properties["Flags"].toUInt(); - int wpaflag = properties["WpaFlags"].toUInt(); - int rsnflag = properties["RsnFlags"].toUInt(); - int wpa_props = wpaflag | rsnflag; - - // obtained by looking at flags of networks in the office as reported by an Android phone - const int supports_wpa = NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104 | NM_802_11_AP_SEC_GROUP_WEP40 | NM_802_11_AP_SEC_GROUP_WEP104 | NM_802_11_AP_SEC_KEY_MGMT_PSK; - - if ((sflag == NM_802_11_AP_FLAGS_NONE) || ((sflag & NM_802_11_AP_FLAGS_WPS) && !(wpa_props & supports_wpa))) { - return SecurityType::OPEN; - } else if ((sflag & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_props & supports_wpa) && !(wpa_props & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { - return SecurityType::WPA; - } else { - LOGW("Unsupported network! sflag: %d, wpaflag: %d, rsnflag: %d", sflag, wpaflag, rsnflag); - return SecurityType::UNSUPPORTED; - } -} - -void WifiManager::connect(const Network &n, const bool is_hidden, const QString &password, const QString &username) { - setCurrentConnecting(n.ssid); - forgetConnection(n.ssid); // Clear all connections that may already exist to the network we are connecting - Connection connection; - connection["connection"]["type"] = "802-11-wireless"; - connection["connection"]["uuid"] = QUuid::createUuid().toString().remove('{').remove('}'); - connection["connection"]["id"] = "openpilot connection " + QString::fromStdString(n.ssid.toStdString()); - connection["connection"]["autoconnect-retries"] = 0; - - connection["802-11-wireless"]["ssid"] = n.ssid; - connection["802-11-wireless"]["hidden"] = is_hidden; - connection["802-11-wireless"]["mode"] = "infrastructure"; - - if (n.security_type == SecurityType::WPA) { - connection["802-11-wireless-security"]["key-mgmt"] = "wpa-psk"; - connection["802-11-wireless-security"]["auth-alg"] = "open"; - connection["802-11-wireless-security"]["psk"] = password; - } - - connection["ipv4"]["method"] = "auto"; - connection["ipv4"]["dns-priority"] = 600; - connection["ipv6"]["method"] = "ignore"; - - asyncCall(NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "AddConnection", QVariant::fromValue(connection)); -} - -void WifiManager::deactivateConnectionBySsid(const QString &ssid) { - for (QDBusObjectPath active_connection : getActiveConnections()) { - auto pth = call(active_connection.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "SpecificObject"); - if (!emptyPath(pth.path())) { - QString Ssid = get_property(pth.path(), "Ssid"); - if (Ssid == ssid) { - deactivateConnection(active_connection); - return; - } - } - } -} - -void WifiManager::deactivateConnection(const QDBusObjectPath &path) { - asyncCall(NM_DBUS_PATH, NM_DBUS_INTERFACE, "DeactivateConnection", QVariant::fromValue(path)); -} - -QVector WifiManager::getActiveConnections() { - auto result = call(NM_DBUS_PATH, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE, "ActiveConnections"); - return qdbus_cast>(result); -} - -bool WifiManager::isKnownConnection(const QString &ssid) { - return !getConnectionPath(ssid).path().isEmpty(); -} - -void WifiManager::forgetConnection(const QString &ssid) { - const QDBusObjectPath &path = getConnectionPath(ssid); - if (!path.path().isEmpty()) { - call(path.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "Delete"); - } -} - -void WifiManager::setCurrentConnecting(const QString &ssid) { - connecting_to_network = ssid; - for (auto &network : seenNetworks) { - network.connected = (network.ssid == ssid) ? ConnectedType::CONNECTING : ConnectedType::DISCONNECTED; - } - emit refreshSignal(); -} - -uint WifiManager::getAdapterType(const QDBusObjectPath &path) { - return call(path.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_DEVICE, "DeviceType"); -} - -void WifiManager::requestScan() { - if (!adapter.isEmpty()) { - asyncCall(adapter, NM_DBUS_INTERFACE_DEVICE_WIRELESS, "RequestScan", QVariantMap()); - } -} - -QByteArray WifiManager::get_property(const QString &network_path , const QString &property) { - return call(network_path, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACCESS_POINT, property); -} - -QString WifiManager::getAdapter(const uint adapter_type) { - QDBusReply> response = call(NM_DBUS_PATH, NM_DBUS_INTERFACE, "GetDevices"); - for (const QDBusObjectPath &path : response.value()) { - if (getAdapterType(path) == adapter_type) { - return path.path(); - } - } - return ""; -} - -void WifiManager::stateChange(unsigned int new_state, unsigned int previous_state, unsigned int change_reason) { - raw_adapter_state = new_state; - if (new_state == NM_DEVICE_STATE_NEED_AUTH && change_reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT && !connecting_to_network.isEmpty()) { - forgetConnection(connecting_to_network); - emit wrongPassword(connecting_to_network); - } else if (new_state == NM_DEVICE_STATE_ACTIVATED) { - connecting_to_network = ""; - refreshNetworks(); - } -} - -// https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.Device.Wireless.html -void WifiManager::propertyChange(const QString &interface, const QVariantMap &props, const QStringList &invalidated_props) { - if (interface == NM_DBUS_INTERFACE_DEVICE_WIRELESS && props.contains("LastScan")) { - refreshNetworks(); - } else if (interface == NM_DBUS_INTERFACE_DEVICE_WIRELESS && props.contains("ActiveAccessPoint")) { - activeAp = props.value("ActiveAccessPoint").value().path(); - } -} - -void WifiManager::deviceAdded(const QDBusObjectPath &path) { - if (getAdapterType(path) == NM_DEVICE_TYPE_WIFI && emptyPath(adapter)) { - adapter = path.path(); - setup(); - } -} - -void WifiManager::connectionRemoved(const QDBusObjectPath &path) { - knownConnections.remove(path); -} - -void WifiManager::newConnection(const QDBusObjectPath &path) { - Connection settings = getConnectionSettings(path); - if (settings.value("connection").value("type") == "802-11-wireless") { - knownConnections[path] = settings.value("802-11-wireless").value("ssid").toString(); - if (knownConnections[path] != tethering_ssid) { - activateWifiConnection(knownConnections[path]); - } - } -} - -QDBusObjectPath WifiManager::getConnectionPath(const QString &ssid) { - return knownConnections.key(ssid); -} - -Connection WifiManager::getConnectionSettings(const QDBusObjectPath &path) { - return QDBusReply(call(path.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "GetSettings")).value(); -} - -void WifiManager::initConnections() { - const QDBusReply> response = call(NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "ListConnections"); - for (const QDBusObjectPath &path : response.value()) { - const Connection settings = getConnectionSettings(path); - if (settings.value("connection").value("type") == "802-11-wireless") { - knownConnections[path] = settings.value("802-11-wireless").value("ssid").toString(); - } else if (settings.value("connection").value("id") == "lte") { - lteConnectionPath = path; - } - } - - if (!isKnownConnection(tethering_ssid)) { - addTetheringConnection(); - } -} - -std::optional WifiManager::activateWifiConnection(const QString &ssid) { - const QDBusObjectPath &path = getConnectionPath(ssid); - if (!path.path().isEmpty()) { - setCurrentConnecting(ssid); - return asyncCall(NM_DBUS_PATH, NM_DBUS_INTERFACE, "ActivateConnection", QVariant::fromValue(path), QVariant::fromValue(QDBusObjectPath(adapter)), QVariant::fromValue(QDBusObjectPath("/"))); - } - return std::nullopt; -} - -void WifiManager::activateModemConnection(const QDBusObjectPath &path) { - QString modem = getAdapter(NM_DEVICE_TYPE_MODEM); - if (!path.path().isEmpty() && !modem.isEmpty()) { - asyncCall(NM_DBUS_PATH, NM_DBUS_INTERFACE, "ActivateConnection", QVariant::fromValue(path), QVariant::fromValue(QDBusObjectPath(modem)), QVariant::fromValue(QDBusObjectPath("/"))); - } -} - -// function matches tici/hardware.py -// FIXME: it can mistakenly show CELL when connected to WIFI -NetworkType WifiManager::currentNetworkType() { - auto primary_conn = call(NM_DBUS_PATH, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE, "PrimaryConnection"); - auto primary_type = call(primary_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); - - if (primary_type == "802-3-ethernet") { - return NetworkType::ETHERNET; - } else if (primary_type == "802-11-wireless" && !isTetheringEnabled()) { - return NetworkType::WIFI; - } else { - for (const QDBusObjectPath &conn : getActiveConnections()) { - auto type = call(conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); - if (type == "gsm") { - return NetworkType::CELL; - } - } - } - return NetworkType::NONE; -} - -MeteredType WifiManager::currentNetworkMetered() { - MeteredType metered = MeteredType::UNKNOWN; - for (const auto &active_conn : getActiveConnections()) { - QString type = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); - if (type == "802-11-wireless") { - QDBusObjectPath conn = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Connection"); - if (!conn.path().isEmpty()) { - Connection settings = getConnectionSettings(conn); - int metered_prop = settings.value("connection").value("metered").toInt(); - if (metered_prop == NM_METERED_YES) { - metered = MeteredType::YES; - } else if (metered_prop == NM_METERED_NO) { - metered = MeteredType::NO; - } - } - break; - } - } - return metered; -} - -std::optional WifiManager::setCurrentNetworkMetered(MeteredType metered) { - for (const auto &active_conn : getActiveConnections()) { - QString type = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); - if (type == "802-11-wireless") { - if (!isTetheringEnabled()) { - QDBusObjectPath conn = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Connection"); - if (!conn.path().isEmpty()) { - Connection settings = getConnectionSettings(conn); - settings["connection"]["metered"] = static_cast(metered); - return asyncCall(conn.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "Update", QVariant::fromValue(settings)); - } - } - } - } - return std::nullopt; -} - -void WifiManager::updateGsmSettings(bool roaming, QString apn, bool metered) { - if (!lteConnectionPath.path().isEmpty()) { - bool changes = false; - bool auto_config = apn.isEmpty(); - Connection settings = getConnectionSettings(lteConnectionPath); - if (settings.value("gsm").value("auto-config").toBool() != auto_config) { - qWarning() << "Changing gsm.auto-config to" << auto_config; - settings["gsm"]["auto-config"] = auto_config; - changes = true; - } - - if (settings.value("gsm").value("apn").toString() != apn) { - qWarning() << "Changing gsm.apn to" << apn; - settings["gsm"]["apn"] = apn; - changes = true; - } - - if (settings.value("gsm").value("home-only").toBool() == roaming) { - qWarning() << "Changing gsm.home-only to" << !roaming; - settings["gsm"]["home-only"] = !roaming; - changes = true; - } - - int meteredInt = metered ? NM_METERED_UNKNOWN : NM_METERED_NO; - if (settings.value("connection").value("metered").toInt() != meteredInt) { - qWarning() << "Changing connection.metered to" << meteredInt; - settings["connection"]["metered"] = meteredInt; - changes = true; - } - - if (changes) { - QDBusPendingCall pending_call = asyncCall(lteConnectionPath.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "UpdateUnsaved", QVariant::fromValue(settings)); // update is temporary - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending_call); - QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher]() { - deactivateConnection(lteConnectionPath); - activateModemConnection(lteConnectionPath); - watcher->deleteLater(); - }); - } - } -} - -// Functions for tethering -void WifiManager::addTetheringConnection() { - Connection connection; - connection["connection"]["id"] = "Hotspot"; - connection["connection"]["uuid"] = QUuid::createUuid().toString().remove('{').remove('}'); - connection["connection"]["type"] = "802-11-wireless"; - connection["connection"]["interface-name"] = "wlan0"; - connection["connection"]["autoconnect"] = false; - - connection["802-11-wireless"]["band"] = "bg"; - connection["802-11-wireless"]["mode"] = "ap"; - connection["802-11-wireless"]["ssid"] = tethering_ssid.toUtf8(); - - connection["802-11-wireless-security"]["group"] = QStringList("ccmp"); - connection["802-11-wireless-security"]["key-mgmt"] = "wpa-psk"; - connection["802-11-wireless-security"]["pairwise"] = QStringList("ccmp"); - connection["802-11-wireless-security"]["proto"] = QStringList("rsn"); - connection["802-11-wireless-security"]["psk"] = defaultTetheringPassword; - - connection["ipv4"]["method"] = "shared"; - QVariantMap address; - address["address"] = "192.168.43.1"; - address["prefix"] = 24u; - connection["ipv4"]["address-data"] = QVariant::fromValue(IpConfig() << address); - connection["ipv4"]["gateway"] = "192.168.43.1"; - connection["ipv4"]["never-default"] = true; - connection["ipv6"]["method"] = "ignore"; - - asyncCall(NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "AddConnection", QVariant::fromValue(connection)); -} - -void WifiManager::tetheringActivated(QDBusPendingCallWatcher *call) { - if (!ipv4_forward) { - QTimer::singleShot(5000, this, [=] { - qWarning() << "net.ipv4.ip_forward = 0"; - std::system("sudo sysctl net.ipv4.ip_forward=0"); - }); - } - call->deleteLater(); - tethering_on = true; -} - -void WifiManager::setTetheringEnabled(bool enabled) { - if (enabled) { - auto pending_call = activateWifiConnection(tethering_ssid); - - if (pending_call) { - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(*pending_call); - QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &WifiManager::tetheringActivated); - } - - } else { - deactivateConnectionBySsid(tethering_ssid); - tethering_on = false; - } -} - -bool WifiManager::isTetheringEnabled() { - if (!emptyPath(activeAp)) { - return get_property(activeAp, "Ssid") == tethering_ssid; - } - return false; -} - -QString WifiManager::getTetheringPassword() { - const QDBusObjectPath &path = getConnectionPath(tethering_ssid); - if (!path.path().isEmpty()) { - QDBusReply> response = call(path.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "GetSecrets", "802-11-wireless-security"); - return response.value().value("802-11-wireless-security").value("psk").toString(); - } - return ""; -} - -void WifiManager::changeTetheringPassword(const QString &newPassword) { - const QDBusObjectPath &path = getConnectionPath(tethering_ssid); - if (!path.path().isEmpty()) { - Connection settings = getConnectionSettings(path); - settings["802-11-wireless-security"]["psk"] = newPassword; - call(path.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "Update", QVariant::fromValue(settings)); - if (isTetheringEnabled()) { - activateWifiConnection(tethering_ssid); - } - } -} diff --git a/selfdrive/ui/qt/network/wifi_manager.h b/selfdrive/ui/qt/network/wifi_manager.h deleted file mode 100644 index cab932a388..0000000000 --- a/selfdrive/ui/qt/network/wifi_manager.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "selfdrive/ui/qt/network/networkmanager.h" - -enum class SecurityType { - OPEN, - WPA, - UNSUPPORTED -}; -enum class ConnectedType { - DISCONNECTED, - CONNECTING, - CONNECTED -}; -enum class NetworkType { - NONE, - WIFI, - CELL, - ETHERNET -}; -enum class MeteredType { - UNKNOWN, - YES, - NO -}; - -typedef QMap Connection; -typedef QVector IpConfig; - -struct Network { - QByteArray ssid; - unsigned int strength; - ConnectedType connected; - SecurityType security_type; -}; -bool compare_by_strength(const Network &a, const Network &b); -inline int strengthLevel(unsigned int strength) { return std::clamp((int)round(strength / 33.), 0, 3); } - -class WifiManager : public QObject { - Q_OBJECT - -public: - QMap seenNetworks; - QMap knownConnections; - QString ipv4_address; - bool tethering_on = false; - bool ipv4_forward = false; - - explicit WifiManager(QObject* parent); - void start(); - void stop(); - void requestScan(); - void forgetConnection(const QString &ssid); - bool isKnownConnection(const QString &ssid); - std::optional activateWifiConnection(const QString &ssid); - NetworkType currentNetworkType(); - MeteredType currentNetworkMetered(); - std::optional setCurrentNetworkMetered(MeteredType metered); - void updateGsmSettings(bool roaming, QString apn, bool metered); - void connect(const Network &ssid, const bool is_hidden = false, const QString &password = {}, const QString &username = {}); - - // Tethering functions - void setTetheringEnabled(bool enabled); - bool isTetheringEnabled(); - void changeTetheringPassword(const QString &newPassword); - QString getTetheringPassword(); - -private: - QString adapter; // Path to network manager wifi-device - QTimer timer; - unsigned int raw_adapter_state = NM_DEVICE_STATE_UNKNOWN; // Connection status https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NMDeviceState - QString connecting_to_network; - QString tethering_ssid; - const QString defaultTetheringPassword = "swagswagcomma"; - QString activeAp; - QDBusObjectPath lteConnectionPath; - - QString getAdapter(const uint = NM_DEVICE_TYPE_WIFI); - uint getAdapterType(const QDBusObjectPath &path); - QString getIp4Address(); - void deactivateConnectionBySsid(const QString &ssid); - void deactivateConnection(const QDBusObjectPath &path); - QVector getActiveConnections(); - QByteArray get_property(const QString &network_path, const QString &property); - SecurityType getSecurityType(const QVariantMap &properties); - QDBusObjectPath getConnectionPath(const QString &ssid); - Connection getConnectionSettings(const QDBusObjectPath &path); - void initConnections(); - void setup(); - void refreshNetworks(); - void activateModemConnection(const QDBusObjectPath &path); - void addTetheringConnection(); - void setCurrentConnecting(const QString &ssid); - -signals: - void wrongPassword(const QString &ssid); - void refreshSignal(); - -private slots: - void stateChange(unsigned int new_state, unsigned int previous_state, unsigned int change_reason); - void propertyChange(const QString &interface, const QVariantMap &props, const QStringList &invalidated_props); - void deviceAdded(const QDBusObjectPath &path); - void connectionRemoved(const QDBusObjectPath &path); - void newConnection(const QDBusObjectPath &path); - void refreshFinished(QDBusPendingCallWatcher *call); - void tetheringActivated(QDBusPendingCallWatcher *call); -}; diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc deleted file mode 100644 index a095228da2..0000000000 --- a/selfdrive/ui/qt/offroad/developer_panel.cc +++ /dev/null @@ -1,95 +0,0 @@ -#include "selfdrive/ui/qt/offroad/developer_panel.h" -#include "selfdrive/ui/qt/widgets/ssh_keys.h" -#include "selfdrive/ui/qt/widgets/controls.h" - -DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) { - adbToggle = new ParamControl("AdbEnabled", tr("Enable ADB"), - tr("ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info."), ""); - addItem(adbToggle); - - // SSH keys - addItem(new SshToggle()); - addItem(new SshControl()); - - joystickToggle = new ParamControl("JoystickDebugMode", tr("Joystick Debug Mode"), "", ""); - QObject::connect(joystickToggle, &ParamControl::toggleFlipped, [=](bool state) { - params.putBool("LongitudinalManeuverMode", false); - longManeuverToggle->refresh(); - }); - addItem(joystickToggle); - - longManeuverToggle = new ParamControl("LongitudinalManeuverMode", tr("Longitudinal Maneuver Mode"), "", ""); - QObject::connect(longManeuverToggle, &ParamControl::toggleFlipped, [=](bool state) { - params.putBool("JoystickDebugMode", false); - joystickToggle->refresh(); - }); - addItem(longManeuverToggle); - - experimentalLongitudinalToggle = new ParamControl( - "AlphaLongitudinalEnabled", - tr("openpilot Longitudinal Control (Alpha)"), - QString("%1

%2") - .arg(tr("WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).")) - .arg(tr("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.")), - "" - ); - experimentalLongitudinalToggle->setConfirmation(true, false); - QObject::connect(experimentalLongitudinalToggle, &ParamControl::toggleFlipped, [=]() { - updateToggles(offroad); - }); - addItem(experimentalLongitudinalToggle); - - // Joystick and longitudinal maneuvers should be hidden on release branches - is_release = params.getBool("IsReleaseBranch"); - - // Toggles should be not available to change in onroad state - QObject::connect(uiState(), &UIState::offroadTransition, this, &DeveloperPanel::updateToggles); -} - -void DeveloperPanel::updateToggles(bool _offroad) { - for (auto btn : findChildren()) { - btn->setVisible(!is_release); - - /* - * experimentalLongitudinalToggle should be toggelable when: - * - visible, and - * - during onroad & offroad states - */ - if (btn != experimentalLongitudinalToggle) { - btn->setEnabled(_offroad); - } - } - - // longManeuverToggle and experimentalLongitudinalToggle should not be toggleable if the car does not have longitudinal control - auto cp_bytes = params.get("CarParamsPersistent"); - if (!cp_bytes.empty()) { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); - cereal::CarParams::Reader CP = cmsg.getRoot(); - - if (!CP.getAlphaLongitudinalAvailable() || is_release) { - params.remove("AlphaLongitudinalEnabled"); - experimentalLongitudinalToggle->setEnabled(false); - } - - /* - * experimentalLongitudinalToggle should be visible when: - * - is not a release branch, and - * - the car supports experimental longitudinal control (alpha) - */ - experimentalLongitudinalToggle->setVisible(CP.getAlphaLongitudinalAvailable() && !is_release); - - longManeuverToggle->setEnabled(hasLongitudinalControl(CP) && _offroad); - } else { - longManeuverToggle->setEnabled(false); - experimentalLongitudinalToggle->setVisible(false); - } - experimentalLongitudinalToggle->refresh(); - - offroad = _offroad; -} - -void DeveloperPanel::showEvent(QShowEvent *event) { - updateToggles(offroad); -} diff --git a/selfdrive/ui/qt/offroad/developer_panel.h b/selfdrive/ui/qt/offroad/developer_panel.h deleted file mode 100644 index c73421b185..0000000000 --- a/selfdrive/ui/qt/offroad/developer_panel.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "selfdrive/ui/qt/offroad/settings.h" - -class DeveloperPanel : public ListWidget { - Q_OBJECT -public: - explicit DeveloperPanel(SettingsWindow *parent); - void showEvent(QShowEvent *event) override; - -private: - Params params; - ParamControl* adbToggle; - ParamControl* joystickToggle; - ParamControl* longManeuverToggle; - ParamControl* experimentalLongitudinalToggle; - bool is_release; - bool offroad = false; - -private slots: - void updateToggles(bool _offroad); -}; diff --git a/selfdrive/ui/qt/offroad/driverview.cc b/selfdrive/ui/qt/offroad/driverview.cc deleted file mode 100644 index 9010227f18..0000000000 --- a/selfdrive/ui/qt/offroad/driverview.cc +++ /dev/null @@ -1,82 +0,0 @@ -#include "selfdrive/ui/qt/offroad/driverview.h" - -#include -#include - -#include "selfdrive/ui/qt/util.h" - -DriverViewWindow::DriverViewWindow(QWidget* parent) : CameraWidget("camerad", VISION_STREAM_DRIVER, parent) { - QObject::connect(this, &CameraWidget::clicked, this, &DriverViewWindow::done); - QObject::connect(device(), &Device::interactiveTimeout, this, [this]() { - if (isVisible()) { - emit done(); - } - }); -} - -void DriverViewWindow::showEvent(QShowEvent* event) { - params.putBool("IsDriverViewEnabled", true); - device()->resetInteractiveTimeout(60); - CameraWidget::showEvent(event); -} - -void DriverViewWindow::hideEvent(QHideEvent* event) { - params.putBool("IsDriverViewEnabled", false); - stopVipcThread(); - CameraWidget::hideEvent(event); -} - -void DriverViewWindow::paintGL() { - CameraWidget::paintGL(); - - std::lock_guard lk(frame_lock); - QPainter p(this); - // startup msg - if (frames.empty()) { - p.setPen(Qt::white); - p.setRenderHint(QPainter::TextAntialiasing); - p.setFont(InterFont(100, QFont::Bold)); - p.drawText(geometry(), Qt::AlignCenter, tr("camera starting")); - return; - } - - const auto &sm = *(uiState()->sm); - cereal::DriverStateV2::Reader driver_state = sm["driverStateV2"].getDriverStateV2(); - bool is_rhd = driver_state.getWheelOnRightProb() > 0.5; - auto driver_data = is_rhd ? driver_state.getRightDriverData() : driver_state.getLeftDriverData(); - - bool face_detected = driver_data.getFaceProb() > 0.7; - if (face_detected) { - auto fxy_list = driver_data.getFacePosition(); - auto std_list = driver_data.getFaceOrientationStd(); - float face_x = fxy_list[0]; - float face_y = fxy_list[1]; - float face_std = std::max(std_list[0], std_list[1]); - - float alpha = 0.7; - if (face_std > 0.15) { - alpha = std::max(0.7 - (face_std-0.15)*3.5, 0.0); - } - const int box_size = 220; - // use approx instead of distort_points - int fbox_x = 1080.0 - 1714.0 * face_x; - int fbox_y = -135.0 + (504.0 + std::abs(face_x)*112.0) + (1205.0 - std::abs(face_x)*724.0) * face_y; - p.setPen(QPen(QColor(255, 255, 255, alpha * 255), 10)); - p.drawRoundedRect(fbox_x - box_size / 2, fbox_y - box_size / 2, box_size, box_size, 35.0, 35.0); - } - - driver_monitor.updateState(*uiState()); - driver_monitor.draw(p, rect()); -} - -mat4 DriverViewWindow::calcFrameMatrix() { - const float driver_view_ratio = 2.0; - const float yscale = stream_height * driver_view_ratio / stream_width; - const float xscale = yscale * glHeight() / glWidth() * stream_width / stream_height; - return mat4{{ - xscale, 0.0, 0.0, 0.0, - 0.0, yscale, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, - }}; -} diff --git a/selfdrive/ui/qt/offroad/driverview.h b/selfdrive/ui/qt/offroad/driverview.h deleted file mode 100644 index f6eb752fe6..0000000000 --- a/selfdrive/ui/qt/offroad/driverview.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "selfdrive/ui/qt/widgets/cameraview.h" -#include "selfdrive/ui/qt/onroad/driver_monitoring.h" - -class DriverViewWindow : public CameraWidget { - Q_OBJECT - -public: - explicit DriverViewWindow(QWidget *parent); - -signals: - void done(); - -protected: - mat4 calcFrameMatrix() override; - void showEvent(QShowEvent *event) override; - void hideEvent(QHideEvent *event) override; - void paintGL() override; - - Params params; - DriverMonitorRenderer driver_monitor; -}; diff --git a/selfdrive/ui/qt/offroad/experimental_mode.cc b/selfdrive/ui/qt/offroad/experimental_mode.cc deleted file mode 100644 index e255073f39..0000000000 --- a/selfdrive/ui/qt/offroad/experimental_mode.cc +++ /dev/null @@ -1,76 +0,0 @@ -#include "selfdrive/ui/qt/offroad/experimental_mode.h" - -#include -#include -#include -#include -#include - -#include "selfdrive/ui/ui.h" - -ExperimentalModeButton::ExperimentalModeButton(QWidget *parent) : QPushButton(parent) { - chill_pixmap = QPixmap("../assets/icons/couch.svg").scaledToWidth(img_width, Qt::SmoothTransformation); - experimental_pixmap = QPixmap("../assets/icons/experimental_grey.svg").scaledToWidth(img_width, Qt::SmoothTransformation); - - // go to toggles and expand experimental mode description - connect(this, &QPushButton::clicked, [=]() { emit openSettings(2, "ExperimentalMode"); }); - - setFixedHeight(125); - QHBoxLayout *main_layout = new QHBoxLayout; - main_layout->setContentsMargins(horizontal_padding, 0, horizontal_padding, 0); - - mode_label = new QLabel; - mode_icon = new QLabel; - mode_icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - - main_layout->addWidget(mode_label, 1, Qt::AlignLeft); - main_layout->addWidget(mode_icon, 0, Qt::AlignRight); - - setLayout(main_layout); - - setStyleSheet(R"( - QPushButton { - border: none; - } - - QLabel { - font-size: 45px; - font-weight: 300; - text-align: left; - font-family: JetBrainsMono; - color: #000000; - } - )"); -} - -void ExperimentalModeButton::paintEvent(QPaintEvent *event) { - QPainter p(this); - p.setPen(Qt::NoPen); - p.setRenderHint(QPainter::Antialiasing); - - QPainterPath path; - path.addRoundedRect(rect(), 10, 10); - - // gradient - bool pressed = isDown(); - QLinearGradient gradient(rect().left(), 0, rect().right(), 0); - if (experimental_mode) { - gradient.setColorAt(0, QColor(255, 155, 63, pressed ? 0xcc : 0xff)); - gradient.setColorAt(1, QColor(219, 56, 34, pressed ? 0xcc : 0xff)); - } else { - gradient.setColorAt(0, QColor(20, 255, 171, pressed ? 0xcc : 0xff)); - gradient.setColorAt(1, QColor(35, 149, 255, pressed ? 0xcc : 0xff)); - } - p.fillPath(path, gradient); - - // vertical line - p.setPen(QPen(QColor(0, 0, 0, 0x4d), 3, Qt::SolidLine)); - int line_x = rect().right() - img_width - (2 * horizontal_padding); - p.drawLine(line_x, rect().bottom(), line_x, rect().top()); -} - -void ExperimentalModeButton::showEvent(QShowEvent *event) { - experimental_mode = params.getBool("ExperimentalMode"); - mode_icon->setPixmap(experimental_mode ? experimental_pixmap : chill_pixmap); - mode_label->setText(experimental_mode ? tr("EXPERIMENTAL MODE ON") : tr("CHILL MODE ON")); -} diff --git a/selfdrive/ui/qt/offroad/experimental_mode.h b/selfdrive/ui/qt/offroad/experimental_mode.h deleted file mode 100644 index bfb7638bbe..0000000000 --- a/selfdrive/ui/qt/offroad/experimental_mode.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - -#include "common/params.h" - -class ExperimentalModeButton : public QPushButton { - Q_OBJECT - -public: - explicit ExperimentalModeButton(QWidget* parent = 0); - -signals: - void openSettings(int index = 0, const QString &toggle = ""); - -private: - void showEvent(QShowEvent *event) override; - - Params params; - bool experimental_mode; - int img_width = 100; - int horizontal_padding = 30; - QPixmap experimental_pixmap; - QPixmap chill_pixmap; - QLabel *mode_label; - QLabel *mode_icon; - -protected: - void paintEvent(QPaintEvent *event) override; -}; diff --git a/selfdrive/ui/qt/offroad/firehose.cc b/selfdrive/ui/qt/offroad/firehose.cc deleted file mode 100644 index aa158b4c7b..0000000000 --- a/selfdrive/ui/qt/offroad/firehose.cc +++ /dev/null @@ -1,112 +0,0 @@ -#include "selfdrive/ui/qt/offroad/firehose.h" -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/offroad/settings.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) { - layout = new QVBoxLayout(this); - layout->setContentsMargins(40, 40, 40, 40); - layout->setSpacing(20); - - // header - QLabel *title = new QLabel(tr("Firehose Mode")); - title->setStyleSheet("font-size: 100px; font-weight: 500; font-family: 'Noto Color Emoji';"); - layout->addWidget(title, 0, Qt::AlignCenter); - - // Create a container for the content - QFrame *content = new QFrame(); - content->setStyleSheet("background-color: #292929; border-radius: 15px; padding: 20px;"); - QVBoxLayout *content_layout = new QVBoxLayout(content); - content_layout->setSpacing(20); - - // Top description - QLabel *description = new QLabel(tr("openpilot learns to drive by watching humans, like you, drive.\n\nFirehose 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.")); - description->setStyleSheet("font-size: 45px; padding-bottom: 20px;"); - description->setWordWrap(true); - content_layout->addWidget(description); - - // Add a separator - QFrame *line = new QFrame(); - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - line->setStyleSheet("background-color: #444444; margin-top: 5px; margin-bottom: 5px;"); - content_layout->addWidget(line); - - toggle_label = new QLabel(tr("Firehose Mode: ACTIVE")); - toggle_label->setStyleSheet("font-size: 60px; font-weight: bold; color: white;"); - content_layout->addWidget(toggle_label); - - // Add contribution label - contribution_label = new QLabel(); - contribution_label->setStyleSheet("font-size: 52px; margin-top: 10px; margin-bottom: 10px;"); - contribution_label->setWordWrap(true); - contribution_label->hide(); - content_layout->addWidget(contribution_label); - - // Add a separator before detailed instructions - QFrame *line2 = new QFrame(); - line2->setFrameShape(QFrame::HLine); - line2->setFrameShadow(QFrame::Sunken); - line2->setStyleSheet("background-color: #444444; margin-top: 10px; margin-bottom: 10px;"); - content_layout->addWidget(line2); - - // Detailed instructions at the bottom - detailed_instructions = new QLabel(tr( - "For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.
" - "
" - "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.
" - "

" - "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." - )); - detailed_instructions->setStyleSheet("font-size: 40px; color: #E4E4E4;"); - detailed_instructions->setWordWrap(true); - content_layout->addWidget(detailed_instructions); - - layout->addWidget(content, 1); - - // Set up the API request for firehose stats - const QString dongle_id = QString::fromStdString(Params().get("DongleId")); - firehose_stats = new RequestRepeater(this, CommaApi::BASE_URL + "/v1/devices/" + dongle_id + "/firehose_stats", - "ApiCache_FirehoseStats", 30, true); - QObject::connect(firehose_stats, &RequestRepeater::requestDone, [=](const QString &response, bool success) { - if (success) { - QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); - QJsonObject json = doc.object(); - int count = json["firehose"].toInt(); - contribution_label->setText(tr("%n segment(s) of your driving is in the training dataset so far.", "", count)); - contribution_label->show(); - } - }); - - QObject::connect(uiState(), &UIState::uiUpdate, this, &FirehosePanel::refresh); -} - -void FirehosePanel::refresh() { - auto deviceState = (*uiState()->sm)["deviceState"].getDeviceState(); - auto networkType = deviceState.getNetworkType(); - bool networkMetered = deviceState.getNetworkMetered(); - - bool is_active = !networkMetered && (networkType != cereal::DeviceState::NetworkType::NONE); - if (is_active) { - toggle_label->setText(tr("ACTIVE")); - toggle_label->setStyleSheet("font-size: 60px; font-weight: bold; color: #2ecc71;"); - } else { - toggle_label->setText(tr("INACTIVE: connect to an unmetered network")); - toggle_label->setStyleSheet("font-size: 60px;"); - } -} diff --git a/selfdrive/ui/qt/offroad/firehose.h b/selfdrive/ui/qt/offroad/firehose.h deleted file mode 100644 index 9082cb0f99..0000000000 --- a/selfdrive/ui/qt/offroad/firehose.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include -#include "selfdrive/ui/qt/request_repeater.h" - -// Forward declarations -class SettingsWindow; - -class FirehosePanel : public QWidget { - Q_OBJECT -public: - explicit FirehosePanel(SettingsWindow *parent); - -private: - QVBoxLayout *layout; - - QLabel *detailed_instructions; - QLabel *contribution_label; - QLabel *toggle_label; - - RequestRepeater *firehose_stats; - -private slots: - void refresh(); -}; diff --git a/selfdrive/ui/qt/offroad/onboarding.cc b/selfdrive/ui/qt/offroad/onboarding.cc deleted file mode 100644 index 1390566671..0000000000 --- a/selfdrive/ui/qt/offroad/onboarding.cc +++ /dev/null @@ -1,211 +0,0 @@ -#include "selfdrive/ui/qt/offroad/onboarding.h" - -#include - -#include -#include -#include -#include - -#include "common/util.h" -#include "common/params.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/input.h" - -TrainingGuide::TrainingGuide(QWidget *parent) : QFrame(parent) { - setAttribute(Qt::WA_OpaquePaintEvent); -} - -void TrainingGuide::mouseReleaseEvent(QMouseEvent *e) { - if (click_timer.elapsed() < 250) { - return; - } - click_timer.restart(); - - auto contains = [this](QRect r, const QPoint &pt) { - if (image.size() != image_raw_size) { - QTransform transform; - transform.translate((width()- image.width()) / 2.0, (height()- image.height()) / 2.0); - transform.scale(image.width() / (float)image_raw_size.width(), image.height() / (float)image_raw_size.height()); - r= transform.mapRect(r); - } - return r.contains(pt); - }; - - if (contains(boundingRect[currentIndex], e->pos())) { - if (currentIndex == 9) { - const QRect yes = QRect(707, 804, 531, 164); - Params().putBool("RecordFront", contains(yes, e->pos())); - } - currentIndex += 1; - } else if (currentIndex == (boundingRect.size() - 2) && contains(boundingRect.last(), e->pos())) { - currentIndex = 0; - } - - if (currentIndex >= (boundingRect.size() - 1)) { - emit completedTraining(); - } else { - update(); - } -} - -void TrainingGuide::showEvent(QShowEvent *event) { - currentIndex = 0; - click_timer.start(); -} - -QImage TrainingGuide::loadImage(int id) { - QImage img(img_path + QString("step%1.png").arg(id)); - image_raw_size = img.size(); - if (image_raw_size != rect().size()) { - img = img.scaled(width(), height(), Qt::KeepAspectRatio, Qt::SmoothTransformation); - } - return img; -} - -void TrainingGuide::paintEvent(QPaintEvent *event) { - QPainter painter(this); - - QRect bg(0, 0, painter.device()->width(), painter.device()->height()); - painter.fillRect(bg, QColor("#000000")); - - image = loadImage(currentIndex); - QRect rect(image.rect()); - rect.moveCenter(bg.center()); - painter.drawImage(rect.topLeft(), image); - - // progress bar - if (currentIndex > 0 && currentIndex < (boundingRect.size() - 2)) { - const int h = 20; - const int w = (currentIndex / (float)(boundingRect.size() - 2)) * width(); - painter.fillRect(QRect(0, height() - h, w, h), QColor("#465BEA")); - } -} - -void TermsPage::showEvent(QShowEvent *event) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(45, 35, 45, 45); - main_layout->setSpacing(0); - - QVBoxLayout *vlayout = new QVBoxLayout(); - vlayout->setContentsMargins(165, 165, 165, 0); - main_layout->addLayout(vlayout); - - QLabel *title = new QLabel(tr("Welcome to openpilot")); - title->setStyleSheet("font-size: 90px; font-weight: 500;"); - vlayout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); - - vlayout->addSpacing(90); - QLabel *desc = new QLabel(tr("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing.")); - desc->setWordWrap(true); - desc->setStyleSheet("font-size: 80px; font-weight: 300;"); - vlayout->addWidget(desc, 0); - - vlayout->addStretch(); - - QHBoxLayout* buttons = new QHBoxLayout; - buttons->setMargin(0); - buttons->setSpacing(45); - main_layout->addLayout(buttons); - - QPushButton *decline_btn = new QPushButton(tr("Decline")); - buttons->addWidget(decline_btn); - QObject::connect(decline_btn, &QPushButton::clicked, this, &TermsPage::declinedTerms); - - accept_btn = new QPushButton(tr("Agree")); - accept_btn->setStyleSheet(R"( - QPushButton { - background-color: #465BEA; - } - QPushButton:pressed { - background-color: #3049F4; - } - )"); - buttons->addWidget(accept_btn); - QObject::connect(accept_btn, &QPushButton::clicked, this, &TermsPage::acceptedTerms); -} - -void DeclinePage::showEvent(QShowEvent *event) { - if (layout()) { - return; - } - - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setMargin(45); - main_layout->setSpacing(40); - - QLabel *text = new QLabel(this); - text->setText(tr("You must accept the Terms and Conditions in order to use openpilot.")); - text->setStyleSheet(R"(font-size: 80px; font-weight: 300; margin: 200px;)"); - text->setWordWrap(true); - main_layout->addWidget(text, 0, Qt::AlignCenter); - - QHBoxLayout* buttons = new QHBoxLayout; - buttons->setSpacing(45); - main_layout->addLayout(buttons); - - QPushButton *back_btn = new QPushButton(tr("Back")); - buttons->addWidget(back_btn); - - QObject::connect(back_btn, &QPushButton::clicked, this, &DeclinePage::getBack); - - QPushButton *uninstall_btn = new QPushButton(tr("Decline, uninstall %1").arg(getBrand())); - uninstall_btn->setStyleSheet("background-color: #B73D3D"); - buttons->addWidget(uninstall_btn); - QObject::connect(uninstall_btn, &QPushButton::clicked, [=]() { - Params().putBool("DoUninstall", true); - }); -} - -void OnboardingWindow::updateActiveScreen() { - if (!accepted_terms) { - setCurrentIndex(0); - } else if (!training_done) { - setCurrentIndex(1); - } else { - emit onboardingDone(); - } -} - -OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) { - std::string current_terms_version = params.get("TermsVersion"); - std::string current_training_version = params.get("TrainingVersion"); - accepted_terms = params.get("HasAcceptedTerms") == current_terms_version; - training_done = params.get("CompletedTrainingVersion") == current_training_version; - - TermsPage* terms = new TermsPage(this); - addWidget(terms); - connect(terms, &TermsPage::acceptedTerms, [=]() { - params.put("HasAcceptedTerms", current_terms_version); - accepted_terms = true; - updateActiveScreen(); - }); - connect(terms, &TermsPage::declinedTerms, [=]() { setCurrentIndex(2); }); - - TrainingGuide* tr = new TrainingGuide(this); - addWidget(tr); - connect(tr, &TrainingGuide::completedTraining, [=]() { - training_done = true; - params.put("CompletedTrainingVersion", current_training_version); - updateActiveScreen(); - }); - - DeclinePage* declinePage = new DeclinePage(this); - addWidget(declinePage); - connect(declinePage, &DeclinePage::getBack, [=]() { updateActiveScreen(); }); - - setStyleSheet(R"( - * { - color: white; - background-color: black; - } - QPushButton { - height: 160px; - font-size: 55px; - font-weight: 400; - border-radius: 10px; - background-color: #4F4F4F; - } - )"); - updateActiveScreen(); -} diff --git a/selfdrive/ui/qt/offroad/onboarding.h b/selfdrive/ui/qt/offroad/onboarding.h deleted file mode 100644 index db229c5fa6..0000000000 --- a/selfdrive/ui/qt/offroad/onboarding.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "common/params.h" -#include "selfdrive/ui/qt/qt_window.h" - -class TrainingGuide : public QFrame { - Q_OBJECT - -public: - explicit TrainingGuide(QWidget *parent = 0); - -private: - void showEvent(QShowEvent *event) override; - void paintEvent(QPaintEvent *event) override; - void mouseReleaseEvent(QMouseEvent* e) override; - QImage loadImage(int id); - - QImage image; - QSize image_raw_size; - int currentIndex = 0; - - // Bounding boxes for each training guide step - const QRect continueBtn = {1840, 0, 320, 1080}; - QVector boundingRect { - QRect(112, 804, 618, 164), - continueBtn, - continueBtn, - QRect(1641, 558, 210, 313), - QRect(1662, 528, 184, 108), - continueBtn, - QRect(1814, 621, 211, 170), - QRect(1350, 0, 497, 755), - QRect(1540, 386, 468, 238), - QRect(112, 804, 1126, 164), - QRect(1598, 199, 316, 333), - continueBtn, - QRect(1364, 90, 796, 990), - continueBtn, - QRect(1593, 114, 318, 853), - QRect(1379, 511, 391, 243), - continueBtn, - continueBtn, - QRect(630, 804, 626, 164), - QRect(108, 804, 426, 164), - }; - - const QString img_path = "../assets/training/"; - QElapsedTimer click_timer; - -signals: - void completedTraining(); -}; - - -class TermsPage : public QFrame { - Q_OBJECT - -public: - explicit TermsPage(QWidget *parent = 0) : QFrame(parent) {} - -private: - void showEvent(QShowEvent *event) override; - - QPushButton *accept_btn; - -signals: - void acceptedTerms(); - void declinedTerms(); -}; - -class DeclinePage : public QFrame { - Q_OBJECT - -public: - explicit DeclinePage(QWidget *parent = 0) : QFrame(parent) {} - -private: - void showEvent(QShowEvent *event) override; - -signals: - void getBack(); -}; - -class OnboardingWindow : public QStackedWidget { - Q_OBJECT - -public: - explicit OnboardingWindow(QWidget *parent = 0); - inline void showTrainingGuide() { setCurrentIndex(1); } - inline bool completed() const { return accepted_terms && training_done; } - -private: - void updateActiveScreen(); - - Params params; - bool accepted_terms = false, training_done = false; - -signals: - void onboardingDone(); -}; diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc deleted file mode 100644 index 6eb8693a1a..0000000000 --- a/selfdrive/ui/qt/offroad/settings.cc +++ /dev/null @@ -1,535 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include "common/util.h" -#include "selfdrive/ui/qt/network/networking.h" -#include "selfdrive/ui/qt/offroad/settings.h" -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/widgets/prime.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" -#include "selfdrive/ui/qt/offroad/developer_panel.h" -#include "selfdrive/ui/qt/offroad/firehose.h" - -TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { - // param, title, desc, icon, restart needed - std::vector> toggle_defs{ - { - "OpenpilotEnabledToggle", - tr("Enable openpilot"), - tr("Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature."), - "../assets/icons/chffr_wheel.png", - true, - }, - { - "ExperimentalMode", - tr("Experimental Mode"), - "", - "../assets/icons/experimental_white.svg", - false, - }, - { - "DisengageOnAccelerator", - tr("Disengage on Accelerator Pedal"), - tr("When enabled, pressing the accelerator pedal will disengage openpilot."), - "../assets/icons/disengage_on_accelerator.svg", - false, - }, - { - "IsLdwEnabled", - tr("Enable Lane Departure Warnings"), - tr("Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h)."), - "../assets/icons/warning.png", - false, - }, - { - "AlwaysOnDM", - tr("Always-On Driver Monitoring"), - tr("Enable driver monitoring even when openpilot is not engaged."), - "../assets/icons/monitoring.png", - false, - }, - { - "RecordFront", - tr("Record and Upload Driver Camera"), - tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), - "../assets/icons/monitoring.png", - true, - }, - { - "RecordAudio", - tr("Record and Upload Microphone Audio"), - tr("Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect."), - "../assets/icons/microphone.png", - true, - }, - { - "IsMetric", - tr("Use Metric System"), - tr("Display speed in km/h instead of mph."), - "../assets/icons/metric.png", - false, - }, - }; - - - std::vector longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")}; - long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"), - tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " - "In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " - "your steering wheel distance button."), - "../assets/icons/speed_limit.png", - longi_button_texts); - - // set up uiState update for personality setting - QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState); - - for (auto &[param, title, desc, icon, needs_restart] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); - - bool locked = params.getBool((param + "Lock").toStdString()); - toggle->setEnabled(!locked); - - if (needs_restart && !locked) { - toggle->setDescription(toggle->getDescription() + tr(" Changing this setting will restart openpilot if the car is powered on.")); - - QObject::connect(uiState(), &UIState::engagedChanged, [toggle](bool engaged) { - toggle->setEnabled(!engaged); - }); - - QObject::connect(toggle, &ParamControl::toggleFlipped, [=](bool state) { - params.putBool("OnroadCycleRequested", true); - }); - } - - addItem(toggle); - toggles[param.toStdString()] = toggle; - - // insert longitudinal personality after NDOG toggle - if (param == "DisengageOnAccelerator") { - addItem(long_personality_setting); - } - } - - // Toggles with confirmation dialogs - toggles["ExperimentalMode"]->setActiveIcon("../assets/icons/experimental.svg"); - toggles["ExperimentalMode"]->setConfirmation(true, true); -} - -void TogglesPanel::updateState(const UIState &s) { - const SubMaster &sm = *(s.sm); - - if (sm.updated("selfdriveState")) { - auto personality = sm["selfdriveState"].getSelfdriveState().getPersonality(); - if (personality != s.scene.personality && s.scene.started && isVisible()) { - long_personality_setting->setCheckedButton(static_cast(personality)); - } - uiState()->scene.personality = personality; - } -} - -void TogglesPanel::expandToggleDescription(const QString ¶m) { - toggles[param.toStdString()]->showDescription(); -} - -void TogglesPanel::scrollToToggle(const QString ¶m) { - if (auto it = toggles.find(param.toStdString()); it != toggles.end()) { - auto scroll_area = qobject_cast(parent()->parent()); - if (scroll_area) { - scroll_area->ensureWidgetVisible(it->second); - } - } -} - -void TogglesPanel::showEvent(QShowEvent *event) { - updateToggles(); -} - -void TogglesPanel::updateToggles() { - auto experimental_mode_toggle = toggles["ExperimentalMode"]; - const QString e2e_description = QString("%1
" - "

%2


" - "%3
" - "

%4


" - "%5
") - .arg(tr("openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. Experimental features are listed below:")) - .arg(tr("End-to-End Longitudinal Control")) - .arg(tr("Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. " - "Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; " - "mistakes should be expected.")) - .arg(tr("New Driving Visualization")) - .arg(tr("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.")); - - const bool is_release = params.getBool("IsReleaseBranch"); - auto cp_bytes = params.get("CarParamsPersistent"); - if (!cp_bytes.empty()) { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); - cereal::CarParams::Reader CP = cmsg.getRoot(); - - if (hasLongitudinalControl(CP)) { - // normal description and toggle - experimental_mode_toggle->setEnabled(true); - experimental_mode_toggle->setDescription(e2e_description); - long_personality_setting->setEnabled(true); - } else { - // no long for now - experimental_mode_toggle->setEnabled(false); - long_personality_setting->setEnabled(false); - params.remove("ExperimentalMode"); - - const QString unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control."); - - QString long_desc = unavailable + " " + \ - tr("openpilot longitudinal control may come in a future update."); - if (CP.getAlphaLongitudinalAvailable()) { - if (is_release) { - long_desc = unavailable + " " + tr("An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches."); - } else { - long_desc = tr("Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode."); - } - } - experimental_mode_toggle->setDescription("" + long_desc + "

" + e2e_description); - } - - experimental_mode_toggle->refresh(); - } else { - experimental_mode_toggle->setDescription(e2e_description); - } -} - -DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { - setSpacing(50); - addItem(new LabelControl(tr("Dongle ID"), getDongleId().value_or(tr("N/A")))); - addItem(new LabelControl(tr("Serial"), params.get("HardwareSerial").c_str())); - - pair_device = new ButtonControl(tr("Pair Device"), tr("PAIR"), - tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.")); - connect(pair_device, &ButtonControl::clicked, [=]() { - PairingPopup popup(this); - popup.exec(); - }); - addItem(pair_device); - - // offroad-only buttons - - auto dcamBtn = new ButtonControl(tr("Driver Camera"), tr("PREVIEW"), - tr("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)")); - connect(dcamBtn, &ButtonControl::clicked, [=]() { emit showDriverView(); }); - addItem(dcamBtn); - - resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), ""); - connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, this, &DevicePanel::updateCalibDescription); - connect(resetCalibBtn, &ButtonControl::clicked, [&]() { - if (!uiState()->engaged()) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) { - // Check engaged again in case it changed while the dialog was open - if (!uiState()->engaged()) { - params.remove("CalibrationParams"); - params.remove("LiveTorqueParameters"); - params.remove("LiveParameters"); - params.remove("LiveParametersV2"); - params.remove("LiveDelay"); - params.putBool("OnroadCycleRequested", true); - updateCalibDescription(); - } - } - } else { - ConfirmationDialog::alert(tr("Disengage to Reset Calibration"), this); - } - }); - addItem(resetCalibBtn); - - auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot")); - connect(retrainingBtn, &ButtonControl::clicked, [=]() { - if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) { - emit reviewTrainingGuide(); - } - }); - addItem(retrainingBtn); - - if (Hardware::TICI()) { - auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), ""); - connect(regulatoryBtn, &ButtonControl::clicked, [=]() { - const std::string txt = util::read_file("../assets/offroad/fcc.html"); - ConfirmationDialog::rich(QString::fromStdString(txt), this); - }); - addItem(regulatoryBtn); - } - - auto translateBtn = new ButtonControl(tr("Change Language"), tr("CHANGE"), ""); - connect(translateBtn, &ButtonControl::clicked, [=]() { - QMap langs = getSupportedLanguages(); - QString selection = MultiOptionDialog::getSelection(tr("Select a language"), langs.keys(), langs.key(uiState()->language), this); - if (!selection.isEmpty()) { - // put language setting, exit Qt UI, and trigger fast restart - params.put("LanguageSetting", langs[selection].toStdString()); - qApp->exit(18); - } - }); - addItem(translateBtn); - - QObject::connect(uiState()->prime_state, &PrimeState::changed, [this] (PrimeState::Type type) { - pair_device->setVisible(type == PrimeState::PRIME_TYPE_UNPAIRED); - }); - QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { - for (auto btn : findChildren()) { - if (btn != pair_device && btn != resetCalibBtn) { - btn->setEnabled(offroad); - } - } - }); - - // power buttons - QHBoxLayout *power_layout = new QHBoxLayout(); - power_layout->setSpacing(30); - - QPushButton *reboot_btn = new QPushButton(tr("Reboot")); - reboot_btn->setObjectName("reboot_btn"); - power_layout->addWidget(reboot_btn); - QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot); - - QPushButton *poweroff_btn = new QPushButton(tr("Power Off")); - poweroff_btn->setObjectName("poweroff_btn"); - power_layout->addWidget(poweroff_btn); - QObject::connect(poweroff_btn, &QPushButton::clicked, this, &DevicePanel::poweroff); - - if (!Hardware::PC()) { - connect(uiState(), &UIState::offroadTransition, poweroff_btn, &QPushButton::setVisible); - } - - setStyleSheet(R"( - #reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; } - #reboot_btn:pressed { background-color: #4a4a4a; } - #poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; } - #poweroff_btn:pressed { background-color: #FF2424; } - )"); - addItem(power_layout); -} - -void DevicePanel::updateCalibDescription() { - QString desc = tr("openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down."); - std::string calib_bytes = params.get("CalibrationParams"); - if (!calib_bytes.empty()) { - try { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size())); - auto calib = cmsg.getRoot().getLiveCalibration(); - if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) { - double pitch = calib.getRpyCalib()[1] * (180 / M_PI); - double yaw = calib.getRpyCalib()[2] * (180 / M_PI); - desc += tr(" Your device is pointed %1° %2 and %3° %4.") - .arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? tr("down") : tr("up"), - QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? tr("left") : tr("right")); - } - } catch (kj::Exception) { - qInfo() << "invalid CalibrationParams"; - } - } - - int lag_perc = 0; - std::string lag_bytes = params.get("LiveDelay"); - if (!lag_bytes.empty()) { - try { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(lag_bytes.data(), lag_bytes.size())); - lag_perc = cmsg.getRoot().getLiveDelay().getCalPerc(); - } catch (kj::Exception) { - qInfo() << "invalid LiveDelay"; - } - } - if (lag_perc < 100) { - desc += tr("\n\nSteering lag calibration is %1% complete.").arg(lag_perc); - } else { - desc += tr("\n\nSteering lag calibration is complete."); - } - - std::string torque_bytes = params.get("LiveTorqueParameters"); - if (!torque_bytes.empty()) { - try { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(torque_bytes.data(), torque_bytes.size())); - auto torque = cmsg.getRoot().getLiveTorqueParameters(); - // don't add for non-torque cars - if (torque.getUseParams()) { - int torque_perc = torque.getCalPerc(); - if (torque_perc < 100) { - desc += tr(" Steering torque response calibration is %1% complete.").arg(torque_perc); - } else { - desc += tr(" Steering torque response calibration is complete."); - } - } - } catch (kj::Exception) { - qInfo() << "invalid LiveTorqueParameters"; - } - } - - desc += "\n\n"; - desc += tr("openpilot is continuously calibrating, resetting is rarely required. " - "Resetting calibration will restart openpilot if the car is powered on."); - resetCalibBtn->setDescription(desc); -} - -void DevicePanel::reboot() { - if (!uiState()->engaged()) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot?"), tr("Reboot"), this)) { - // Check engaged again in case it changed while the dialog was open - if (!uiState()->engaged()) { - params.putBool("DoReboot", true); - } - } - } else { - ConfirmationDialog::alert(tr("Disengage to Reboot"), this); - } -} - -void DevicePanel::poweroff() { - if (!uiState()->engaged()) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) { - // Check engaged again in case it changed while the dialog was open - if (!uiState()->engaged()) { - params.putBool("DoShutdown", true); - } - } - } else { - ConfirmationDialog::alert(tr("Disengage to Power Off"), this); - } -} - -void SettingsWindow::showEvent(QShowEvent *event) { - setCurrentPanel(0); -} - -void SettingsWindow::setCurrentPanel(int index, const QString ¶m) { - if (!param.isEmpty()) { - // Check if param ends with "Panel" to determine if it's a panel name - if (param.endsWith("Panel")) { - QString panelName = param; - panelName.chop(5); // Remove "Panel" suffix - - // 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())) { - index = i; - break; - } - } - } else { - emit expandToggleDescription(param); - emit scrollToToggle(param); - } - } - - panel_widget->setCurrentIndex(index); - nav_btns->buttons()[index]->setChecked(true); -} - -SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { - - // setup two main layouts - sidebar_widget = new QWidget; - QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); - panel_widget = new QStackedWidget(); - - // close button - QPushButton *close_btn = new QPushButton(tr("×")); - close_btn->setStyleSheet(R"( - QPushButton { - font-size: 140px; - padding-bottom: 20px; - border-radius: 100px; - background-color: #292929; - font-weight: 400; - } - QPushButton:pressed { - background-color: #3B3B3B; - } - )"); - close_btn->setFixedSize(200, 200); - sidebar_layout->addSpacing(45); - sidebar_layout->addWidget(close_btn, 0, Qt::AlignCenter); - QObject::connect(close_btn, &QPushButton::clicked, this, &SettingsWindow::closeSettings); - - // setup panels - DevicePanel *device = new DevicePanel(this); - QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide); - QObject::connect(device, &DevicePanel::showDriverView, this, &SettingsWindow::showDriverView); - - TogglesPanel *toggles = new TogglesPanel(this); - QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); - QObject::connect(this, &SettingsWindow::scrollToToggle, toggles, &TogglesPanel::scrollToToggle); - - auto networking = new Networking(this); - QObject::connect(uiState()->prime_state, &PrimeState::changed, networking, &Networking::setPrimeType); - - QList> panels = { - {tr("Device"), device}, - {tr("Network"), networking}, - {tr("Toggles"), toggles}, - {tr("Software"), new SoftwarePanel(this)}, - {tr("Firehose"), new FirehosePanel(this)}, - {tr("Developer"), new DeveloperPanel(this)}, - }; - - nav_btns = new QButtonGroup(this); - for (auto &[name, panel] : panels) { - QPushButton *btn = new QPushButton(name); - btn->setCheckable(true); - btn->setChecked(nav_btns->buttons().size() == 0); - btn->setStyleSheet(R"( - QPushButton { - color: grey; - border: none; - background: none; - font-size: 65px; - font-weight: 500; - } - QPushButton:checked { - color: white; - } - QPushButton:pressed { - color: #ADADAD; - } - )"); - btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - nav_btns->addButton(btn); - sidebar_layout->addWidget(btn, 0, Qt::AlignRight); - - const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins - panel->setContentsMargins(lr_margin, 25, lr_margin, 25); - - ScrollView *panel_frame = new ScrollView(panel, this); - panel_widget->addWidget(panel_frame); - - QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { - btn->setChecked(true); - panel_widget->setCurrentWidget(w); - }); - } - sidebar_layout->setContentsMargins(50, 50, 100, 50); - - // main settings layout, sidebar + main panel - QHBoxLayout *main_layout = new QHBoxLayout(this); - - sidebar_widget->setFixedWidth(500); - main_layout->addWidget(sidebar_widget); - main_layout->addWidget(panel_widget); - - setStyleSheet(R"( - * { - color: white; - font-size: 50px; - } - SettingsWindow { - background-color: black; - } - QStackedWidget, ScrollView { - background-color: #292929; - border-radius: 30px; - } - )"); -} diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h deleted file mode 100644 index d52cf16bb7..0000000000 --- a/selfdrive/ui/qt/offroad/settings.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/controls.h" - -// ********** settings window + top-level panels ********** -class SettingsWindow : public QFrame { - Q_OBJECT - -public: - explicit SettingsWindow(QWidget *parent = 0); - void setCurrentPanel(int index, const QString ¶m = ""); - -protected: - void showEvent(QShowEvent *event) override; - -signals: - void closeSettings(); - void reviewTrainingGuide(); - void showDriverView(); - void expandToggleDescription(const QString ¶m); - void scrollToToggle(const QString ¶m); - -private: - QPushButton *sidebar_alert_widget; - QWidget *sidebar_widget; - QButtonGroup *nav_btns; - QStackedWidget *panel_widget; -}; - -class DevicePanel : public ListWidget { - Q_OBJECT -public: - explicit DevicePanel(SettingsWindow *parent); - -signals: - void reviewTrainingGuide(); - void showDriverView(); - -private slots: - void poweroff(); - void reboot(); - void updateCalibDescription(); - -private: - Params params; - ButtonControl *pair_device; - ButtonControl *resetCalibBtn; -}; - -class TogglesPanel : public ListWidget { - Q_OBJECT -public: - explicit TogglesPanel(SettingsWindow *parent); - void showEvent(QShowEvent *event) override; - -public slots: - void expandToggleDescription(const QString ¶m); - void scrollToToggle(const QString ¶m); - -private slots: - void updateState(const UIState &s); - -private: - Params params; - std::map toggles; - ButtonParamControl *long_personality_setting; - - void updateToggles(); -}; - -class SoftwarePanel : public ListWidget { - Q_OBJECT -public: - explicit SoftwarePanel(QWidget* parent = nullptr); - -private: - void showEvent(QShowEvent *event) override; - void updateLabels(); - void checkForUpdates(); - - bool is_onroad = false; - - QLabel *onroadLbl; - LabelControl *versionLbl; - ButtonControl *installBtn; - ButtonControl *downloadBtn; - ButtonControl *targetBranchBtn; - - Params params; - ParamWatcher *fs_watch; -}; - -// Forward declaration -class FirehosePanel; diff --git a/selfdrive/ui/qt/offroad/software_settings.cc b/selfdrive/ui/qt/offroad/software_settings.cc deleted file mode 100644 index 9bc3fad3c9..0000000000 --- a/selfdrive/ui/qt/offroad/software_settings.cc +++ /dev/null @@ -1,156 +0,0 @@ -#include "selfdrive/ui/qt/offroad/settings.h" - -#include -#include -#include - -#include -#include - -#include "common/params.h" -#include "common/util.h" -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/input.h" -#include "system/hardware/hw.h" - - -void SoftwarePanel::checkForUpdates() { - std::system("pkill -SIGUSR1 -f system.updated.updated"); -} - -SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { - onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off.")); - onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;"); - addItem(onroadLbl); - - // current version - versionLbl = new LabelControl(tr("Current Version"), ""); - addItem(versionLbl); - - // download update btn - downloadBtn = new ButtonControl(tr("Download"), tr("CHECK")); - connect(downloadBtn, &ButtonControl::clicked, [=]() { - downloadBtn->setEnabled(false); - if (downloadBtn->text() == tr("CHECK")) { - checkForUpdates(); - } else { - std::system("pkill -SIGHUP -f system.updated.updated"); - } - }); - addItem(downloadBtn); - - // install update btn - installBtn = new ButtonControl(tr("Install Update"), tr("INSTALL")); - connect(installBtn, &ButtonControl::clicked, [=]() { - installBtn->setEnabled(false); - params.putBool("DoReboot", true); - }); - addItem(installBtn); - - // branch selecting - targetBranchBtn = new ButtonControl(tr("Target Branch"), tr("SELECT")); - connect(targetBranchBtn, &ButtonControl::clicked, [=]() { - auto current = params.get("GitBranch"); - QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(","); - for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "nightly-dev", "master"}) { - auto i = branches.indexOf(b); - if (i >= 0) { - branches.removeAt(i); - branches.insert(0, b); - } - } - - QString cur = QString::fromStdString(params.get("UpdaterTargetBranch")); - QString selection = MultiOptionDialog::getSelection(tr("Select a branch"), branches, cur, this); - if (!selection.isEmpty()) { - params.put("UpdaterTargetBranch", selection.toStdString()); - targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch"))); - checkForUpdates(); - } - }); - if (!params.getBool("IsTestedBranch")) { - addItem(targetBranchBtn); - } - - // uninstall button - auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL")); - connect(uninstallBtn, &ButtonControl::clicked, [&]() { - if (ConfirmationDialog::confirm(tr("Are you sure you want to uninstall?"), tr("Uninstall"), this)) { - params.putBool("DoUninstall", true); - } - }); - addItem(uninstallBtn); - - fs_watch = new ParamWatcher(this); - QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { - updateLabels(); - }); - - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { - is_onroad = !offroad; - updateLabels(); - }); - - updateLabels(); -} - -void SoftwarePanel::showEvent(QShowEvent *event) { - // nice for testing on PC - installBtn->setEnabled(true); - - updateLabels(); -} - -void SoftwarePanel::updateLabels() { - // add these back in case the files got removed - fs_watch->addParam("LastUpdateTime"); - fs_watch->addParam("UpdateFailedCount"); - fs_watch->addParam("UpdaterState"); - fs_watch->addParam("UpdateAvailable"); - - if (!isVisible()) { - return; - } - - // updater only runs offroad - onroadLbl->setVisible(is_onroad); - downloadBtn->setVisible(!is_onroad); - - // download update - QString updater_state = QString::fromStdString(params.get("UpdaterState")); - bool failed = std::atoi(params.get("UpdateFailedCount").c_str()) > 0; - if (updater_state != "idle") { - downloadBtn->setEnabled(false); - downloadBtn->setValue(updater_state); - } else { - if (failed) { - downloadBtn->setText(tr("CHECK")); - downloadBtn->setValue(tr("failed to check for update")); - } else if (params.getBool("UpdaterFetchAvailable")) { - downloadBtn->setText(tr("DOWNLOAD")); - downloadBtn->setValue(tr("update available")); - } else { - QString lastUpdate = tr("never"); - auto tm = params.get("LastUpdateTime"); - if (!tm.empty()) { - lastUpdate = timeAgo(QDateTime::fromString(QString::fromStdString(tm + "Z"), Qt::ISODate)); - } - downloadBtn->setText(tr("CHECK")); - downloadBtn->setValue(tr("up to date, last checked %1").arg(lastUpdate)); - } - downloadBtn->setEnabled(true); - } - targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch"))); - - // current + new versions - versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription"))); - versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes"))); - - installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable")); - installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription"))); - installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes"))); - - update(); -} diff --git a/selfdrive/ui/qt/onroad/alerts.cc b/selfdrive/ui/qt/onroad/alerts.cc deleted file mode 100644 index 0236e20f16..0000000000 --- a/selfdrive/ui/qt/onroad/alerts.cc +++ /dev/null @@ -1,112 +0,0 @@ -#include "selfdrive/ui/qt/onroad/alerts.h" - -#include -#include - -#include "selfdrive/ui/qt/util.h" - -void OnroadAlerts::updateState(const UIState &s) { - Alert a = getAlert(*(s.sm), s.scene.started_frame); - if (!alert.equal(a)) { - alert = a; - update(); - } -} - -void OnroadAlerts::clear() { - alert = {}; - update(); -} - -OnroadAlerts::Alert OnroadAlerts::getAlert(const SubMaster &sm, uint64_t started_frame) { - const cereal::SelfdriveState::Reader &ss = sm["selfdriveState"].getSelfdriveState(); - const uint64_t selfdrive_frame = sm.rcv_frame("selfdriveState"); - - Alert a = {}; - if (selfdrive_frame >= started_frame) { // Don't get old alert. - a = {ss.getAlertText1().cStr(), ss.getAlertText2().cStr(), - ss.getAlertType().cStr(), ss.getAlertSize(), ss.getAlertStatus()}; - } - - if (!sm.updated("selfdriveState") && (sm.frame - started_frame) > 5 * UI_FREQ) { - const int SELFDRIVE_STATE_TIMEOUT = 5; - const int ss_missing = (nanos_since_boot() - sm.rcv_time("selfdriveState")) / 1e9; - - // Handle selfdrive timeout - if (selfdrive_frame < started_frame) { - // car is started, but selfdriveState hasn't been seen at all - a = {tr("openpilot Unavailable"), tr("Waiting to start"), - "selfdriveWaiting", cereal::SelfdriveState::AlertSize::MID, - cereal::SelfdriveState::AlertStatus::NORMAL}; - } else if (ss_missing > SELFDRIVE_STATE_TIMEOUT && !Hardware::PC()) { - // car is started, but selfdrive is lagging or died - if (ss.getEnabled() && (ss_missing - SELFDRIVE_STATE_TIMEOUT) < 10) { - a = {tr("TAKE CONTROL IMMEDIATELY"), tr("System Unresponsive"), - "selfdriveUnresponsive", cereal::SelfdriveState::AlertSize::FULL, - cereal::SelfdriveState::AlertStatus::CRITICAL}; - } else { - a = {tr("System Unresponsive"), tr("Reboot Device"), - "selfdriveUnresponsivePermanent", cereal::SelfdriveState::AlertSize::MID, - cereal::SelfdriveState::AlertStatus::NORMAL}; - } - } - } - return a; -} - -void OnroadAlerts::paintEvent(QPaintEvent *event) { - if (alert.size == cereal::SelfdriveState::AlertSize::NONE) { - return; - } - static std::map alert_heights = { - {cereal::SelfdriveState::AlertSize::SMALL, 271}, - {cereal::SelfdriveState::AlertSize::MID, 420}, - {cereal::SelfdriveState::AlertSize::FULL, height()}, - }; - int h = alert_heights[alert.size]; - - int margin = 40; - int radius = 30; - if (alert.size == cereal::SelfdriveState::AlertSize::FULL) { - margin = 0; - radius = 0; - } - QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2); - - QPainter p(this); - - // draw background + gradient - p.setPen(Qt::NoPen); - p.setCompositionMode(QPainter::CompositionMode_SourceOver); - p.setBrush(QBrush(alert_colors[alert.status])); - p.drawRoundedRect(r, radius, radius); - - QLinearGradient g(0, r.y(), 0, r.bottom()); - g.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.05)); - g.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0.35)); - - p.setCompositionMode(QPainter::CompositionMode_DestinationOver); - p.setBrush(QBrush(g)); - p.drawRoundedRect(r, radius, radius); - p.setCompositionMode(QPainter::CompositionMode_SourceOver); - - // text - const QPoint c = r.center(); - p.setPen(QColor(0xff, 0xff, 0xff)); - p.setRenderHint(QPainter::TextAntialiasing); - if (alert.size == cereal::SelfdriveState::AlertSize::SMALL) { - p.setFont(InterFont(74, QFont::DemiBold)); - p.drawText(r, Qt::AlignCenter, alert.text1); - } else if (alert.size == cereal::SelfdriveState::AlertSize::MID) { - p.setFont(InterFont(88, QFont::Bold)); - p.drawText(QRect(0, c.y() - 125, width(), 150), Qt::AlignHCenter | Qt::AlignTop, alert.text1); - p.setFont(InterFont(66)); - p.drawText(QRect(0, c.y() + 21, width(), 90), Qt::AlignHCenter, alert.text2); - } else if (alert.size == cereal::SelfdriveState::AlertSize::FULL) { - bool l = alert.text1.length() > 15; - p.setFont(InterFont(l ? 132 : 177, QFont::Bold)); - p.drawText(QRect(0, r.y() + (l ? 240 : 270), width(), 600), Qt::AlignHCenter | Qt::TextWordWrap, alert.text1); - p.setFont(InterFont(88)); - p.drawText(QRect(0, r.height() - (l ? 361 : 420), width(), 300), Qt::AlignHCenter | Qt::TextWordWrap, alert.text2); - } -} diff --git a/selfdrive/ui/qt/onroad/alerts.h b/selfdrive/ui/qt/onroad/alerts.h deleted file mode 100644 index de38d8ffc3..0000000000 --- a/selfdrive/ui/qt/onroad/alerts.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include - -#include "selfdrive/ui/ui.h" - -class OnroadAlerts : public QWidget { - Q_OBJECT - -public: - OnroadAlerts(QWidget *parent = 0) : QWidget(parent) {} - void updateState(const UIState &s); - void clear(); - -protected: - struct Alert { - QString text1; - QString text2; - QString type; - cereal::SelfdriveState::AlertSize size; - cereal::SelfdriveState::AlertStatus status; - - bool equal(const Alert &other) const { - return text1 == other.text1 && text2 == other.text2 && type == other.type; - } - }; - - const QMap alert_colors = { - {cereal::SelfdriveState::AlertStatus::NORMAL, QColor(0x15, 0x15, 0x15, 0xf1)}, - {cereal::SelfdriveState::AlertStatus::USER_PROMPT, QColor(0xDA, 0x6F, 0x25, 0xf1)}, - {cereal::SelfdriveState::AlertStatus::CRITICAL, QColor(0xC9, 0x22, 0x31, 0xf1)}, - }; - - void paintEvent(QPaintEvent*) override; - OnroadAlerts::Alert getAlert(const SubMaster &sm, uint64_t started_frame); - - QColor bg; - Alert alert = {}; -}; diff --git a/selfdrive/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc deleted file mode 100644 index f504ad69f1..0000000000 --- a/selfdrive/ui/qt/onroad/annotated_camera.cc +++ /dev/null @@ -1,157 +0,0 @@ - -#include "selfdrive/ui/qt/onroad/annotated_camera.h" - -#include -#include -#include - -#include "common/swaglog.h" -#include "selfdrive/ui/qt/util.h" - -// Window that shows camera view and variety of info drawn on top -AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget *parent) - : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, parent) { - pm = std::make_unique(std::vector{"uiDebug"}); - - main_layout = new QVBoxLayout(this); - main_layout->setMargin(UI_BORDER_SIZE); - main_layout->setSpacing(0); - - experimental_btn = new ExperimentalButton(this); - main_layout->addWidget(experimental_btn, 0, Qt::AlignTop | Qt::AlignRight); -} - -void AnnotatedCameraWidget::updateState(const UIState &s) { - // update engageability/experimental mode button - experimental_btn->updateState(s); - dmon.updateState(s); -} - -void AnnotatedCameraWidget::initializeGL() { - CameraWidget::initializeGL(); - qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION)); - qInfo() << "OpenGL vendor:" << QString((const char*)glGetString(GL_VENDOR)); - qInfo() << "OpenGL renderer:" << QString((const char*)glGetString(GL_RENDERER)); - qInfo() << "OpenGL language version:" << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); - - prev_draw_t = millis_since_boot(); - setBackgroundColor(bg_colors[STATUS_DISENGAGED]); -} - -mat4 AnnotatedCameraWidget::calcFrameMatrix() { - // Project point at "infinity" to compute x and y offsets - // to ensure this ends up in the middle of the screen - // for narrow come and a little lower for wide cam. - // TODO: use proper perspective transform? - - // Select intrinsic matrix and calibration based on camera type - auto *s = uiState(); - bool wide_cam = active_stream_type == VISION_STREAM_WIDE_ROAD; - const auto &intrinsic_matrix = wide_cam ? ECAM_INTRINSIC_MATRIX : FCAM_INTRINSIC_MATRIX; - const auto &calibration = wide_cam ? s->scene.view_from_wide_calib : s->scene.view_from_calib; - - // Compute the calibration transformation matrix - const auto calib_transform = intrinsic_matrix * calibration; - - float zoom = wide_cam ? 2.0 : 1.1; - Eigen::Vector3f inf(1000., 0., 0.); - auto Kep = calib_transform * inf; - - int w = width(), h = height(); - float center_x = intrinsic_matrix(0, 2); - float center_y = intrinsic_matrix(1, 2); - - float max_x_offset = center_x * zoom - w / 2 - 5; - float max_y_offset = center_y * zoom - h / 2 - 5; - float x_offset = std::clamp((Kep.x() / Kep.z() - center_x) * zoom, -max_x_offset, max_x_offset); - float y_offset = std::clamp((Kep.y() / Kep.z() - center_y) * zoom, -max_y_offset, max_y_offset); - - // Apply transformation such that video pixel coordinates match video - // 1) Put (0, 0) in the middle of the video - // 2) Apply same scaling as video - // 3) Put (0, 0) in top left corner of video - Eigen::Matrix3f video_transform =(Eigen::Matrix3f() << - zoom, 0.0f, (w / 2 - x_offset) - (center_x * zoom), - 0.0f, zoom, (h / 2 - y_offset) - (center_y * zoom), - 0.0f, 0.0f, 1.0f).finished(); - - model.setTransform(video_transform * calib_transform); - - float zx = zoom * 2 * center_x / w; - float zy = zoom * 2 * center_y / h; - return mat4{{ - zx, 0.0, 0.0, -x_offset / w * 2, - 0.0, zy, 0.0, y_offset / h * 2, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, - }}; -} - -void AnnotatedCameraWidget::paintGL() { - UIState *s = uiState(); - SubMaster &sm = *(s->sm); - const double start_draw_t = millis_since_boot(); - - // draw camera frame - { - std::lock_guard lk(frame_lock); - - if (frames.empty()) { - if (skip_frame_count > 0) { - skip_frame_count--; - qDebug() << "skipping frame, not ready"; - return; - } - } else { - // skip drawing up to this many frames if we're - // missing camera frames. this smooths out the - // transitions from the narrow and wide cameras - skip_frame_count = 5; - } - - // Wide or narrow cam dependent on speed - bool has_wide_cam = available_streams.count(VISION_STREAM_WIDE_ROAD); - if (has_wide_cam) { - float v_ego = sm["carState"].getCarState().getVEgo(); - if ((v_ego < 10) || available_streams.size() == 1) { - wide_cam_requested = true; - } else if (v_ego > 15) { - wide_cam_requested = false; - } - wide_cam_requested = wide_cam_requested && sm["selfdriveState"].getSelfdriveState().getExperimentalMode(); - } - CameraWidget::setStreamType(wide_cam_requested ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD); - CameraWidget::setFrameId(sm["modelV2"].getModelV2().getFrameId()); - CameraWidget::paintGL(); - } - - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - painter.setPen(Qt::NoPen); - - model.draw(painter, rect()); - dmon.draw(painter, rect()); - hud.updateState(*s); - hud.draw(painter, rect()); - - double cur_draw_t = millis_since_boot(); - double dt = cur_draw_t - prev_draw_t; - double fps = fps_filter.update(1. / dt * 1000); - if (fps < 15) { - LOGW("slow frame rate: %.2f fps", fps); - } - prev_draw_t = cur_draw_t; - - // publish debug msg - MessageBuilder msg; - auto m = msg.initEvent().initUiDebug(); - m.setDrawTimeMillis(cur_draw_t - start_draw_t); - pm->send("uiDebug", msg); -} - -void AnnotatedCameraWidget::showEvent(QShowEvent *event) { - CameraWidget::showEvent(event); - - ui_update_params(uiState()); - prev_draw_t = millis_since_boot(); -} diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h deleted file mode 100644 index d205579f6c..0000000000 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include "selfdrive/ui/qt/onroad/hud.h" -#include "selfdrive/ui/qt/onroad/buttons.h" -#include "selfdrive/ui/qt/onroad/driver_monitoring.h" -#include "selfdrive/ui/qt/onroad/model.h" -#include "selfdrive/ui/qt/widgets/cameraview.h" - -class AnnotatedCameraWidget : public CameraWidget { - Q_OBJECT - -public: - explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0); - void updateState(const UIState &s); - -private: - QVBoxLayout *main_layout; - ExperimentalButton *experimental_btn; - DriverMonitorRenderer dmon; - HudRenderer hud; - ModelRenderer model; - std::unique_ptr pm; - - int skip_frame_count = 0; - bool wide_cam_requested = false; - -protected: - void paintGL() override; - void initializeGL() override; - void showEvent(QShowEvent *event) override; - mat4 calcFrameMatrix() override; - - double prev_draw_t = 0; - FirstOrderFilter fps_filter; -}; diff --git a/selfdrive/ui/qt/onroad/buttons.cc b/selfdrive/ui/qt/onroad/buttons.cc deleted file mode 100644 index 32e58c9dba..0000000000 --- a/selfdrive/ui/qt/onroad/buttons.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include "selfdrive/ui/qt/onroad/buttons.h" - -#include - -#include "selfdrive/ui/qt/util.h" - -void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity) { - p.setRenderHint(QPainter::Antialiasing); - p.setOpacity(1.0); // bg dictates opacity of ellipse - p.setPen(Qt::NoPen); - p.setBrush(bg); - p.drawEllipse(center, btn_size / 2, btn_size / 2); - p.setOpacity(opacity); - p.drawPixmap(center - QPoint(img.width() / 2, img.height() / 2), img); - p.setOpacity(1.0); -} - -// ExperimentalButton -ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(false), engageable(false), QPushButton(parent) { - setFixedSize(btn_size, btn_size); - - engage_img = loadPixmap("../assets/icons/chffr_wheel.png", {img_size, img_size}); - experimental_img = loadPixmap("../assets/icons/experimental.svg", {img_size, img_size}); - QObject::connect(this, &QPushButton::clicked, this, &ExperimentalButton::changeMode); -} - -void ExperimentalButton::changeMode() { - const auto cp = (*uiState()->sm)["carParams"].getCarParams(); - bool can_change = hasLongitudinalControl(cp) && params.getBool("ExperimentalModeConfirmed"); - if (can_change) { - params.putBool("ExperimentalMode", !experimental_mode); - } -} - -void ExperimentalButton::updateState(const UIState &s) { - const auto cs = (*s.sm)["selfdriveState"].getSelfdriveState(); - bool eng = cs.getEngageable() || cs.getEnabled(); - if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) { - engageable = eng; - experimental_mode = cs.getExperimentalMode(); - update(); - } -} - -void ExperimentalButton::paintEvent(QPaintEvent *event) { - QPainter p(this); - QPixmap img = experimental_mode ? experimental_img : engage_img; - drawIcon(p, QPoint(btn_size / 2, btn_size / 2), img, QColor(0, 0, 0, 166), (isDown() || !engageable) ? 0.6 : 1.0); -} diff --git a/selfdrive/ui/qt/onroad/buttons.h b/selfdrive/ui/qt/onroad/buttons.h deleted file mode 100644 index 9c91bc3c7b..0000000000 --- a/selfdrive/ui/qt/onroad/buttons.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -#include "selfdrive/ui/ui.h" - -const int btn_size = 192; -const int img_size = (btn_size / 4) * 3; - -class ExperimentalButton : public QPushButton { - Q_OBJECT - -public: - explicit ExperimentalButton(QWidget *parent = 0); - void updateState(const UIState &s); - -private: - void paintEvent(QPaintEvent *event) override; - void changeMode(); - - Params params; - QPixmap engage_img; - QPixmap experimental_img; - bool experimental_mode; - bool engageable; -}; - -void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity); diff --git a/selfdrive/ui/qt/onroad/driver_monitoring.cc b/selfdrive/ui/qt/onroad/driver_monitoring.cc deleted file mode 100644 index 49f2c950b4..0000000000 --- a/selfdrive/ui/qt/onroad/driver_monitoring.cc +++ /dev/null @@ -1,107 +0,0 @@ -#include "selfdrive/ui/qt/onroad/driver_monitoring.h" -#include -#include - -#include "selfdrive/ui/qt/onroad/buttons.h" -#include "selfdrive/ui/qt/util.h" - -// Default 3D coordinates for face keypoints -static constexpr vec3 DEFAULT_FACE_KPTS_3D[] = { - {-5.98, -51.20, 8.00}, {-17.64, -49.14, 8.00}, {-23.81, -46.40, 8.00}, {-29.98, -40.91, 8.00}, {-32.04, -37.49, 8.00}, - {-34.10, -32.00, 8.00}, {-36.16, -21.03, 8.00}, {-36.16, 6.40, 8.00}, {-35.47, 10.51, 8.00}, {-32.73, 19.43, 8.00}, - {-29.30, 26.29, 8.00}, {-24.50, 33.83, 8.00}, {-19.01, 41.37, 8.00}, {-14.21, 46.17, 8.00}, {-12.16, 47.54, 8.00}, - {-4.61, 49.60, 8.00}, {4.99, 49.60, 8.00}, {12.53, 47.54, 8.00}, {14.59, 46.17, 8.00}, {19.39, 41.37, 8.00}, - {24.87, 33.83, 8.00}, {29.67, 26.29, 8.00}, {33.10, 19.43, 8.00}, {35.84, 10.51, 8.00}, {36.53, 6.40, 8.00}, - {36.53, -21.03, 8.00}, {34.47, -32.00, 8.00}, {32.42, -37.49, 8.00}, {30.36, -40.91, 8.00}, {24.19, -46.40, 8.00}, - {18.02, -49.14, 8.00}, {6.36, -51.20, 8.00}, {-5.98, -51.20, 8.00}, -}; - -// Colors used for drawing based on monitoring state -static const QColor DMON_ENGAGED_COLOR = QColor::fromRgbF(0.1, 0.945, 0.26); -static const QColor DMON_DISENGAGED_COLOR = QColor::fromRgbF(0.545, 0.545, 0.545); - -DriverMonitorRenderer::DriverMonitorRenderer() : face_kpts_draw(std::size(DEFAULT_FACE_KPTS_3D)) { - dm_img = loadPixmap("../assets/icons/driver_face.png", {img_size + 5, img_size + 5}); -} - -void DriverMonitorRenderer::updateState(const UIState &s) { - auto &sm = *(s.sm); - is_visible = sm["selfdriveState"].getSelfdriveState().getAlertSize() == cereal::SelfdriveState::AlertSize::NONE && - sm.rcv_frame("driverStateV2") > s.scene.started_frame; - if (!is_visible) return; - - auto dm_state = sm["driverMonitoringState"].getDriverMonitoringState(); - is_active = dm_state.getIsActiveMode(); - is_rhd = dm_state.getIsRHD(); - dm_fade_state = std::clamp(dm_fade_state + 0.2f * (0.5f - is_active), 0.0f, 1.0f); - - const auto &driverstate = sm["driverStateV2"].getDriverStateV2(); - const auto driver_orient = is_rhd ? driverstate.getRightDriverData().getFaceOrientation() : driverstate.getLeftDriverData().getFaceOrientation(); - - for (int i = 0; i < 3; ++i) { - float v_this = (i == 0 ? (driver_orient[i] < 0 ? 0.7 : 0.9) : 0.4) * driver_orient[i]; - driver_pose_diff[i] = std::abs(driver_pose_vals[i] - v_this); - driver_pose_vals[i] = 0.8f * v_this + (1 - 0.8) * driver_pose_vals[i]; - driver_pose_sins[i] = std::sin(driver_pose_vals[i] * (1.0f - dm_fade_state)); - driver_pose_coss[i] = std::cos(driver_pose_vals[i] * (1.0f - dm_fade_state)); - } - - auto [sin_y, sin_x, sin_z] = driver_pose_sins; - auto [cos_y, cos_x, cos_z] = driver_pose_coss; - - // Rotation matrix for transforming face keypoints based on driver's head orientation - const mat3 r_xyz = {{ - cos_x * cos_z, cos_x * sin_z, -sin_x, - -sin_y * sin_x * cos_z - cos_y * sin_z, -sin_y * sin_x * sin_z + cos_y * cos_z, -sin_y * cos_x, - cos_y * sin_x * cos_z - sin_y * sin_z, cos_y * sin_x * sin_z + sin_y * cos_z, cos_y * cos_x, - }}; - - // Transform vertices - for (int i = 0; i < face_kpts_draw.size(); ++i) { - vec3 kpt = matvecmul3(r_xyz, DEFAULT_FACE_KPTS_3D[i]); - face_kpts_draw[i] = {{kpt.v[0], kpt.v[1], kpt.v[2] * (1.0f - dm_fade_state) + 8 * dm_fade_state}}; - } -} - -void DriverMonitorRenderer::draw(QPainter &painter, const QRect &surface_rect) { - if (!is_visible) return; - - painter.save(); - - int offset = UI_BORDER_SIZE + btn_size / 2; - float x = is_rhd ? surface_rect.width() - offset : offset; - float y = surface_rect.height() - offset; - float opacity = is_active ? 0.65f : 0.2f; - - drawIcon(painter, QPoint(x, y), dm_img, QColor(0, 0, 0, 70), opacity); - - QPointF keypoints[std::size(DEFAULT_FACE_KPTS_3D)]; - for (int i = 0; i < std::size(keypoints); ++i) { - const auto &v = face_kpts_draw[i].v; - float kp = (v[2] - 8) / 120.0f + 1.0f; - keypoints[i] = QPointF(v[0] * kp + x, v[1] * kp + y); - } - - painter.setPen(QPen(QColor::fromRgbF(1.0, 1.0, 1.0, opacity), 5.2, Qt::SolidLine, Qt::RoundCap)); - painter.drawPolyline(keypoints, std::size(keypoints)); - - // tracking arcs - const int arc_l = 133; - const float arc_t_default = 6.7f; - const float arc_t_extend = 12.0f; - QColor arc_color = uiState()->engaged() ? DMON_ENGAGED_COLOR : DMON_DISENGAGED_COLOR; - arc_color.setAlphaF(0.4 * (1.0f - dm_fade_state)); - - float delta_x = -driver_pose_sins[1] * arc_l / 2.0f; - float delta_y = -driver_pose_sins[0] * arc_l / 2.0f; - - // Draw horizontal tracking arc - painter.setPen(QPen(arc_color, arc_t_default + arc_t_extend * std::min(1.0, driver_pose_diff[1] * 5.0), Qt::SolidLine, Qt::RoundCap)); - painter.drawArc(QRectF(std::min(x + delta_x, x), y - arc_l / 2, std::abs(delta_x), arc_l), (driver_pose_sins[1] > 0 ? 90 : -90) * 16, 180 * 16); - - // Draw vertical tracking arc - painter.setPen(QPen(arc_color, arc_t_default + arc_t_extend * std::min(1.0, driver_pose_diff[0] * 5.0), Qt::SolidLine, Qt::RoundCap)); - painter.drawArc(QRectF(x - arc_l / 2, std::min(y + delta_y, y), arc_l, std::abs(delta_y)), (driver_pose_sins[0] > 0 ? 0 : 180) * 16, 180 * 16); - - painter.restore(); -} diff --git a/selfdrive/ui/qt/onroad/driver_monitoring.h b/selfdrive/ui/qt/onroad/driver_monitoring.h deleted file mode 100644 index 47db151c81..0000000000 --- a/selfdrive/ui/qt/onroad/driver_monitoring.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include "selfdrive/ui/ui.h" - -class DriverMonitorRenderer { -public: - DriverMonitorRenderer(); - void updateState(const UIState &s); - void draw(QPainter &painter, const QRect &surface_rect); - -private: - float driver_pose_vals[3] = {}; - float driver_pose_diff[3] = {}; - float driver_pose_sins[3] = {}; - float driver_pose_coss[3] = {}; - bool is_visible = false; - bool is_active = false; - bool is_rhd = false; - float dm_fade_state = 1.0; - QPixmap dm_img; - std::vector face_kpts_draw; -}; diff --git a/selfdrive/ui/qt/onroad/hud.cc b/selfdrive/ui/qt/onroad/hud.cc deleted file mode 100644 index 4cfa3d0e3c..0000000000 --- a/selfdrive/ui/qt/onroad/hud.cc +++ /dev/null @@ -1,112 +0,0 @@ -#include "selfdrive/ui/qt/onroad/hud.h" - -#include - -#include "selfdrive/ui/qt/util.h" - -constexpr int SET_SPEED_NA = 255; - -HudRenderer::HudRenderer() {} - -void HudRenderer::updateState(const UIState &s) { - is_metric = s.scene.is_metric; - status = s.status; - - const SubMaster &sm = *(s.sm); - if (sm.rcv_frame("carState") < s.scene.started_frame) { - is_cruise_set = false; - set_speed = SET_SPEED_NA; - speed = 0.0; - return; - } - - const auto &controls_state = sm["controlsState"].getControlsState(); - const auto &car_state = sm["carState"].getCarState(); - - // Handle older routes where vCruiseCluster is not set - set_speed = car_state.getVCruiseCluster() == 0.0 ? controls_state.getVCruiseDEPRECATED() : car_state.getVCruiseCluster(); - is_cruise_set = set_speed > 0 && set_speed != SET_SPEED_NA; - is_cruise_available = set_speed != -1; - - if (is_cruise_set && !is_metric) { - set_speed *= KM_TO_MILE; - } - - // Handle older routes where vEgoCluster is not set - v_ego_cluster_seen = v_ego_cluster_seen || car_state.getVEgoCluster() != 0.0; - float v_ego = v_ego_cluster_seen ? car_state.getVEgoCluster() : car_state.getVEgo(); - speed = std::max(0.0f, v_ego * (is_metric ? MS_TO_KPH : MS_TO_MPH)); -} - -void HudRenderer::draw(QPainter &p, const QRect &surface_rect) { - p.save(); - - // Draw header gradient - QLinearGradient bg(0, UI_HEADER_HEIGHT - (UI_HEADER_HEIGHT / 2.5), 0, UI_HEADER_HEIGHT); - bg.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.45)); - bg.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0)); - p.fillRect(0, 0, surface_rect.width(), UI_HEADER_HEIGHT, bg); - - - if (is_cruise_available) { - drawSetSpeed(p, surface_rect); - } - drawCurrentSpeed(p, surface_rect); - - p.restore(); -} - -void HudRenderer::drawSetSpeed(QPainter &p, const QRect &surface_rect) { - // Draw outer box + border to contain set speed - const QSize default_size = {172, 204}; - QSize set_speed_size = is_metric ? QSize(200, 204) : default_size; - QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size); - - // Draw set speed box - p.setPen(QPen(QColor(255, 255, 255, 75), 6)); - p.setBrush(QColor(0, 0, 0, 166)); - p.drawRoundedRect(set_speed_rect, 32, 32); - - // Colors based on status - QColor max_color = QColor(0xa6, 0xa6, 0xa6, 0xff); - QColor set_speed_color = QColor(0x72, 0x72, 0x72, 0xff); - if (is_cruise_set) { - set_speed_color = QColor(255, 255, 255); - if (status == STATUS_DISENGAGED) { - max_color = QColor(255, 255, 255); - } else if (status == STATUS_OVERRIDE) { - max_color = QColor(0x91, 0x9b, 0x95, 0xff); - } else { - max_color = QColor(0x80, 0xd8, 0xa6, 0xff); - } - } - - // Draw "MAX" text - p.setFont(InterFont(40, QFont::DemiBold)); - p.setPen(max_color); - p.drawText(set_speed_rect.adjusted(0, 27, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("MAX")); - - // Draw set speed - QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(set_speed)) : "–"; - p.setFont(InterFont(90, QFont::Bold)); - p.setPen(set_speed_color); - p.drawText(set_speed_rect.adjusted(0, 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, setSpeedStr); -} - -void HudRenderer::drawCurrentSpeed(QPainter &p, const QRect &surface_rect) { - QString speedStr = QString::number(std::nearbyint(speed)); - - p.setFont(InterFont(176, QFont::Bold)); - drawText(p, surface_rect.center().x(), 210, speedStr); - - p.setFont(InterFont(66)); - drawText(p, surface_rect.center().x(), 290, is_metric ? tr("km/h") : tr("mph"), 200); -} - -void HudRenderer::drawText(QPainter &p, int x, int y, const QString &text, int alpha) { - QRect real_rect = p.fontMetrics().boundingRect(text); - real_rect.moveCenter({x, y - real_rect.height() / 2}); - - p.setPen(QColor(0xff, 0xff, 0xff, alpha)); - p.drawText(real_rect.x(), real_rect.bottom(), text); -} diff --git a/selfdrive/ui/qt/onroad/hud.h b/selfdrive/ui/qt/onroad/hud.h deleted file mode 100644 index b2ac379dbe..0000000000 --- a/selfdrive/ui/qt/onroad/hud.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include "selfdrive/ui/ui.h" - -class HudRenderer : public QObject { - Q_OBJECT - -public: - HudRenderer(); - void updateState(const UIState &s); - void draw(QPainter &p, const QRect &surface_rect); - -private: - void drawSetSpeed(QPainter &p, const QRect &surface_rect); - void drawCurrentSpeed(QPainter &p, const QRect &surface_rect); - void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255); - - float speed = 0; - float set_speed = 0; - bool is_cruise_set = false; - bool is_cruise_available = true; - bool is_metric = false; - bool v_ego_cluster_seen = false; - int status = STATUS_DISENGAGED; -}; diff --git a/selfdrive/ui/qt/onroad/model.cc b/selfdrive/ui/qt/onroad/model.cc deleted file mode 100644 index 52902abdc8..0000000000 --- a/selfdrive/ui/qt/onroad/model.cc +++ /dev/null @@ -1,250 +0,0 @@ -#include "selfdrive/ui/qt/onroad/model.h" - -constexpr int CLIP_MARGIN = 500; -constexpr float MIN_DRAW_DISTANCE = 10.0; -constexpr float MAX_DRAW_DISTANCE = 100.0; - -static int get_path_length_idx(const cereal::XYZTData::Reader &line, const float path_height) { - const auto &line_x = line.getX(); - int max_idx = 0; - for (int i = 1; i < line_x.size() && line_x[i] <= path_height; ++i) { - max_idx = i; - } - return max_idx; -} - -void ModelRenderer::draw(QPainter &painter, const QRect &surface_rect) { - auto *s = uiState(); - auto &sm = *(s->sm); - // Check if data is up-to-date - if (sm.rcv_frame("liveCalibration") < s->scene.started_frame || - sm.rcv_frame("modelV2") < s->scene.started_frame) { - return; - } - - clip_region = surface_rect.adjusted(-CLIP_MARGIN, -CLIP_MARGIN, CLIP_MARGIN, CLIP_MARGIN); - experimental_mode = sm["selfdriveState"].getSelfdriveState().getExperimentalMode(); - longitudinal_control = sm["carParams"].getCarParams().getOpenpilotLongitudinalControl(); - path_offset_z = sm["liveCalibration"].getLiveCalibration().getHeight()[0]; - - painter.save(); - - const auto &model = sm["modelV2"].getModelV2(); - const auto &radar_state = sm["radarState"].getRadarState(); - const auto &lead_one = radar_state.getLeadOne(); - - update_model(model, lead_one); - drawLaneLines(painter); - drawPath(painter, model, surface_rect.height()); - - if (longitudinal_control && sm.alive("radarState")) { - update_leads(radar_state, model.getPosition()); - const auto &lead_two = radar_state.getLeadTwo(); - if (lead_one.getStatus()) { - drawLead(painter, lead_one, lead_vertices[0], surface_rect); - } - if (lead_two.getStatus() && (std::abs(lead_one.getDRel() - lead_two.getDRel()) > 3.0)) { - drawLead(painter, lead_two, lead_vertices[1], surface_rect); - } - } - - painter.restore(); -} - -void ModelRenderer::update_leads(const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line) { - for (int i = 0; i < 2; ++i) { - const auto &lead_data = (i == 0) ? radar_state.getLeadOne() : radar_state.getLeadTwo(); - if (lead_data.getStatus()) { - float z = line.getZ()[get_path_length_idx(line, lead_data.getDRel())]; - mapToScreen(lead_data.getDRel(), -lead_data.getYRel(), z + path_offset_z, &lead_vertices[i]); - } - } -} - -void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) { - const auto &model_position = model.getPosition(); - float max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE); - - // update lane lines - const auto &lane_lines = model.getLaneLines(); - const auto &line_probs = model.getLaneLineProbs(); - int max_idx = get_path_length_idx(lane_lines[0], max_distance); - for (int i = 0; i < std::size(lane_line_vertices); i++) { - lane_line_probs[i] = line_probs[i]; - mapLineToPolygon(lane_lines[i], 0.025 * lane_line_probs[i], 0, &lane_line_vertices[i], max_idx); - } - - // update road edges - const auto &road_edges = model.getRoadEdges(); - const auto &edge_stds = model.getRoadEdgeStds(); - for (int i = 0; i < std::size(road_edge_vertices); i++) { - road_edge_stds[i] = edge_stds[i]; - mapLineToPolygon(road_edges[i], 0.025, 0, &road_edge_vertices[i], max_idx); - } - - // update path - if (lead.getStatus()) { - const float lead_d = lead.getDRel() * 2.; - max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance); - } - max_idx = get_path_length_idx(model_position, max_distance); - mapLineToPolygon(model_position, 0.9, path_offset_z, &track_vertices, max_idx, false); -} - -void ModelRenderer::drawLaneLines(QPainter &painter) { - // lanelines - for (int i = 0; i < std::size(lane_line_vertices); ++i) { - painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp(lane_line_probs[i], 0.0, 0.7))); - painter.drawPolygon(lane_line_vertices[i]); - } - - // road edges - for (int i = 0; i < std::size(road_edge_vertices); ++i) { - painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp(1.0 - road_edge_stds[i], 0.0, 1.0))); - painter.drawPolygon(road_edge_vertices[i]); - } -} - -void ModelRenderer::drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, int height) { - QLinearGradient bg(0, height, 0, 0); - if (experimental_mode) { - // The first half of track_vertices are the points for the right side of the path - const auto &acceleration = model.getAcceleration().getX(); - const int max_len = std::min(track_vertices.length() / 2, acceleration.size()); - - for (int i = 0; i < max_len; ++i) { - // Some points are out of frame - int track_idx = max_len - i - 1; // flip idx to start from bottom right - if (track_vertices[track_idx].y() < 0 || track_vertices[track_idx].y() > height) continue; - - // Flip so 0 is bottom of frame - float lin_grad_point = (height - track_vertices[track_idx].y()) / height; - - // speed up: 120, slow down: 0 - float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0); - // FIXME: painter.drawPolygon can be slow if hue is not rounded - path_hue = int(path_hue * 100 + 0.5) / 100; - - float saturation = fmin(fabs(acceleration[i] * 1.5), 1); - float lightness = util::map_val(saturation, 0.0f, 1.0f, 0.95f, 0.62f); // lighter when grey - float alpha = util::map_val(lin_grad_point, 0.75f / 2.f, 0.75f, 0.4f, 0.0f); // matches previous alpha fade - bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha)); - - // Skip a point, unless next is last - i += (i + 2) < max_len ? 1 : 0; - } - - } else { - updatePathGradient(bg); - } - - painter.setBrush(bg); - painter.drawPolygon(track_vertices); -} - -void ModelRenderer::updatePathGradient(QLinearGradient &bg) { - static const QColor throttle_colors[] = { - QColor::fromHslF(148. / 360., 0.94, 0.51, 0.4), - QColor::fromHslF(112. / 360., 1.0, 0.68, 0.35), - QColor::fromHslF(112. / 360., 1.0, 0.68, 0.0)}; - - static const QColor no_throttle_colors[] = { - QColor::fromHslF(148. / 360., 0.0, 0.95, 0.4), - QColor::fromHslF(112. / 360., 0.0, 0.95, 0.35), - QColor::fromHslF(112. / 360., 0.0, 0.95, 0.0), - }; - - // Transition speed; 0.1 corresponds to 0.5 seconds at UI_FREQ - constexpr float transition_speed = 0.1f; - - // Start transition if throttle state changes - bool allow_throttle = (*uiState()->sm)["longitudinalPlan"].getLongitudinalPlan().getAllowThrottle() || !longitudinal_control; - if (allow_throttle != prev_allow_throttle) { - prev_allow_throttle = allow_throttle; - // Invert blend factor for a smooth transition when the state changes mid-animation - blend_factor = std::max(1.0f - blend_factor, 0.0f); - } - - const QColor *begin_colors = allow_throttle ? no_throttle_colors : throttle_colors; - const QColor *end_colors = allow_throttle ? throttle_colors : no_throttle_colors; - if (blend_factor < 1.0f) { - blend_factor = std::min(blend_factor + transition_speed, 1.0f); - } - - // Set gradient colors by blending the start and end colors - bg.setColorAt(0.0f, blendColors(begin_colors[0], end_colors[0], blend_factor)); - bg.setColorAt(0.5f, blendColors(begin_colors[1], end_colors[1], blend_factor)); - bg.setColorAt(1.0f, blendColors(begin_colors[2], end_colors[2], blend_factor)); -} - -QColor ModelRenderer::blendColors(const QColor &start, const QColor &end, float t) { - if (t == 1.0f) return end; - return QColor::fromRgbF( - (1 - t) * start.redF() + t * end.redF(), - (1 - t) * start.greenF() + t * end.greenF(), - (1 - t) * start.blueF() + t * end.blueF(), - (1 - t) * start.alphaF() + t * end.alphaF()); -} - -void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, - const QPointF &vd, const QRect &surface_rect) { - const float speedBuff = 10.; - const float leadBuff = 40.; - const float d_rel = lead_data.getDRel(); - const float v_rel = lead_data.getVRel(); - - float fillAlpha = 0; - if (d_rel < leadBuff) { - fillAlpha = 255 * (1.0 - (d_rel / leadBuff)); - if (v_rel < 0) { - fillAlpha += 255 * (-1 * (v_rel / speedBuff)); - } - fillAlpha = (int)(fmin(fillAlpha, 255)); - } - - float sz = std::clamp((25 * 30) / (d_rel / 3 + 30), 15.0f, 30.0f) * 2.35; - float x = std::clamp(vd.x(), 0.f, surface_rect.width() - sz / 2); - float y = std::min(vd.y(), surface_rect.height() - sz * 0.6); - - float g_xo = sz / 5; - float g_yo = sz / 10; - - QPointF glow[] = {{x + (sz * 1.35) + g_xo, y + sz + g_yo}, {x, y - g_yo}, {x - (sz * 1.35) - g_xo, y + sz + g_yo}}; - painter.setBrush(QColor(218, 202, 37, 255)); - painter.drawPolygon(glow, std::size(glow)); - - // chevron - QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}}; - painter.setBrush(QColor(201, 34, 49, fillAlpha)); - painter.drawPolygon(chevron, std::size(chevron)); -} - -// Projects a point in car to space to the corresponding point in full frame image space. -bool ModelRenderer::mapToScreen(float in_x, float in_y, float in_z, QPointF *out) { - Eigen::Vector3f input(in_x, in_y, in_z); - auto pt = car_space_transform * input; - *out = QPointF(pt.x() / pt.z(), pt.y() / pt.z()); - return clip_region.contains(*out); -} - -void ModelRenderer::mapLineToPolygon(const cereal::XYZTData::Reader &line, float y_off, float z_off, - QPolygonF *pvd, int max_idx, bool allow_invert) { - const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ(); - QPointF left, right; - pvd->clear(); - for (int i = 0; i <= max_idx; i++) { - // highly negative x positions are drawn above the frame and cause flickering, clip to zy plane of camera - if (line_x[i] < 0) continue; - - bool l = mapToScreen(line_x[i], line_y[i] - y_off, line_z[i] + z_off, &left); - bool r = mapToScreen(line_x[i], line_y[i] + y_off, line_z[i] + z_off, &right); - if (l && r) { - // For wider lines the drawn polygon will "invert" when going over a hill and cause artifacts - if (!allow_invert && pvd->size() && left.y() > pvd->back().y()) { - continue; - } - pvd->push_back(left); - pvd->push_front(right); - } - } -} diff --git a/selfdrive/ui/qt/onroad/model.h b/selfdrive/ui/qt/onroad/model.h deleted file mode 100644 index 79547e4b83..0000000000 --- a/selfdrive/ui/qt/onroad/model.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -#include "selfdrive/ui/ui.h" - -class ModelRenderer { -public: - ModelRenderer() {} - void setTransform(const Eigen::Matrix3f &transform) { car_space_transform = transform; } - void draw(QPainter &painter, const QRect &surface_rect); - -private: - bool mapToScreen(float in_x, float in_y, float in_z, QPointF *out); - void mapLineToPolygon(const cereal::XYZTData::Reader &line, float y_off, float z_off, - QPolygonF *pvd, int max_idx, bool allow_invert = true); - void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, const QRect &surface_rect); - void update_leads(const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line); - void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead); - void drawLaneLines(QPainter &painter); - void drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, int height); - void updatePathGradient(QLinearGradient &bg); - QColor blendColors(const QColor &start, const QColor &end, float t); - - bool longitudinal_control = false; - bool experimental_mode = false; - float blend_factor = 1.0f; - bool prev_allow_throttle = true; - float lane_line_probs[4] = {}; - float road_edge_stds[2] = {}; - float path_offset_z = 1.22f; - QPolygonF track_vertices; - QPolygonF lane_line_vertices[4] = {}; - QPolygonF road_edge_vertices[2] = {}; - QPointF lead_vertices[2] = {}; - Eigen::Matrix3f car_space_transform = Eigen::Matrix3f::Zero(); - QRectF clip_region; -}; diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc deleted file mode 100644 index 080f9bd50f..0000000000 --- a/selfdrive/ui/qt/onroad/onroad_home.cc +++ /dev/null @@ -1,65 +0,0 @@ -#include "selfdrive/ui/qt/onroad/onroad_home.h" - -#include -#include - -#include "selfdrive/ui/qt/util.h" - -OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setMargin(UI_BORDER_SIZE); - QStackedLayout *stacked_layout = new QStackedLayout; - stacked_layout->setStackingMode(QStackedLayout::StackAll); - main_layout->addLayout(stacked_layout); - - nvg = new AnnotatedCameraWidget(VISION_STREAM_ROAD, this); - - QWidget * split_wrapper = new QWidget; - split = new QHBoxLayout(split_wrapper); - split->setContentsMargins(0, 0, 0, 0); - split->setSpacing(0); - split->addWidget(nvg); - - if (getenv("DUAL_CAMERA_VIEW")) { - CameraWidget *arCam = new CameraWidget("camerad", VISION_STREAM_ROAD, this); - split->insertWidget(0, arCam); - } - - stacked_layout->addWidget(split_wrapper); - - alerts = new OnroadAlerts(this); - alerts->setAttribute(Qt::WA_TransparentForMouseEvents, true); - stacked_layout->addWidget(alerts); - - // setup stacking order - alerts->raise(); - - setAttribute(Qt::WA_OpaquePaintEvent); - QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState); - QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition); -} - -void OnroadWindow::updateState(const UIState &s) { - if (!s.scene.started) { - return; - } - - alerts->updateState(s); - nvg->updateState(s); - - QColor bgColor = bg_colors[s.status]; - if (bg != bgColor) { - // repaint border - bg = bgColor; - update(); - } -} - -void OnroadWindow::offroadTransition(bool offroad) { - alerts->clear(); -} - -void OnroadWindow::paintEvent(QPaintEvent *event) { - QPainter p(this); - p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255)); -} diff --git a/selfdrive/ui/qt/onroad/onroad_home.h b/selfdrive/ui/qt/onroad/onroad_home.h deleted file mode 100644 index c321d2d44f..0000000000 --- a/selfdrive/ui/qt/onroad/onroad_home.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "selfdrive/ui/qt/onroad/alerts.h" -#include "selfdrive/ui/qt/onroad/annotated_camera.h" - -class OnroadWindow : public QWidget { - Q_OBJECT - -public: - OnroadWindow(QWidget* parent = 0); - -private: - void paintEvent(QPaintEvent *event); - OnroadAlerts *alerts; - AnnotatedCameraWidget *nvg; - QColor bg = bg_colors[STATUS_DISENGAGED]; - QHBoxLayout* split; - -private slots: - void offroadTransition(bool offroad); - void updateState(const UIState &s); -}; diff --git a/selfdrive/ui/qt/prime_state.cc b/selfdrive/ui/qt/prime_state.cc deleted file mode 100644 index f12daf1e3c..0000000000 --- a/selfdrive/ui/qt/prime_state.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include "selfdrive/ui/qt/prime_state.h" - -#include - -#include "selfdrive/ui/qt/api.h" -#include "selfdrive/ui/qt/request_repeater.h" -#include "selfdrive/ui/qt/util.h" - -PrimeState::PrimeState(QObject* parent) : QObject(parent) { - const char *env_prime_type = std::getenv("PRIME_TYPE"); - auto type = env_prime_type ? env_prime_type : Params().get("PrimeType"); - - if (!type.empty()) { - prime_type = static_cast(std::atoi(type.c_str())); - } - - if (auto dongleId = getDongleId()) { - QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/"; - RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5); - QObject::connect(repeater, &RequestRepeater::requestDone, this, &PrimeState::handleReply); - } - - // Emit the initial state change - QTimer::singleShot(1, [this]() { emit changed(prime_type); }); -} - -void PrimeState::handleReply(const QString& response, bool success) { - if (!success) return; - - QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); - if (doc.isNull()) { - qDebug() << "JSON Parse failed on getting pairing and PrimeState status"; - return; - } - - QJsonObject json = doc.object(); - bool is_paired = json["is_paired"].toBool(); - auto type = static_cast(json["prime_type"].toInt()); - setType(is_paired ? type : PrimeState::PRIME_TYPE_UNPAIRED); -} - -void PrimeState::setType(PrimeState::Type type) { - if (type != prime_type) { - prime_type = type; - Params().put("PrimeType", std::to_string(prime_type)); - emit changed(prime_type); - } -} diff --git a/selfdrive/ui/qt/prime_state.h b/selfdrive/ui/qt/prime_state.h deleted file mode 100644 index 0e2e3bb043..0000000000 --- a/selfdrive/ui/qt/prime_state.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -class PrimeState : public QObject { - Q_OBJECT - -public: - - enum Type { - PRIME_TYPE_UNKNOWN = -2, - PRIME_TYPE_UNPAIRED = -1, - PRIME_TYPE_NONE = 0, - PRIME_TYPE_MAGENTA = 1, - PRIME_TYPE_LITE = 2, - PRIME_TYPE_BLUE = 3, - PRIME_TYPE_MAGENTA_NEW = 4, - PRIME_TYPE_PURPLE = 5, - }; - - PrimeState(QObject *parent); - void setType(PrimeState::Type type); - inline PrimeState::Type currentType() const { return prime_type; } - inline bool isSubscribed() const { return prime_type > PrimeState::PRIME_TYPE_NONE; } - -signals: - void changed(PrimeState::Type prime_type); - -private: - void handleReply(const QString &response, bool success); - - PrimeState::Type prime_type = PrimeState::PRIME_TYPE_UNKNOWN; -}; diff --git a/selfdrive/ui/qt/qt_window.cc b/selfdrive/ui/qt/qt_window.cc deleted file mode 100644 index cdd817ae2f..0000000000 --- a/selfdrive/ui/qt/qt_window.cc +++ /dev/null @@ -1,36 +0,0 @@ -#include "selfdrive/ui/qt/qt_window.h" - -void setMainWindow(QWidget *w) { - const float scale = util::getenv("SCALE", 1.0f); - const QSize sz = QGuiApplication::primaryScreen()->size(); - - if (Hardware::PC() && scale == 1.0 && !(sz - DEVICE_SCREEN_SIZE).isValid()) { - w->setMinimumSize(QSize(640, 480)); // allow resize smaller than fullscreen - w->setMaximumSize(DEVICE_SCREEN_SIZE); - w->resize(sz); - } else { - w->setFixedSize(DEVICE_SCREEN_SIZE * scale); - } - w->show(); - -#ifdef __TICI__ - QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); - wl_surface *s = reinterpret_cast(native->nativeResourceForWindow("surface", w->windowHandle())); - wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270); - wl_surface_commit(s); - - w->setWindowState(Qt::WindowFullScreen); - w->setVisible(true); - - // ensure we have a valid eglDisplay, otherwise the ui will silently fail - void *egl = native->nativeResourceForWindow("egldisplay", w->windowHandle()); - assert(egl != nullptr); -#endif -} - - -extern "C" { - void set_main_window(void *w) { - setMainWindow((QWidget*)w); - } -} diff --git a/selfdrive/ui/qt/qt_window.h b/selfdrive/ui/qt/qt_window.h deleted file mode 100644 index b9783477eb..0000000000 --- a/selfdrive/ui/qt/qt_window.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#ifdef __TICI__ -#include -#include -#include -#endif - -#include "system/hardware/hw.h" - -const QString ASSET_PATH = ":/"; -const QSize DEVICE_SCREEN_SIZE = {2160, 1080}; - -void setMainWindow(QWidget *w); diff --git a/selfdrive/ui/qt/request_repeater.cc b/selfdrive/ui/qt/request_repeater.cc deleted file mode 100644 index 7aa731898c..0000000000 --- a/selfdrive/ui/qt/request_repeater.cc +++ /dev/null @@ -1,27 +0,0 @@ -#include "selfdrive/ui/qt/request_repeater.h" - -RequestRepeater::RequestRepeater(QObject *parent, const QString &requestURL, const QString &cacheKey, - int period, bool while_onroad) : HttpRequest(parent) { - timer = new QTimer(this); - timer->setTimerType(Qt::VeryCoarseTimer); - QObject::connect(timer, &QTimer::timeout, [=]() { - if ((!uiState()->scene.started || while_onroad) && device()->isAwake() && !active()) { - sendRequest(requestURL); - } - }); - - 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); }); - } - QObject::connect(this, &HttpRequest::requestDone, [=](const QString &resp, bool success) { - if (success && resp != prevResp) { - params.put(cacheKey.toStdString(), resp.toStdString()); - prevResp = resp; - } - }); - } -} diff --git a/selfdrive/ui/qt/request_repeater.h b/selfdrive/ui/qt/request_repeater.h deleted file mode 100644 index c0e2758273..0000000000 --- a/selfdrive/ui/qt/request_repeater.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "common/util.h" -#include "selfdrive/ui/qt/api.h" -#include "selfdrive/ui/ui.h" - -class RequestRepeater : public HttpRequest { -public: - RequestRepeater(QObject *parent, const QString &requestURL, const QString &cacheKey = "", int period = 0, bool while_onroad=false); - -private: - Params params; - QTimer *timer; - QString prevResp; -}; diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc deleted file mode 100644 index e9c6a2f7c0..0000000000 --- a/selfdrive/ui/qt/sidebar.cc +++ /dev/null @@ -1,165 +0,0 @@ -#include "selfdrive/ui/qt/sidebar.h" - -#include - -#include "selfdrive/ui/qt/util.h" - -void Sidebar::drawMetric(QPainter &p, const QPair &label, QColor c, int y) { - const QRect rect = {30, y, 240, 126}; - - p.setPen(Qt::NoPen); - p.setBrush(QBrush(c)); - p.setClipRect(rect.x() + 4, rect.y(), 18, rect.height(), Qt::ClipOperation::ReplaceClip); - p.drawRoundedRect(QRect(rect.x() + 4, rect.y() + 4, 100, 118), 18, 18); - p.setClipping(false); - - QPen pen = QPen(QColor(0xff, 0xff, 0xff, 0x55)); - pen.setWidth(2); - p.setPen(pen); - p.setBrush(Qt::NoBrush); - p.drawRoundedRect(rect, 20, 20); - - p.setPen(QColor(0xff, 0xff, 0xff)); - p.setFont(InterFont(35, QFont::DemiBold)); - p.drawText(rect.adjusted(22, 0, 0, 0), Qt::AlignCenter, label.first + "\n" + label.second); -} - -Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(false), settings_pressed(false), mic_indicator_pressed(false) { - home_img = loadPixmap("../assets/images/button_home.png", home_btn.size()); - flag_img = loadPixmap("../assets/images/button_flag.png", home_btn.size()); - settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio); - mic_img = loadPixmap("../assets/icons/microphone.png", QSize(30, 30)); - link_img = loadPixmap("../assets/icons/link.png", QSize(60, 60)); - - connect(this, &Sidebar::valueChanged, [=] { update(); }); - - setAttribute(Qt::WA_OpaquePaintEvent); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); - setFixedWidth(300); - - QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState); - - pm = std::make_unique(std::vector{"bookmarkButton"}); -} - -void Sidebar::mousePressEvent(QMouseEvent *event) { - if (onroad && home_btn.contains(event->pos())) { - flag_pressed = true; - update(); - } else if (settings_btn.contains(event->pos())) { - settings_pressed = true; - update(); - } else if (recording_audio && mic_indicator_btn.contains(event->pos())) { - mic_indicator_pressed = true; - update(); - } -} - -void Sidebar::mouseReleaseEvent(QMouseEvent *event) { - if (flag_pressed || settings_pressed || mic_indicator_pressed) { - flag_pressed = settings_pressed = mic_indicator_pressed = false; - update(); - } - if (onroad && home_btn.contains(event->pos())) { - MessageBuilder msg; - msg.initEvent().initBookmarkButton(); - pm->send("bookmarkButton", msg); - } else if (settings_btn.contains(event->pos())) { - emit openSettings(); - } else if (recording_audio && mic_indicator_btn.contains(event->pos())) { - emit openSettings(2, "RecordAudio"); - } -} - -void Sidebar::offroadTransition(bool offroad) { - onroad = !offroad; - update(); -} - -void Sidebar::updateState(const UIState &s) { - if (!isVisible()) return; - - auto &sm = *(s.sm); - - networking = networking ? networking : window()->findChild(""); - bool tethering_on = networking && networking->wifi->tethering_on; - auto deviceState = sm["deviceState"].getDeviceState(); - setProperty("netType", tethering_on ? "Hotspot": network_type[deviceState.getNetworkType()]); - int strength = tethering_on ? 4 : (int)deviceState.getNetworkStrength(); - setProperty("netStrength", strength > 0 ? strength + 1 : 0); - - ItemStatus connectStatus; - auto last_ping = deviceState.getLastAthenaPingTime(); - if (last_ping == 0) { - connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color}; - } else { - connectStatus = nanos_since_boot() - last_ping < 80e9 - ? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, good_color} - : ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color}; - } - setProperty("connectStatus", QVariant::fromValue(connectStatus)); - - ItemStatus tempStatus = {{tr("TEMP"), tr("HIGH")}, danger_color}; - auto ts = deviceState.getThermalStatus(); - if (ts == cereal::DeviceState::ThermalStatus::GREEN) { - tempStatus = {{tr("TEMP"), tr("GOOD")}, good_color}; - } else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) { - tempStatus = {{tr("TEMP"), tr("OK")}, warning_color}; - } - setProperty("tempStatus", QVariant::fromValue(tempStatus)); - - ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, good_color}; - if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) { - pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color}; - } - setProperty("pandaStatus", QVariant::fromValue(pandaStatus)); - - setProperty("recordingAudio", s.scene.recording_audio); -} - -void Sidebar::paintEvent(QPaintEvent *event) { - QPainter p(this); - p.setPen(Qt::NoPen); - p.setRenderHint(QPainter::Antialiasing); - - p.fillRect(rect(), QColor(57, 57, 57)); - - // buttons - p.setOpacity(settings_pressed ? 0.65 : 1.0); - p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_img); - p.setOpacity(onroad && flag_pressed ? 0.65 : 1.0); - p.drawPixmap(home_btn.x(), home_btn.y(), onroad ? flag_img : home_img); - if (recording_audio) { - p.setBrush(danger_color); - p.setOpacity(mic_indicator_pressed ? 0.65 : 1.0); - p.drawRoundedRect(mic_indicator_btn, mic_indicator_btn.height() / 2, mic_indicator_btn.height() / 2); - int icon_x = mic_indicator_btn.x() + (mic_indicator_btn.width() - mic_img.width()) / 2; - int icon_y = mic_indicator_btn.y() + (mic_indicator_btn.height() - mic_img.height()) / 2; - p.drawPixmap(icon_x, icon_y, mic_img); - } - p.setOpacity(1.0); - - // network - int x = 58; - const QColor gray(0x54, 0x54, 0x54); - for (int i = 0; i < 5; ++i) { - p.setBrush(i < net_strength ? Qt::white : gray); - p.drawEllipse(x, 196, 27, 27); - x += 37; - } - - p.setFont(InterFont(35)); - p.setPen(QColor(0xff, 0xff, 0xff)); - const QRect r = QRect(58, 247, width() - 100, 50); - - if (net_type == "Hotspot") { - p.drawPixmap(r.x(), r.y() + (r.height() - link_img.height()) / 2, link_img); - } else { - p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, net_type); - } - - // metrics - drawMetric(p, temp_status.first, temp_status.second, 338); - drawMetric(p, panda_status.first, panda_status.second, 496); - drawMetric(p, connect_status.first, connect_status.second, 654); -} diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h deleted file mode 100644 index 6a2b7b6951..0000000000 --- a/selfdrive/ui/qt/sidebar.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/network/networking.h" - -typedef QPair, QColor> ItemStatus; -Q_DECLARE_METATYPE(ItemStatus); - -class Sidebar : public QFrame { - Q_OBJECT - Q_PROPERTY(ItemStatus connectStatus MEMBER connect_status NOTIFY valueChanged); - Q_PROPERTY(ItemStatus pandaStatus MEMBER panda_status NOTIFY valueChanged); - Q_PROPERTY(ItemStatus tempStatus MEMBER temp_status NOTIFY valueChanged); - Q_PROPERTY(QString netType MEMBER net_type NOTIFY valueChanged); - Q_PROPERTY(int netStrength MEMBER net_strength NOTIFY valueChanged); - Q_PROPERTY(bool recordingAudio MEMBER recording_audio NOTIFY valueChanged); - -public: - explicit Sidebar(QWidget* parent = 0); - -signals: - void openSettings(int index = 0, const QString ¶m = ""); - void valueChanged(); - -public slots: - void offroadTransition(bool offroad); - void updateState(const UIState &s); - -protected: - void paintEvent(QPaintEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void drawMetric(QPainter &p, const QPair &label, QColor c, int y); - - QPixmap home_img, flag_img, settings_img, mic_img, link_img; - bool onroad, recording_audio, flag_pressed, settings_pressed, mic_indicator_pressed; - const QMap network_type = { - {cereal::DeviceState::NetworkType::NONE, tr("--")}, - {cereal::DeviceState::NetworkType::WIFI, tr("Wi-Fi")}, - {cereal::DeviceState::NetworkType::ETHERNET, tr("ETH")}, - {cereal::DeviceState::NetworkType::CELL2_G, tr("2G")}, - {cereal::DeviceState::NetworkType::CELL3_G, tr("3G")}, - {cereal::DeviceState::NetworkType::CELL4_G, tr("LTE")}, - {cereal::DeviceState::NetworkType::CELL5_G, tr("5G")} - }; - - const QRect home_btn = QRect(60, 860, 180, 180); - const QRect settings_btn = QRect(50, 35, 200, 117); - const QRect mic_indicator_btn = QRect(158, 252, 75, 40); - const QColor good_color = QColor(255, 255, 255); - const QColor warning_color = QColor(218, 202, 37); - const QColor danger_color = QColor(201, 34, 49); - - ItemStatus connect_status, panda_status, temp_status; - QString net_type; - int net_strength = 0; - -private: - std::unique_ptr pm; - Networking *networking = nullptr; -}; diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc deleted file mode 100644 index 493c203977..0000000000 --- a/selfdrive/ui/qt/util.cc +++ /dev/null @@ -1,228 +0,0 @@ -#include "selfdrive/ui/qt/util.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/swaglog.h" -#include "common/util.h" -#include "system/hardware/hw.h" - -QString getVersion() { - static QString version = QString::fromStdString(Params().get("Version")); - return version; -} - -QString getBrand() { - return QObject::tr("openpilot"); -} - -QString getUserAgent() { - return "openpilot-" + getVersion(); -} - -std::optional getDongleId() { - std::string id = Params().get("DongleId"); - - if (!id.empty() && (id != "UnregisteredDevice")) { - return QString::fromStdString(id); - } else { - return {}; - } -} - -QMap getSupportedLanguages() { - QFile f(":/languages.json"); - f.open(QIODevice::ReadOnly | QIODevice::Text); - QString val = f.readAll(); - - QJsonObject obj = QJsonDocument::fromJson(val.toUtf8()).object(); - QMap map; - for (auto key : obj.keys()) { - map[key] = obj[key].toString(); - } - return map; -} - -QString timeAgo(const QDateTime &date) { - if (!util::system_time_valid()) { - return date.date().toString(); - } - - int diff = date.secsTo(QDateTime::currentDateTimeUtc()); - - QString s; - if (diff < 60) { - s = QObject::tr("now"); - } else if (diff < 60 * 60) { - int minutes = diff / 60; - s = QObject::tr("%n minute(s) ago", "", minutes); - } else if (diff < 60 * 60 * 24) { - int hours = diff / (60 * 60); - s = QObject::tr("%n hour(s) ago", "", hours); - } else if (diff < 3600 * 24 * 7) { - int days = diff / (60 * 60 * 24); - s = QObject::tr("%n day(s) ago", "", days); - } else { - s = date.date().toString(); - } - - return s; -} - -void setQtSurfaceFormat() { - QSurfaceFormat fmt; -#ifdef __APPLE__ - fmt.setVersion(3, 2); - fmt.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); - fmt.setRenderableType(QSurfaceFormat::OpenGL); -#else - fmt.setRenderableType(QSurfaceFormat::OpenGLES); -#endif - fmt.setSamples(16); - fmt.setStencilBufferSize(1); - QSurfaceFormat::setDefaultFormat(fmt); -} - -void sigTermHandler(int s) { - std::signal(s, SIG_DFL); - qApp->quit(); -} - -void initApp(int argc, char *argv[], bool disable_hidpi) { - Hardware::set_display_power(true); - Hardware::set_brightness(65); - - // setup signal handlers to exit gracefully - std::signal(SIGINT, sigTermHandler); - std::signal(SIGTERM, sigTermHandler); - - QString app_dir; -#ifdef __APPLE__ - // Get the devicePixelRatio, and scale accordingly to maintain 1:1 rendering - QApplication tmp(argc, argv); - app_dir = QCoreApplication::applicationDirPath(); - if (disable_hidpi) { - qputenv("QT_SCALE_FACTOR", QString::number(1.0 / tmp.devicePixelRatio()).toLocal8Bit()); - } -#else - app_dir = QFileInfo(util::readlink("/proc/self/exe").c_str()).path(); -#endif - - qputenv("QT_DBL_CLICK_DIST", QByteArray::number(150)); - // ensure the current dir matches the exectuable's directory - QDir::setCurrent(app_dir); - - setQtSurfaceFormat(); -} - -void swagLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - static std::map levels = { - {QtMsgType::QtDebugMsg, CLOUDLOG_DEBUG}, - {QtMsgType::QtInfoMsg, CLOUDLOG_INFO}, - {QtMsgType::QtWarningMsg, CLOUDLOG_WARNING}, - {QtMsgType::QtCriticalMsg, CLOUDLOG_ERROR}, - {QtMsgType::QtSystemMsg, CLOUDLOG_ERROR}, - {QtMsgType::QtFatalMsg, CLOUDLOG_CRITICAL}, - }; - - std::string file, function; - if (context.file != nullptr) file = context.file; - if (context.function != nullptr) function = context.function; - - auto bts = msg.toUtf8(); - cloudlog_e(levels[type], file.c_str(), context.line, function.c_str(), "%s", bts.constData()); -} - - -QWidget* topWidget(QWidget* widget) { - while (widget->parentWidget() != nullptr) widget=widget->parentWidget(); - return widget; -} - -QPixmap loadPixmap(const QString &fileName, const QSize &size, Qt::AspectRatioMode aspectRatioMode) { - if (size.isEmpty()) { - return QPixmap(fileName); - } else { - return QPixmap(fileName).scaled(size, aspectRatioMode, Qt::SmoothTransformation); - } -} - -static QHash load_bootstrap_icons() { - QHash icons; - - QFile f(":/bootstrap-icons.svg"); - if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { - QDomDocument xml; - xml.setContent(&f); - QDomNode n = xml.documentElement().firstChild(); - while (!n.isNull()) { - QDomElement e = n.toElement(); - if (!e.isNull() && e.hasAttribute("id")) { - QString svg_str; - QTextStream stream(&svg_str); - n.save(stream, 0); - svg_str.replace("", ""); - icons[e.attribute("id")] = svg_str.toUtf8(); - } - n = n.nextSibling(); - } - } - return icons; -} - -QPixmap bootstrapPixmap(const QString &id) { - static QHash icons = load_bootstrap_icons(); - - QPixmap pixmap; - if (auto it = icons.find(id); it != icons.end()) { - pixmap.loadFromData(it.value(), "svg"); - } - return pixmap; -} - -bool hasLongitudinalControl(const cereal::CarParams::Reader &car_params) { - // Using the experimental longitudinal toggle, returns whether longitudinal control - // will be active without needing a restart of openpilot - return car_params.getAlphaLongitudinalAvailable() - ? Params().getBool("AlphaLongitudinalEnabled") - : car_params.getOpenpilotLongitudinalControl(); -} - -// ParamWatcher - -ParamWatcher::ParamWatcher(QObject *parent) : QObject(parent) { - watcher = new QFileSystemWatcher(this); - QObject::connect(watcher, &QFileSystemWatcher::fileChanged, this, &ParamWatcher::fileChanged); -} - -void ParamWatcher::fileChanged(const QString &path) { - auto param_name = QFileInfo(path).fileName(); - auto param_value = QString::fromStdString(params.get(param_name.toStdString())); - - auto it = params_hash.find(param_name); - bool content_changed = (it == params_hash.end()) || (it.value() != param_value); - params_hash[param_name] = param_value; - // emit signal when the content changes. - if (content_changed) { - emit paramChanged(param_name, param_value); - } -} - -void ParamWatcher::addParam(const QString ¶m_name) { - watcher->addPath(QString::fromStdString(params.getParamPath(param_name.toStdString()))); -} diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h deleted file mode 100644 index 2bf1a70a62..0000000000 --- a/selfdrive/ui/qt/util.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "cereal/gen/cpp/car.capnp.h" -#include "common/params.h" - -QString getVersion(); -QString getBrand(); -QString getUserAgent(); -std::optional getDongleId(); -QMap getSupportedLanguages(); -void setQtSurfaceFormat(); -void sigTermHandler(int s); -QString timeAgo(const QDateTime &date); -void swagLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); -void initApp(int argc, char *argv[], bool disable_hidpi = true); -QWidget* topWidget(QWidget* widget); -QPixmap loadPixmap(const QString &fileName, const QSize &size = {}, Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio); -QPixmap bootstrapPixmap(const QString &id); -bool hasLongitudinalControl(const cereal::CarParams::Reader &car_params); - -struct InterFont : public QFont { - InterFont(int pixel_size, QFont::Weight weight = QFont::Normal) : QFont("Inter") { - setPixelSize(pixel_size); - setWeight(weight); - } -}; - -class ParamWatcher : public QObject { - Q_OBJECT - -public: - ParamWatcher(QObject *parent); - void addParam(const QString ¶m_name); - -signals: - void paramChanged(const QString ¶m_name, const QString ¶m_value); - -private: - void fileChanged(const QString &path); - - QFileSystemWatcher *watcher; - QHash params_hash; - Params params; -}; diff --git a/selfdrive/ui/qt/widgets/cameraview.cc b/selfdrive/ui/qt/widgets/cameraview.cc deleted file mode 100644 index 5f533cd090..0000000000 --- a/selfdrive/ui/qt/widgets/cameraview.cc +++ /dev/null @@ -1,365 +0,0 @@ -#include "selfdrive/ui/qt/widgets/cameraview.h" - -#ifdef __APPLE__ -#include -#else -#include -#endif - -#include -#include - -namespace { - -const char frame_vertex_shader[] = -#ifdef __APPLE__ - "#version 330 core\n" -#else - "#version 300 es\n" -#endif - "layout(location = 0) in vec4 aPosition;\n" - "layout(location = 1) in vec2 aTexCoord;\n" - "uniform mat4 uTransform;\n" - "out vec2 vTexCoord;\n" - "void main() {\n" - " gl_Position = uTransform * aPosition;\n" - " vTexCoord = aTexCoord;\n" - "}\n"; - -const char frame_fragment_shader[] = -#ifdef __TICI__ - "#version 300 es\n" - "#extension GL_OES_EGL_image_external_essl3 : enable\n" - "precision mediump float;\n" - "uniform samplerExternalOES uTexture;\n" - "in vec2 vTexCoord;\n" - "out vec4 colorOut;\n" - "void main() {\n" - " colorOut = texture(uTexture, vTexCoord);\n" - // gamma to improve worst case visibility when dark - " colorOut.rgb = pow(colorOut.rgb, vec3(1.0/1.28));\n" - "}\n"; -#else -#ifdef __APPLE__ - "#version 330 core\n" -#else - "#version 300 es\n" - "precision mediump float;\n" -#endif - "uniform sampler2D uTextureY;\n" - "uniform sampler2D uTextureUV;\n" - "in vec2 vTexCoord;\n" - "out vec4 colorOut;\n" - "void main() {\n" - " float y = texture(uTextureY, vTexCoord).r;\n" - " vec2 uv = texture(uTextureUV, vTexCoord).rg - 0.5;\n" - " float r = y + 1.402 * uv.y;\n" - " float g = y - 0.344 * uv.x - 0.714 * uv.y;\n" - " float b = y + 1.772 * uv.x;\n" - " colorOut = vec4(r, g, b, 1.0);\n" - "}\n"; -#endif - -} // namespace - -CameraWidget::CameraWidget(std::string stream_name, VisionStreamType type, QWidget* parent) : - stream_name(stream_name), active_stream_type(type), requested_stream_type(type), QOpenGLWidget(parent) { - setAttribute(Qt::WA_OpaquePaintEvent); - qRegisterMetaType>("availableStreams"); - QObject::connect(this, &CameraWidget::vipcThreadConnected, this, &CameraWidget::vipcConnected, Qt::BlockingQueuedConnection); - QObject::connect(this, &CameraWidget::vipcThreadFrameReceived, this, &CameraWidget::vipcFrameReceived, Qt::QueuedConnection); - QObject::connect(this, &CameraWidget::vipcAvailableStreamsUpdated, this, &CameraWidget::availableStreamsUpdated, Qt::QueuedConnection); - QObject::connect(QApplication::instance(), &QCoreApplication::aboutToQuit, this, &CameraWidget::stopVipcThread); -} - -CameraWidget::~CameraWidget() { - makeCurrent(); - stopVipcThread(); - if (isValid()) { - glDeleteVertexArrays(1, &frame_vao); - glDeleteBuffers(1, &frame_vbo); - glDeleteBuffers(1, &frame_ibo); -#ifndef __TICI__ - glDeleteTextures(2, textures); -#endif - } - doneCurrent(); -} - -// Qt uses device-independent pixels, depending on platform this may be -// different to what OpenGL uses -int CameraWidget::glWidth() { - return width() * devicePixelRatio(); -} - -int CameraWidget::glHeight() { - return height() * devicePixelRatio(); -} - -void CameraWidget::initializeGL() { - initializeOpenGLFunctions(); - - program = std::make_unique(context()); - bool ret = program->addShaderFromSourceCode(QOpenGLShader::Vertex, frame_vertex_shader); - assert(ret); - ret = program->addShaderFromSourceCode(QOpenGLShader::Fragment, frame_fragment_shader); - assert(ret); - - program->link(); - GLint frame_pos_loc = program->attributeLocation("aPosition"); - GLint frame_texcoord_loc = program->attributeLocation("aTexCoord"); - - auto [x1, x2, y1, y2] = requested_stream_type == VISION_STREAM_DRIVER ? std::tuple(0.f, 1.f, 1.f, 0.f) : std::tuple(1.f, 0.f, 1.f, 0.f); - const uint8_t frame_indicies[] = {0, 1, 2, 0, 2, 3}; - const float frame_coords[4][4] = { - {-1.0, -1.0, x2, y1}, // bl - {-1.0, 1.0, x2, y2}, // tl - { 1.0, 1.0, x1, y2}, // tr - { 1.0, -1.0, x1, y1}, // br - }; - - glGenVertexArrays(1, &frame_vao); - glBindVertexArray(frame_vao); - glGenBuffers(1, &frame_vbo); - glBindBuffer(GL_ARRAY_BUFFER, frame_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(frame_coords), frame_coords, GL_STATIC_DRAW); - glEnableVertexAttribArray(frame_pos_loc); - glVertexAttribPointer(frame_pos_loc, 2, GL_FLOAT, GL_FALSE, - sizeof(frame_coords[0]), (const void *)0); - glEnableVertexAttribArray(frame_texcoord_loc); - glVertexAttribPointer(frame_texcoord_loc, 2, GL_FLOAT, GL_FALSE, - sizeof(frame_coords[0]), (const void *)(sizeof(float) * 2)); - glGenBuffers(1, &frame_ibo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, frame_ibo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(frame_indicies), frame_indicies, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - glUseProgram(program->programId()); - -#ifdef __TICI__ - glUniform1i(program->uniformLocation("uTexture"), 0); -#else - glGenTextures(2, textures); - glUniform1i(program->uniformLocation("uTextureY"), 0); - glUniform1i(program->uniformLocation("uTextureUV"), 1); -#endif -} - -void CameraWidget::showEvent(QShowEvent *event) { - if (!vipc_thread) { - clearFrames(); - vipc_thread = new QThread(); - connect(vipc_thread, &QThread::started, [=]() { vipcThread(); }); - connect(vipc_thread, &QThread::finished, vipc_thread, &QObject::deleteLater); - vipc_thread->start(); - } -} - -void CameraWidget::stopVipcThread() { - makeCurrent(); - if (vipc_thread) { - vipc_thread->requestInterruption(); - vipc_thread->quit(); - vipc_thread->wait(); - vipc_thread = nullptr; - } - -#ifdef __TICI__ - EGLDisplay egl_display = eglGetCurrentDisplay(); - assert(egl_display != EGL_NO_DISPLAY); - for (auto &pair : egl_images) { - eglDestroyImageKHR(egl_display, pair.second); - assert(eglGetError() == EGL_SUCCESS); - } - egl_images.clear(); -#endif -} - -void CameraWidget::availableStreamsUpdated(std::set streams) { - available_streams = streams; -} - -mat4 CameraWidget::calcFrameMatrix() { - // Scale the frame to fit the widget while maintaining the aspect ratio. - float widget_aspect_ratio = (float)width() / height(); - float frame_aspect_ratio = (float)stream_width / stream_height; - float zx = std::min(frame_aspect_ratio / widget_aspect_ratio, 1.0f); - float zy = std::min(widget_aspect_ratio / frame_aspect_ratio, 1.0f); - - return mat4{{ - zx, 0.0, 0.0, 0.0, - 0.0, zy, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, - }}; -} - -void CameraWidget::paintGL() { - glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF()); - glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - - std::lock_guard lk(frame_lock); - if (frames.empty()) return; - - int frame_idx = frames.size() - 1; - - // Always draw latest frame until sync logic is more stable - // for (frame_idx = 0; frame_idx < frames.size() - 1; frame_idx++) { - // if (frames[frame_idx].first == draw_frame_id) break; - // } - - // Log duplicate/dropped frames - if (frames[frame_idx].first == prev_frame_id) { - qDebug() << "Drawing same frame twice" << frames[frame_idx].first; - } else if (frames[frame_idx].first != prev_frame_id + 1) { - qDebug() << "Skipped frame" << frames[frame_idx].first; - } - prev_frame_id = frames[frame_idx].first; - VisionBuf *frame = frames[frame_idx].second; - assert(frame != nullptr); - - auto frame_mat = calcFrameMatrix(); - - glViewport(0, 0, glWidth(), glHeight()); - glBindVertexArray(frame_vao); - glUseProgram(program->programId()); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - -#ifdef __TICI__ - // no frame copy - glActiveTexture(GL_TEXTURE0); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_images[frame->idx]); - assert(glGetError() == GL_NO_ERROR); -#else - // fallback to copy - glPixelStorei(GL_UNPACK_ROW_LENGTH, stream_stride); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, textures[0]); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, stream_width, stream_height, GL_RED, GL_UNSIGNED_BYTE, frame->y); - assert(glGetError() == GL_NO_ERROR); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, stream_stride/2); - glActiveTexture(GL_TEXTURE0 + 1); - glBindTexture(GL_TEXTURE_2D, textures[1]); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, stream_width/2, stream_height/2, GL_RG, GL_UNSIGNED_BYTE, frame->uv); - assert(glGetError() == GL_NO_ERROR); -#endif - - glUniformMatrix4fv(program->uniformLocation("uTransform"), 1, GL_TRUE, frame_mat.v); - glEnableVertexAttribArray(0); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0); - glDisableVertexAttribArray(0); - glBindVertexArray(0); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); -} - -void CameraWidget::vipcConnected(VisionIpcClient *vipc_client) { - makeCurrent(); - stream_width = vipc_client->buffers[0].width; - stream_height = vipc_client->buffers[0].height; - stream_stride = vipc_client->buffers[0].stride; - -#ifdef __TICI__ - EGLDisplay egl_display = eglGetCurrentDisplay(); - assert(egl_display != EGL_NO_DISPLAY); - for (auto &pair : egl_images) { - eglDestroyImageKHR(egl_display, pair.second); - } - egl_images.clear(); - - for (int i = 0; i < vipc_client->num_buffers; i++) { // import buffers into OpenGL - int fd = dup(vipc_client->buffers[i].fd); // eglDestroyImageKHR will close, so duplicate - EGLint img_attrs[] = { - EGL_WIDTH, (int)vipc_client->buffers[i].width, - EGL_HEIGHT, (int)vipc_client->buffers[i].height, - EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_NV12, - EGL_DMA_BUF_PLANE0_FD_EXT, fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, - EGL_DMA_BUF_PLANE0_PITCH_EXT, (int)vipc_client->buffers[i].stride, - EGL_DMA_BUF_PLANE1_FD_EXT, fd, - EGL_DMA_BUF_PLANE1_OFFSET_EXT, (int)vipc_client->buffers[i].uv_offset, - EGL_DMA_BUF_PLANE1_PITCH_EXT, (int)vipc_client->buffers[i].stride, - EGL_NONE - }; - egl_images[i] = eglCreateImageKHR(egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, 0, img_attrs); - assert(eglGetError() == EGL_SUCCESS); - } -#else - glBindTexture(GL_TEXTURE_2D, textures[0]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, stream_width, stream_height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); - assert(glGetError() == GL_NO_ERROR); - - glBindTexture(GL_TEXTURE_2D, textures[1]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, stream_width/2, stream_height/2, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr); - assert(glGetError() == GL_NO_ERROR); -#endif -} - -void CameraWidget::vipcFrameReceived() { - update(); -} - -void CameraWidget::vipcThread() { - VisionStreamType cur_stream = requested_stream_type; - std::unique_ptr vipc_client; - VisionIpcBufExtra meta_main = {0}; - - while (!QThread::currentThread()->isInterruptionRequested()) { - if (!vipc_client || cur_stream != requested_stream_type) { - clearFrames(); - qDebug().nospace() << "connecting to stream " << requested_stream_type << ", was connected to " << cur_stream; - cur_stream = requested_stream_type; - vipc_client.reset(new VisionIpcClient(stream_name, cur_stream, false)); - } - active_stream_type = cur_stream; - - if (!vipc_client->connected) { - clearFrames(); - auto streams = VisionIpcClient::getAvailableStreams(stream_name, false); - if (streams.empty()) { - QThread::msleep(100); - continue; - } - emit vipcAvailableStreamsUpdated(streams); - - if (!vipc_client->connect(false)) { - QThread::msleep(100); - continue; - } - emit vipcThreadConnected(vipc_client.get()); - } - - if (VisionBuf *buf = vipc_client->recv(&meta_main, 1000)) { - { - std::lock_guard lk(frame_lock); - frames.push_back(std::make_pair(meta_main.frame_id, buf)); - while (frames.size() > FRAME_BUFFER_SIZE) { - frames.pop_front(); - } - } - emit vipcThreadFrameReceived(); - } else { - if (!isVisible()) { - vipc_client->connected = false; - } - } - } -} - -void CameraWidget::clearFrames() { - std::lock_guard lk(frame_lock); - frames.clear(); - available_streams.clear(); -} diff --git a/selfdrive/ui/qt/widgets/cameraview.h b/selfdrive/ui/qt/widgets/cameraview.h deleted file mode 100644 index 598603a08a..0000000000 --- a/selfdrive/ui/qt/widgets/cameraview.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef __TICI__ -#define EGL_EGLEXT_PROTOTYPES -#define EGL_NO_X11 -#define GL_TEXTURE_EXTERNAL_OES 0x8D65 -#include -#include -#include -#endif - -#include "msgq/visionipc/visionipc_client.h" -#include "selfdrive/ui/ui.h" - -const int FRAME_BUFFER_SIZE = 5; - -class CameraWidget : public QOpenGLWidget, protected QOpenGLFunctions { - Q_OBJECT - -public: - using QOpenGLWidget::QOpenGLWidget; - explicit CameraWidget(std::string stream_name, VisionStreamType stream_type, QWidget* parent = nullptr); - ~CameraWidget(); - void setBackgroundColor(const QColor &color) { bg = color; } - void setFrameId(int frame_id) { draw_frame_id = frame_id; } - void setStreamType(VisionStreamType type) { requested_stream_type = type; } - VisionStreamType getStreamType() { return active_stream_type; } - void stopVipcThread(); - -signals: - void clicked(); - void vipcThreadConnected(VisionIpcClient *); - void vipcThreadFrameReceived(); - void vipcAvailableStreamsUpdated(std::set); - -protected: - void paintGL() override; - void initializeGL() override; - void showEvent(QShowEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override { emit clicked(); } - virtual mat4 calcFrameMatrix(); - void vipcThread(); - void clearFrames(); - - int glWidth(); - int glHeight(); - - GLuint frame_vao, frame_vbo, frame_ibo; - GLuint textures[2]; - std::unique_ptr program; - QColor bg = QColor("#000000"); - -#ifdef __TICI__ - std::map egl_images; -#endif - - std::string stream_name; - int stream_width = 0; - int stream_height = 0; - int stream_stride = 0; - std::atomic active_stream_type; - std::atomic requested_stream_type; - std::set available_streams; - QThread *vipc_thread = nullptr; - std::recursive_mutex frame_lock; - std::deque> frames; - uint32_t draw_frame_id = 0; - uint32_t prev_frame_id = 0; - -protected slots: - void vipcConnected(VisionIpcClient *vipc_client); - void vipcFrameReceived(); - void availableStreamsUpdated(std::set streams); -}; - -Q_DECLARE_METATYPE(std::set); diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc deleted file mode 100644 index 40dda971f5..0000000000 --- a/selfdrive/ui/qt/widgets/controls.cc +++ /dev/null @@ -1,141 +0,0 @@ -#include "selfdrive/ui/qt/widgets/controls.h" - -#include -#include - -AbstractControl::AbstractControl(const QString &title, const QString &desc, const QString &icon, QWidget *parent) : QFrame(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setMargin(0); - - hlayout = new QHBoxLayout; - hlayout->setMargin(0); - hlayout->setSpacing(20); - - // left icon - icon_label = new QLabel(this); - hlayout->addWidget(icon_label); - if (!icon.isEmpty()) { - icon_pixmap = QPixmap(icon).scaledToWidth(80, Qt::SmoothTransformation); - icon_label->setPixmap(icon_pixmap); - icon_label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - } - icon_label->setVisible(!icon.isEmpty()); - - // title - title_label = new QPushButton(title); - title_label->setFixedHeight(120); - title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; border: none;"); - hlayout->addWidget(title_label, 1); - - // value next to control button - value = new ElidedLabel(); - value->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - value->setStyleSheet("color: #aaaaaa"); - hlayout->addWidget(value); - - main_layout->addLayout(hlayout); - - // description - description = new QLabel(desc); - description->setContentsMargins(40, 20, 40, 20); - description->setStyleSheet("font-size: 40px; color: grey"); - description->setWordWrap(true); - description->setVisible(false); - main_layout->addWidget(description); - - connect(title_label, &QPushButton::clicked, [=]() { - if (!description->isVisible()) { - emit showDescriptionEvent(); - } - - if (!description->text().isEmpty()) { - description->setVisible(!description->isVisible()); - } - }); - - main_layout->addStretch(); -} - -void AbstractControl::hideEvent(QHideEvent *e) { - if (description != nullptr) { - description->hide(); - } -} - -// controls - -ButtonControl::ButtonControl(const QString &title, const QString &text, const QString &desc, QWidget *parent) : AbstractControl(title, desc, "", parent) { - btn.setText(text); - btn.setStyleSheet(R"( - QPushButton { - padding: 0; - border-radius: 50px; - font-size: 35px; - font-weight: 500; - color: #E4E4E4; - background-color: #393939; - } - QPushButton:pressed { - background-color: #4a4a4a; - } - QPushButton:disabled { - color: #33E4E4E4; - } - )"); - btn.setFixedSize(250, 100); - QObject::connect(&btn, &QPushButton::clicked, this, &ButtonControl::clicked); - hlayout->addWidget(&btn); -} - -// ElidedLabel - -ElidedLabel::ElidedLabel(QWidget *parent) : ElidedLabel({}, parent) {} - -ElidedLabel::ElidedLabel(const QString &text, QWidget *parent) : QLabel(text.trimmed(), parent) { - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - setMinimumWidth(1); -} - -void ElidedLabel::resizeEvent(QResizeEvent* event) { - QLabel::resizeEvent(event); - lastText_ = elidedText_ = ""; -} - -void ElidedLabel::paintEvent(QPaintEvent *event) { - const QString curText = text(); - if (curText != lastText_) { - elidedText_ = fontMetrics().elidedText(curText, Qt::ElideRight, contentsRect().width()); - lastText_ = curText; - } - - QPainter painter(this); - drawFrame(&painter); - QStyleOption opt; - opt.initFrom(this); - style()->drawItemText(&painter, contentsRect(), alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole()); -} - -// ParamControl - -ParamControl::ParamControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent) - : ToggleControl(title, desc, icon, false, parent) { - key = param.toStdString(); - QObject::connect(this, &ParamControl::toggleFlipped, this, &ParamControl::toggleClicked); -} - -void ParamControl::toggleClicked(bool state) { - auto do_confirm = [this]() { - QString content("

" + title_label->text() + "


" - "

" + getDescription() + "

"); - return ConfirmationDialog(content, tr("Enable"), tr("Cancel"), true, this).exec(); - }; - - bool confirmed = store_confirm && params.getBool(key + "Confirmed"); - if (!confirm || confirmed || !state || do_confirm()) { - if (store_confirm && state) params.putBool(key + "Confirmed", true); - params.putBool(key, state); - setIcon(state); - } else { - toggle.togglePosition(); - } -} diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h deleted file mode 100644 index 3342de5324..0000000000 --- a/selfdrive/ui/qt/widgets/controls.h +++ /dev/null @@ -1,319 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "common/params.h" -#include "selfdrive/ui/qt/widgets/input.h" -#include "selfdrive/ui/qt/widgets/toggle.h" - -class ElidedLabel : public QLabel { - Q_OBJECT - -public: - explicit ElidedLabel(QWidget *parent = 0); - explicit ElidedLabel(const QString &text, QWidget *parent = 0); - -signals: - void clicked(); - -protected: - void paintEvent(QPaintEvent *event) override; - void resizeEvent(QResizeEvent* event) override; - void mouseReleaseEvent(QMouseEvent *event) override { - if (rect().contains(event->pos())) { - emit clicked(); - } - } - QString lastText_, elidedText_; -}; - - -class AbstractControl : public QFrame { - Q_OBJECT - -public: - void setDescription(const QString &desc) { - if (description) description->setText(desc); - } - - void setTitle(const QString &title) { - title_label->setText(title); - } - - void setValue(const QString &val) { - value->setText(val); - } - - const QString getDescription() { - return description->text(); - } - - QLabel *icon_label; - QPixmap icon_pixmap; - -public slots: - void showDescription() { - description->setVisible(true); - } - -signals: - void showDescriptionEvent(); - -protected: - AbstractControl(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); - void hideEvent(QHideEvent *e) override; - - QHBoxLayout *hlayout; - QPushButton *title_label; - -private: - ElidedLabel *value; - QLabel *description = nullptr; -}; - -// widget to display a value -class LabelControl : public AbstractControl { - Q_OBJECT - -public: - LabelControl(const QString &title, const QString &text = "", const QString &desc = "", QWidget *parent = nullptr) : AbstractControl(title, desc, "", parent) { - label.setText(text); - label.setAlignment(Qt::AlignRight | Qt::AlignVCenter); - hlayout->addWidget(&label); - } - void setText(const QString &text) { label.setText(text); } - -private: - ElidedLabel label; -}; - -// widget for a button with a label -class ButtonControl : public AbstractControl { - Q_OBJECT - -public: - ButtonControl(const QString &title, const QString &text, const QString &desc = "", QWidget *parent = nullptr); - inline void setText(const QString &text) { btn.setText(text); } - inline QString text() const { return btn.text(); } - -signals: - void clicked(); - -public slots: - void setEnabled(bool enabled) { btn.setEnabled(enabled); } - -private: - QPushButton btn; -}; - -class ToggleControl : public AbstractControl { - Q_OBJECT - -public: - ToggleControl(const QString &title, const QString &desc = "", const QString &icon = "", const bool state = false, QWidget *parent = nullptr) : AbstractControl(title, desc, icon, parent) { - toggle.setFixedSize(150, 100); - if (state) { - toggle.togglePosition(); - } - hlayout->addWidget(&toggle); - QObject::connect(&toggle, &Toggle::stateChanged, this, &ToggleControl::toggleFlipped); - } - - void setEnabled(bool enabled) { - toggle.setEnabled(enabled); - toggle.update(); - } - -signals: - void toggleFlipped(bool state); - -protected: - Toggle toggle; -}; - -// widget to toggle params -class ParamControl : public ToggleControl { - Q_OBJECT - -public: - ParamControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr); - void setConfirmation(bool _confirm, bool _store_confirm) { - confirm = _confirm; - store_confirm = _store_confirm; - } - - void setActiveIcon(const QString &icon) { - active_icon_pixmap = QPixmap(icon).scaledToWidth(80, Qt::SmoothTransformation); - } - - void refresh() { - bool state = params.getBool(key); - if (state != toggle.on) { - toggle.togglePosition(); - setIcon(state); - } - } - - void showEvent(QShowEvent *event) override { - refresh(); - } - -private: - void toggleClicked(bool state); - void setIcon(bool state) { - if (state && !active_icon_pixmap.isNull()) { - icon_label->setPixmap(active_icon_pixmap); - } else if (!icon_pixmap.isNull()) { - icon_label->setPixmap(icon_pixmap); - } - } - - std::string key; - Params params; - QPixmap active_icon_pixmap; - bool confirm = false; - bool store_confirm = false; -}; - -class MultiButtonControl : public AbstractControl { - Q_OBJECT -public: - MultiButtonControl(const QString &title, const QString &desc, const QString &icon, - const std::vector &button_texts, const int minimum_button_width = 225) : AbstractControl(title, desc, icon) { - const QString style = R"( - QPushButton { - border-radius: 50px; - font-size: 40px; - font-weight: 500; - height:100px; - padding: 0 25 0 25; - color: #E4E4E4; - background-color: #393939; - } - QPushButton:pressed { - background-color: #4a4a4a; - } - QPushButton:checked:enabled { - background-color: #33Ab4C; - } - QPushButton:checked:disabled { - background-color: #9933Ab4C; - } - QPushButton:disabled { - color: #33E4E4E4; - } - )"; - - button_group = new QButtonGroup(this); - button_group->setExclusive(true); - for (int i = 0; i < button_texts.size(); i++) { - QPushButton *button = new QPushButton(button_texts[i], this); - button->setCheckable(true); - button->setChecked(i == 0); - button->setStyleSheet(style); - button->setMinimumWidth(minimum_button_width); - hlayout->addWidget(button); - button_group->addButton(button, i); - } - - QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonClicked), this, &MultiButtonControl::buttonClicked); - } - - void setEnabled(bool enable) { - for (auto btn : button_group->buttons()) { - btn->setEnabled(enable); - } - } - - void setCheckedButton(int id) { - button_group->button(id)->setChecked(true); - } - -signals: - void buttonClicked(int id); - -protected: - QButtonGroup *button_group; -}; - -class ButtonParamControl : public MultiButtonControl { - Q_OBJECT -public: - ButtonParamControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const std::vector &button_texts, const int minimum_button_width = 225) : MultiButtonControl(title, desc, icon, - button_texts, minimum_button_width) { - key = param.toStdString(); - int value = atoi(params.get(key).c_str()); - - if (value > 0 && value < button_group->buttons().size()) { - button_group->button(value)->setChecked(true); - } - - QObject::connect(this, QOverload::of(&MultiButtonControl::buttonClicked), [=](int id) { - params.put(key, std::to_string(id)); - }); - } - - void refresh() { - int value = atoi(params.get(key).c_str()); - button_group->button(value)->setChecked(true); - } - - void showEvent(QShowEvent *event) override { - refresh(); - } - -private: - std::string key; - Params params; -}; - -class ListWidget : public QWidget { - Q_OBJECT - public: - explicit ListWidget(QWidget *parent = 0) : QWidget(parent), outer_layout(this) { - outer_layout.setMargin(0); - outer_layout.setSpacing(0); - outer_layout.addLayout(&inner_layout); - inner_layout.setMargin(0); - inner_layout.setSpacing(25); // default spacing is 25 - outer_layout.addStretch(1); - } - inline void addItem(QWidget *w) { inner_layout.addWidget(w); } - inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); } - inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); } - -private: - void paintEvent(QPaintEvent *) override { - QPainter p(this); - p.setPen(Qt::gray); - for (int i = 0; i < inner_layout.count() - 1; ++i) { - QWidget *widget = inner_layout.itemAt(i)->widget(); - if (widget == nullptr || widget->isVisible()) { - QRect r = inner_layout.itemAt(i)->geometry(); - int bottom = r.bottom() + inner_layout.spacing() / 2; - p.drawLine(r.left() + 40, bottom, r.right() - 40, bottom); - } - } - } - QVBoxLayout outer_layout; - QVBoxLayout inner_layout; -}; - -// convenience class for wrapping layouts -class LayoutWidget : public QWidget { - Q_OBJECT - -public: - LayoutWidget(QLayout *l, QWidget *parent = nullptr) : QWidget(parent) { - setLayout(l); - } -}; diff --git a/selfdrive/ui/qt/widgets/input.cc b/selfdrive/ui/qt/widgets/input.cc deleted file mode 100644 index 0cbf14931b..0000000000 --- a/selfdrive/ui/qt/widgets/input.cc +++ /dev/null @@ -1,336 +0,0 @@ -#include "selfdrive/ui/qt/widgets/input.h" - -#include -#include - -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - - -DialogBase::DialogBase(QWidget *parent) : QDialog(parent) { - Q_ASSERT(parent != nullptr); - parent->installEventFilter(this); - - setStyleSheet(R"( - * { - outline: none; - color: white; - font-family: Inter; - } - DialogBase { - background-color: black; - } - QPushButton { - height: 160; - font-size: 55px; - font-weight: 400; - border-radius: 10px; - color: white; - background-color: #333333; - } - QPushButton:pressed { - background-color: #444444; - } - )"); -} - -bool DialogBase::eventFilter(QObject *o, QEvent *e) { - if (o == parent() && e->type() == QEvent::Hide) { - reject(); - } - return QDialog::eventFilter(o, e); -} - -int DialogBase::exec() { - setMainWindow(this); - return QDialog::exec(); -} - -InputDialog::InputDialog(const QString &title, QWidget *parent, const QString &subtitle, bool secret) : DialogBase(parent) { - main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(50, 55, 50, 50); - main_layout->setSpacing(0); - - // build header - QHBoxLayout *header_layout = new QHBoxLayout(); - - QVBoxLayout *vlayout = new QVBoxLayout; - header_layout->addLayout(vlayout); - label = new QLabel(title, this); - label->setStyleSheet("font-size: 90px; font-weight: bold;"); - vlayout->addWidget(label, 1, Qt::AlignTop | Qt::AlignLeft); - - if (!subtitle.isEmpty()) { - sublabel = new QLabel(subtitle, this); - sublabel->setStyleSheet("font-size: 55px; font-weight: light; color: #BDBDBD;"); - vlayout->addWidget(sublabel, 1, Qt::AlignTop | Qt::AlignLeft); - } - - QPushButton* cancel_btn = new QPushButton(tr("Cancel")); - cancel_btn->setFixedSize(386, 125); - cancel_btn->setStyleSheet(R"( - QPushButton { - font-size: 48px; - border-radius: 10px; - color: #E4E4E4; - background-color: #333333; - } - QPushButton:pressed { - background-color: #444444; - } - )"); - header_layout->addWidget(cancel_btn, 0, Qt::AlignRight); - QObject::connect(cancel_btn, &QPushButton::clicked, this, &InputDialog::reject); - QObject::connect(cancel_btn, &QPushButton::clicked, this, &InputDialog::cancel); - - main_layout->addLayout(header_layout); - - // text box - main_layout->addStretch(2); - - QWidget *textbox_widget = new QWidget; - textbox_widget->setObjectName("textbox"); - QHBoxLayout *textbox_layout = new QHBoxLayout(textbox_widget); - textbox_layout->setContentsMargins(50, 0, 50, 0); - - textbox_widget->setStyleSheet(R"( - #textbox { - margin-left: 50px; - margin-right: 50px; - border-radius: 0; - border-bottom: 3px solid #BDBDBD; - } - * { - border: none; - font-size: 80px; - font-weight: light; - background-color: transparent; - } - )"); - - line = new QLineEdit(); - line->setStyleSheet("lineedit-password-character: 8226; lineedit-password-mask-delay: 1500;"); - textbox_layout->addWidget(line, 1); - - if (secret) { - eye_btn = new QPushButton(); - eye_btn->setCheckable(true); - eye_btn->setFixedSize(150, 120); - QObject::connect(eye_btn, &QPushButton::toggled, [=](bool checked) { - if (checked) { - eye_btn->setIcon(QIcon(ASSET_PATH + "icons/eye_closed.svg")); - eye_btn->setIconSize(QSize(81, 54)); - line->setEchoMode(QLineEdit::Password); - } else { - eye_btn->setIcon(QIcon(ASSET_PATH + "icons/eye_open.svg")); - eye_btn->setIconSize(QSize(81, 44)); - line->setEchoMode(QLineEdit::Normal); - } - }); - eye_btn->toggle(); - eye_btn->setChecked(false); - textbox_layout->addWidget(eye_btn); - } - - main_layout->addWidget(textbox_widget, 0, Qt::AlignBottom); - main_layout->addSpacing(25); - - k = new Keyboard(this); - QObject::connect(k, &Keyboard::emitEnter, this, &InputDialog::handleEnter); - QObject::connect(k, &Keyboard::emitBackspace, this, [=]() { - line->backspace(); - }); - QObject::connect(k, &Keyboard::emitKey, this, [=](const QString &key) { - line->insert(key.left(1)); - }); - - main_layout->addWidget(k, 2, Qt::AlignBottom); -} - -QString InputDialog::getText(const QString &prompt, QWidget *parent, const QString &subtitle, - bool secret, int minLength, const QString &defaultText) { - InputDialog d(prompt, parent, subtitle, secret); - d.line->setText(defaultText); - d.setMinLength(minLength); - const int ret = d.exec(); - return ret ? d.text() : QString(); -} - -QString InputDialog::text() { - return line->text(); -} - -void InputDialog::show() { - setMainWindow(this); -} - -void InputDialog::handleEnter() { - if (line->text().length() >= minLength) { - done(QDialog::Accepted); - emitText(line->text()); - } else { - setMessage(tr("Need at least %n character(s)!", "", minLength), false); - } -} - -void InputDialog::setMessage(const QString &message, bool clearInputField) { - label->setText(message); - if (clearInputField) { - line->setText(""); - } -} - -void InputDialog::setMinLength(int length) { - minLength = length; -} - -// ConfirmationDialog - -ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString &confirm_text, const QString &cancel_text, - const bool rich, QWidget *parent) : DialogBase(parent) { - QFrame *container = new QFrame(this); - container->setStyleSheet(R"( - QFrame { background-color: #1B1B1B; color: #C9C9C9; } - #confirm_btn { background-color: #465BEA; } - #confirm_btn:pressed { background-color: #3049F4; } - )"); - QVBoxLayout *main_layout = new QVBoxLayout(container); - main_layout->setContentsMargins(32, rich ? 32 : 120, 32, 32); - - QLabel *prompt = new QLabel(prompt_text, this); - prompt->setWordWrap(true); - prompt->setAlignment(rich ? Qt::AlignLeft : Qt::AlignHCenter); - prompt->setStyleSheet((rich ? "font-size: 42px; font-weight: light;" : "font-size: 70px; font-weight: bold;") + QString(" margin: 45px;")); - main_layout->addWidget(rich ? (QWidget*)new ScrollView(prompt, this) : (QWidget*)prompt, 1, Qt::AlignTop); - - // cancel + confirm buttons - QHBoxLayout *btn_layout = new QHBoxLayout(); - btn_layout->setSpacing(30); - main_layout->addLayout(btn_layout); - - if (cancel_text.length()) { - QPushButton* cancel_btn = new QPushButton(cancel_text); - btn_layout->addWidget(cancel_btn); - QObject::connect(cancel_btn, &QPushButton::clicked, this, &ConfirmationDialog::reject); - } - - if (confirm_text.length()) { - QPushButton* confirm_btn = new QPushButton(confirm_text); - confirm_btn->setObjectName("confirm_btn"); - btn_layout->addWidget(confirm_btn); - QObject::connect(confirm_btn, &QPushButton::clicked, this, &ConfirmationDialog::accept); - } - - QVBoxLayout *outer_layout = new QVBoxLayout(this); - int margin = rich ? 100 : 200; - outer_layout->setContentsMargins(margin, margin, margin, margin); - outer_layout->addWidget(container); -} - -bool ConfirmationDialog::alert(const QString &prompt_text, QWidget *parent) { - ConfirmationDialog d(prompt_text, tr("Ok"), "", false, parent); - return d.exec(); -} - -bool ConfirmationDialog::confirm(const QString &prompt_text, const QString &confirm_text, QWidget *parent) { - ConfirmationDialog d(prompt_text, confirm_text, tr("Cancel"), false, parent); - return d.exec(); -} - -bool ConfirmationDialog::rich(const QString &prompt_text, QWidget *parent) { - ConfirmationDialog d(prompt_text, tr("Ok"), "", true, parent); - return d.exec(); -} - -// MultiOptionDialog - -MultiOptionDialog::MultiOptionDialog(const QString &prompt_text, const QStringList &l, const QString ¤t, QWidget *parent) : DialogBase(parent) { - QFrame *container = new QFrame(this); - container->setStyleSheet(R"( - QFrame { background-color: #1B1B1B; } - #confirm_btn[enabled="false"] { background-color: #2B2B2B; } - #confirm_btn:enabled { background-color: #465BEA; } - #confirm_btn:enabled:pressed { background-color: #3049F4; } - )"); - - QVBoxLayout *main_layout = new QVBoxLayout(container); - main_layout->setContentsMargins(55, 50, 55, 50); - - QLabel *title = new QLabel(prompt_text, this); - title->setStyleSheet("font-size: 70px; font-weight: 500;"); - main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); - main_layout->addSpacing(25); - - QWidget *listWidget = new QWidget(this); - QVBoxLayout *listLayout = new QVBoxLayout(listWidget); - listLayout->setSpacing(20); - listWidget->setStyleSheet(R"( - QPushButton { - height: 135; - padding: 0px 50px; - text-align: left; - font-size: 55px; - font-weight: 300; - border-radius: 10px; - background-color: #4F4F4F; - } - QPushButton:checked { background-color: #465BEA; } - )"); - - QButtonGroup *group = new QButtonGroup(listWidget); - group->setExclusive(true); - - QPushButton *confirm_btn = new QPushButton(tr("Select")); - confirm_btn->setObjectName("confirm_btn"); - confirm_btn->setEnabled(false); - - for (const QString &s : l) { - QPushButton *selectionLabel = new QPushButton(s); - selectionLabel->setCheckable(true); - selectionLabel->setChecked(s == current); - QObject::connect(selectionLabel, &QPushButton::toggled, [=](bool checked) { - if (checked) selection = s; - if (selection != current) { - confirm_btn->setEnabled(true); - } else { - confirm_btn->setEnabled(false); - } - }); - - group->addButton(selectionLabel); - listLayout->addWidget(selectionLabel); - } - // add stretch to keep buttons spaced correctly - listLayout->addStretch(1); - - ScrollView *scroll_view = new ScrollView(listWidget, this); - scroll_view->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - - main_layout->addWidget(scroll_view); - main_layout->addSpacing(35); - - // cancel + confirm buttons - QHBoxLayout *blayout = new QHBoxLayout; - main_layout->addLayout(blayout); - blayout->setSpacing(50); - - QPushButton *cancel_btn = new QPushButton(tr("Cancel")); - QObject::connect(cancel_btn, &QPushButton::clicked, this, &ConfirmationDialog::reject); - QObject::connect(confirm_btn, &QPushButton::clicked, this, &ConfirmationDialog::accept); - blayout->addWidget(cancel_btn); - blayout->addWidget(confirm_btn); - - QVBoxLayout *outer_layout = new QVBoxLayout(this); - outer_layout->setContentsMargins(50, 50, 50, 50); - outer_layout->addWidget(container); -} - -QString MultiOptionDialog::getSelection(const QString &prompt_text, const QStringList &l, const QString ¤t, QWidget *parent) { - MultiOptionDialog d(prompt_text, l, current, parent); - if (d.exec()) { - return d.selection; - } - return ""; -} diff --git a/selfdrive/ui/qt/widgets/input.h b/selfdrive/ui/qt/widgets/input.h deleted file mode 100644 index 089e54e4a0..0000000000 --- a/selfdrive/ui/qt/widgets/input.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "selfdrive/ui/qt/widgets/keyboard.h" - - -class DialogBase : public QDialog { - Q_OBJECT - -protected: - DialogBase(QWidget *parent); - bool eventFilter(QObject *o, QEvent *e) override; - -public slots: - int exec() override; -}; - -class InputDialog : public DialogBase { - Q_OBJECT - -public: - explicit InputDialog(const QString &title, QWidget *parent, const QString &subtitle = "", bool secret = false); - static QString getText(const QString &title, QWidget *parent, const QString &subtitle = "", - bool secret = false, int minLength = -1, const QString &defaultText = ""); - QString text(); - void setMessage(const QString &message, bool clearInputField = true); - void setMinLength(int length); - void show(); - -private: - int minLength; - QLineEdit *line; - Keyboard *k; - QLabel *label; - QLabel *sublabel; - QVBoxLayout *main_layout; - QPushButton *eye_btn; - -private slots: - void handleEnter(); - -signals: - void cancel(); - void emitText(const QString &text); -}; - -class ConfirmationDialog : public DialogBase { - Q_OBJECT - -public: - explicit ConfirmationDialog(const QString &prompt_text, const QString &confirm_text, - const QString &cancel_text, const bool rich, QWidget* parent); - static bool alert(const QString &prompt_text, QWidget *parent); - static bool confirm(const QString &prompt_text, const QString &confirm_text, QWidget *parent); - static bool rich(const QString &prompt_text, QWidget *parent); -}; - -class MultiOptionDialog : public DialogBase { - Q_OBJECT - -public: - explicit MultiOptionDialog(const QString &prompt_text, const QStringList &l, const QString ¤t, QWidget *parent); - static QString getSelection(const QString &prompt_text, const QStringList &l, const QString ¤t, QWidget *parent); - QString selection; -}; diff --git a/selfdrive/ui/qt/widgets/keyboard.cc b/selfdrive/ui/qt/widgets/keyboard.cc deleted file mode 100644 index 9ead27b8d5..0000000000 --- a/selfdrive/ui/qt/widgets/keyboard.cc +++ /dev/null @@ -1,182 +0,0 @@ -#include "selfdrive/ui/qt/widgets/keyboard.h" - -#include - -#include -#include -#include -#include -#include - -const QString BACKSPACE_KEY = "⌫"; -const QString ENTER_KEY = "→"; -const QString SHIFT_KEY = "⇧"; -const QString CAPS_LOCK_KEY = "⇪"; - -const QMap KEY_STRETCH = {{" ", 3}, {ENTER_KEY, 2}}; - -const QStringList CONTROL_BUTTONS = {SHIFT_KEY, CAPS_LOCK_KEY, "ABC", "#+=", "123", BACKSPACE_KEY, ENTER_KEY}; - -const float key_spacing_vertical = 20; -const float key_spacing_horizontal = 15; - -KeyButton::KeyButton(const QString &text, QWidget *parent) : QPushButton(text, parent) { - setAttribute(Qt::WA_AcceptTouchEvents); - setFocusPolicy(Qt::NoFocus); -} - -bool KeyButton::event(QEvent *event) { - if (event->type() == QEvent::TouchBegin || event->type() == QEvent::TouchEnd) { - QTouchEvent *touchEvent = static_cast(event); - if (!touchEvent->touchPoints().empty()) { - const QEvent::Type mouseType = event->type() == QEvent::TouchBegin ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease; - QMouseEvent mouseEvent(mouseType, touchEvent->touchPoints().front().pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - QPushButton::event(&mouseEvent); - event->accept(); - parentWidget()->update(); - return true; - } - } - return QPushButton::event(event); -} - -KeyboardLayout::KeyboardLayout(QWidget* parent, const std::vector>& layout) : QWidget(parent) { - QVBoxLayout* main_layout = new QVBoxLayout(this); - main_layout->setMargin(0); - main_layout->setSpacing(0); - - QButtonGroup* btn_group = new QButtonGroup(this); - QObject::connect(btn_group, SIGNAL(buttonClicked(QAbstractButton*)), parent, SLOT(handleButton(QAbstractButton*))); - - for (const auto &s : layout) { - QHBoxLayout *hlayout = new QHBoxLayout; - hlayout->setSpacing(0); - - if (main_layout->count() == 1) { - hlayout->addSpacing(90); - } - - for (const QString &p : s) { - KeyButton* btn = new KeyButton(p); - if (p == BACKSPACE_KEY) { - btn->setAutoRepeat(true); - } else if (p == ENTER_KEY) { - btn->setStyleSheet(R"( - QPushButton { - background-color: #465BEA; - } - QPushButton:pressed { - background-color: #444444; - } - )"); - } - btn->setFixedHeight(135 + key_spacing_vertical); - btn_group->addButton(btn); - hlayout->addWidget(btn, KEY_STRETCH.value(p, 1)); - } - - if (main_layout->count() == 1) { - hlayout->addSpacing(90); - } - - main_layout->addLayout(hlayout); - } - - setStyleSheet(QString(R"( - QPushButton { - font-size: 75px; - margin-left: %1px; - margin-right: %1px; - margin-top: %2px; - margin-bottom: %2px; - padding: 0px; - border-radius: 10px; - color: #dddddd; - background-color: #444444; - } - QPushButton:pressed { - background-color: #333333; - } - )").arg(key_spacing_vertical / 2).arg(key_spacing_horizontal / 2)); -} - -Keyboard::Keyboard(QWidget *parent) : QFrame(parent) { - main_layout = new QStackedLayout(this); - main_layout->setMargin(0); - - // lowercase - std::vector> 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}, - {"123", "/", "-", " ", ".", ENTER_KEY}, - }; - main_layout->addWidget(new KeyboardLayout(this, lowercase)); - - // uppercase - std::vector> uppercase = { - {"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}, - {"123", "/", "-", " ", ".", ENTER_KEY}, - }; - main_layout->addWidget(new KeyboardLayout(this, uppercase)); - - // numbers + specials - std::vector> numbers = { - {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}, - {"-", "/", ":", ";", "(", ")", "$", "&&", "@", "\""}, - {"#+=", ".", ",", "?", "!", "`", BACKSPACE_KEY}, - {"ABC", " ", ".", ENTER_KEY}, - }; - main_layout->addWidget(new KeyboardLayout(this, numbers)); - - // extra specials - std::vector> specials = { - {"[", "]", "{", "}", "#", "%", "^", "*", "+", "="}, - {"_", "\\", "|", "~", "<", ">", "€", "£", "¥", "•"}, - {"123", ".", ",", "?", "!", "'", BACKSPACE_KEY}, - {"ABC", " ", ".", ENTER_KEY}, - }; - main_layout->addWidget(new KeyboardLayout(this, specials)); - - main_layout->setCurrentIndex(0); -} - -void Keyboard::handleCapsPress() { - shift_state = (shift_state + 1) % 3; - bool is_uppercase = shift_state > 0; - main_layout->setCurrentIndex(is_uppercase); - - for (KeyButton* btn : main_layout->currentWidget()->findChildren()) { - if (btn->text() == SHIFT_KEY || btn->text() == CAPS_LOCK_KEY) { - btn->setText(shift_state == 2 ? CAPS_LOCK_KEY : SHIFT_KEY); - btn->setStyleSheet(is_uppercase ? "background-color: #465BEA;" : ""); - } - } -} - -void Keyboard::handleButton(QAbstractButton* btn) { - const QString &key = btn->text(); - if (CONTROL_BUTTONS.contains(key)) { - if (key == "ABC" || key == "123" || key == "#+=") { - int index = (key == "ABC") ? 0 : (key == "123" ? 2 : 3); - main_layout->setCurrentIndex(index); - shift_state = 0; - } else if (key == SHIFT_KEY || key == CAPS_LOCK_KEY) { - handleCapsPress(); - } else if (key == ENTER_KEY) { - main_layout->setCurrentIndex(0); - shift_state = 0; - emit emitEnter(); - } else if (key == BACKSPACE_KEY) { - emit emitBackspace(); - } - } else { - if (shift_state == 1 && "A" <= key && key <= "Z") { - main_layout->setCurrentIndex(0); - shift_state = 0; - } - emit emitKey(key); - } -} diff --git a/selfdrive/ui/qt/widgets/keyboard.h b/selfdrive/ui/qt/widgets/keyboard.h deleted file mode 100644 index e61617283a..0000000000 --- a/selfdrive/ui/qt/widgets/keyboard.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -class KeyButton : public QPushButton { - Q_OBJECT - -public: - KeyButton(const QString &text, QWidget *parent = 0); - bool event(QEvent *event) override; -}; - -class KeyboardLayout : public QWidget { - Q_OBJECT - -public: - explicit KeyboardLayout(QWidget* parent, const std::vector>& layout); -}; - -class Keyboard : public QFrame { - Q_OBJECT - -public: - explicit Keyboard(QWidget *parent = 0); - -private: - QStackedLayout* main_layout; - int shift_state = 0; - -private slots: - void handleButton(QAbstractButton* m_button); - void handleCapsPress(); - -signals: - void emitKey(const QString &s); - void emitBackspace(); - void emitEnter(); -}; diff --git a/selfdrive/ui/qt/widgets/offroad_alerts.cc b/selfdrive/ui/qt/widgets/offroad_alerts.cc deleted file mode 100644 index 3a4828a2a8..0000000000 --- a/selfdrive/ui/qt/widgets/offroad_alerts.cc +++ /dev/null @@ -1,138 +0,0 @@ -#include "selfdrive/ui/qt/widgets/offroad_alerts.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include "common/util.h" -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -AbstractAlert::AbstractAlert(bool hasRebootBtn, QWidget *parent) : QFrame(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setMargin(50); - main_layout->setSpacing(30); - - QWidget *widget = new QWidget; - scrollable_layout = new QVBoxLayout(widget); - widget->setStyleSheet("background-color: transparent;"); - main_layout->addWidget(new ScrollView(widget)); - - // bottom footer, dismiss + reboot buttons - QHBoxLayout *footer_layout = new QHBoxLayout(); - main_layout->addLayout(footer_layout); - - QPushButton *dismiss_btn = new QPushButton(tr("Close")); - dismiss_btn->setFixedSize(400, 125); - footer_layout->addWidget(dismiss_btn, 0, Qt::AlignBottom | Qt::AlignLeft); - QObject::connect(dismiss_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss); - - action_btn = new QPushButton(); - action_btn->setVisible(false); - action_btn->setFixedHeight(125); - footer_layout->addWidget(action_btn, 0, Qt::AlignBottom | Qt::AlignRight); - QObject::connect(action_btn, &QPushButton::clicked, [=]() { - if (!alerts["Offroad_ExcessiveActuation"]->text().isEmpty()) { - params.remove("Offroad_ExcessiveActuation"); - } else { - params.putBool("SnoozeUpdate", true); - } - }); - QObject::connect(action_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss); - action_btn->setStyleSheet("color: white; background-color: #4F4F4F; padding-left: 60px; padding-right: 60px;"); - - if (hasRebootBtn) { - QPushButton *rebootBtn = new QPushButton(tr("Reboot and Update")); - rebootBtn->setFixedSize(600, 125); - footer_layout->addWidget(rebootBtn, 0, Qt::AlignBottom | Qt::AlignRight); - QObject::connect(rebootBtn, &QPushButton::clicked, [=]() { Hardware::reboot(); }); - } - - setStyleSheet(R"( - * { - font-size: 48px; - color: white; - } - QFrame { - border-radius: 30px; - background-color: #393939; - } - QPushButton { - color: black; - font-weight: 500; - border-radius: 30px; - background-color: white; - } - )"); -} - -int OffroadAlert::refresh() { - // build widgets for each offroad alert on first refresh - if (alerts.empty()) { - QString json = util::read_file("../selfdrived/alerts_offroad.json").c_str(); - QJsonObject obj = QJsonDocument::fromJson(json.toUtf8()).object(); - - // descending sort labels by severity - std::vector> sorted; - for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) { - sorted.push_back({it.key().toStdString(), it.value()["severity"].toInt()}); - } - std::sort(sorted.begin(), sorted.end(), [=](auto &l, auto &r) { return l.second > r.second; }); - - for (auto &[key, severity] : sorted) { - QLabel *l = new QLabel(this); - alerts[key] = l; - l->setMargin(60); - l->setWordWrap(true); - l->setStyleSheet(QString("background-color: %1").arg(severity ? "#E22C2C" : "#292929")); - scrollable_layout->addWidget(l); - } - scrollable_layout->addStretch(1); - } - - int alertCount = 0; - for (const auto &[key, label] : alerts) { - QString text; - std::string bytes = params.get(key); - if (bytes.size()) { - auto doc_par = QJsonDocument::fromJson(bytes.c_str()); - text = tr(doc_par["text"].toString().toUtf8().data()); - auto extra = doc_par["extra"].toString(); - if (!extra.isEmpty()) { - text = text.arg(extra); - } - } - label->setText(text); - label->setVisible(!text.isEmpty()); - alertCount += !text.isEmpty(); - } - - action_btn->setVisible(!alerts["Offroad_ExcessiveActuation"]->text().isEmpty() || !alerts["Offroad_ConnectivityNeeded"]->text().isEmpty()); - if (!alerts["Offroad_ExcessiveActuation"]->text().isEmpty()) { - action_btn->setText(tr("Acknowledge Excessive Actuation")); - } else { - action_btn->setText(tr("Snooze Update")); - } - - return alertCount; -} - -UpdateAlert::UpdateAlert(QWidget *parent) : AbstractAlert(true, parent) { - releaseNotes = new QLabel(this); - releaseNotes->setWordWrap(true); - releaseNotes->setAlignment(Qt::AlignTop); - scrollable_layout->addWidget(releaseNotes); -} - -bool UpdateAlert::refresh() { - bool updateAvailable = params.getBool("UpdateAvailable"); - if (updateAvailable) { - releaseNotes->setText(params.get("UpdaterNewReleaseNotes").c_str()); - } - return updateAvailable; -} diff --git a/selfdrive/ui/qt/widgets/offroad_alerts.h b/selfdrive/ui/qt/widgets/offroad_alerts.h deleted file mode 100644 index 2dcf4f9d8c..0000000000 --- a/selfdrive/ui/qt/widgets/offroad_alerts.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include - -#include "common/params.h" - -class AbstractAlert : public QFrame { - Q_OBJECT - -protected: - AbstractAlert(bool hasRebootBtn, QWidget *parent = nullptr); - - QPushButton *action_btn; - QVBoxLayout *scrollable_layout; - Params params; - std::map alerts; - -signals: - void dismiss(); -}; - -class UpdateAlert : public AbstractAlert { - Q_OBJECT - -public: - UpdateAlert(QWidget *parent = 0); - bool refresh(); - -private: - QLabel *releaseNotes = nullptr; -}; - -class OffroadAlert : public AbstractAlert { - Q_OBJECT - -public: - explicit OffroadAlert(QWidget *parent = 0) : AbstractAlert(false, parent) {} - int refresh(); -}; diff --git a/selfdrive/ui/qt/widgets/prime.cc b/selfdrive/ui/qt/widgets/prime.cc deleted file mode 100644 index ee820c46a3..0000000000 --- a/selfdrive/ui/qt/widgets/prime.cc +++ /dev/null @@ -1,265 +0,0 @@ -#include "selfdrive/ui/qt/widgets/prime.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "selfdrive/ui/qt/request_repeater.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/widgets/wifi.h" - -using qrcodegen::QrCode; - -PairingQRWidget::PairingQRWidget(QWidget* parent) : QWidget(parent) { - timer = new QTimer(this); - connect(timer, &QTimer::timeout, this, &PairingQRWidget::refresh); -} - -void PairingQRWidget::showEvent(QShowEvent *event) { - refresh(); - timer->start(5 * 60 * 1000); - device()->setOffroadBrightness(100); -} - -void PairingQRWidget::hideEvent(QHideEvent *event) { - timer->stop(); - device()->setOffroadBrightness(BACKLIGHT_OFFROAD); -} - -void PairingQRWidget::refresh() { - QString pairToken = CommaApi::create_jwt({{"pair", true}}); - QString qrString = "https://connect.comma.ai/?pair=" + pairToken; - this->updateQrCode(qrString); - update(); -} - -void PairingQRWidget::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 PairingQRWidget::paintEvent(QPaintEvent *e) { - QPainter p(this); - p.fillRect(rect(), Qt::white); - - QSize s = (size() - img.size()) / 2; - p.drawPixmap(s.width(), s.height(), img); -} - - -PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) { - QHBoxLayout *hlayout = new QHBoxLayout(this); - hlayout->setContentsMargins(0, 0, 0, 0); - hlayout->setSpacing(0); - - setStyleSheet("PairingPopup { background-color: #E0E0E0; }"); - - // text - QVBoxLayout *vlayout = new QVBoxLayout(); - vlayout->setContentsMargins(85, 70, 50, 70); - vlayout->setSpacing(50); - hlayout->addLayout(vlayout, 1); - { - QPushButton *close = new QPushButton(QIcon(":/icons/close.svg"), "", this); - close->setIconSize(QSize(80, 80)); - close->setStyleSheet("border: none;"); - vlayout->addWidget(close, 0, Qt::AlignLeft); - QObject::connect(close, &QPushButton::clicked, this, &QDialog::reject); - - vlayout->addSpacing(30); - - QLabel *title = new QLabel(tr("Pair your device to your comma account"), this); - title->setStyleSheet("font-size: 75px; color: black;"); - title->setWordWrap(true); - vlayout->addWidget(title); - - QLabel *instructions = new QLabel(QString(R"( -
    -
  1. %1
  2. -
  3. %2
  4. -
  5. %3
  6. -
- )").arg(tr("Go to https://connect.comma.ai on your phone")) - .arg(tr("Click \"add new device\" and scan the QR code on the right")) - .arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this); - - instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;"); - instructions->setWordWrap(true); - vlayout->addWidget(instructions); - - vlayout->addStretch(); - } - - // QR code - PairingQRWidget *qr = new PairingQRWidget(this); - hlayout->addWidget(qr, 1); -} - -int PairingPopup::exec() { - if (!util::system_time_valid()) { - ConfirmationDialog::alert(tr("Please connect to Wi-Fi to complete initial pairing"), parentWidget()); - return QDialog::Rejected; - } - return DialogBase::exec(); -} - - -PrimeUserWidget::PrimeUserWidget(QWidget *parent) : QFrame(parent) { - setObjectName("primeWidget"); - QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setContentsMargins(56, 40, 56, 40); - mainLayout->setSpacing(20); - - QLabel *subscribed = new QLabel(tr("✓ SUBSCRIBED")); - subscribed->setStyleSheet("font-size: 41px; font-weight: bold; color: #86FF4E;"); - mainLayout->addWidget(subscribed); - - QLabel *commaPrime = new QLabel(tr("comma prime")); - commaPrime->setStyleSheet("font-size: 75px; font-weight: bold;"); - mainLayout->addWidget(commaPrime); -} - - -PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QFrame(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(80, 90, 80, 60); - main_layout->setSpacing(0); - - QLabel *upgrade = new QLabel(tr("Upgrade Now")); - upgrade->setStyleSheet("font-size: 75px; font-weight: bold;"); - main_layout->addWidget(upgrade, 0, Qt::AlignTop); - main_layout->addSpacing(50); - - QLabel *description = new QLabel(tr("Become a comma prime member at connect.comma.ai")); - description->setStyleSheet("font-size: 56px; font-weight: light; color: white;"); - description->setWordWrap(true); - main_layout->addWidget(description, 0, Qt::AlignTop); - - main_layout->addStretch(); - - QLabel *features = new QLabel(tr("PRIME FEATURES:")); - features->setStyleSheet("font-size: 41px; font-weight: bold; color: #E5E5E5;"); - main_layout->addWidget(features, 0, Qt::AlignBottom); - main_layout->addSpacing(30); - - QVector bullets = {tr("Remote access"), tr("24/7 LTE connectivity"), tr("1 year of drive storage"), tr("Remote snapshots")}; - for (auto &b : bullets) { - const QString check = " "; - QLabel *l = new QLabel(check + b); - l->setAlignment(Qt::AlignLeft); - l->setStyleSheet("font-size: 50px; margin-bottom: 15px;"); - main_layout->addWidget(l, 0, Qt::AlignBottom); - } - - setStyleSheet(R"( - PrimeAdWidget { - border-radius: 10px; - background-color: #333333; - } - )"); -} - - -SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) { - mainLayout = new QStackedWidget; - - // Unpaired, registration prompt layout - - QFrame* finishRegistration = new QFrame; - finishRegistration->setObjectName("primeWidget"); - QVBoxLayout* finishRegistrationLayout = new QVBoxLayout(finishRegistration); - finishRegistrationLayout->setSpacing(38); - finishRegistrationLayout->setContentsMargins(64, 48, 64, 48); - - QLabel* registrationTitle = new QLabel(tr("Finish Setup")); - registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold;"); - finishRegistrationLayout->addWidget(registrationTitle); - - QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.")); - registrationDescription->setWordWrap(true); - registrationDescription->setStyleSheet("font-size: 50px; font-weight: light;"); - finishRegistrationLayout->addWidget(registrationDescription); - - finishRegistrationLayout->addStretch(); - - QPushButton* pair = new QPushButton(tr("Pair device")); - pair->setStyleSheet(R"( - QPushButton { - font-size: 55px; - font-weight: 500; - border-radius: 10px; - background-color: #465BEA; - padding: 64px; - } - QPushButton:pressed { - background-color: #3049F4; - } - )"); - finishRegistrationLayout->addWidget(pair); - - popup = new PairingPopup(this); - QObject::connect(pair, &QPushButton::clicked, popup, &PairingPopup::exec); - - mainLayout->addWidget(finishRegistration); - - // build stacked layout - QVBoxLayout *outer_layout = new QVBoxLayout(this); - outer_layout->setContentsMargins(0, 0, 0, 0); - outer_layout->addWidget(mainLayout); - - QWidget *content = new QWidget; - QVBoxLayout *content_layout = new QVBoxLayout(content); - content_layout->setContentsMargins(0, 0, 0, 0); - content_layout->setSpacing(30); - - WiFiPromptWidget *wifi_prompt = new WiFiPromptWidget; - QObject::connect(wifi_prompt, &WiFiPromptWidget::openSettings, this, &SetupWidget::openSettings); - content_layout->addWidget(wifi_prompt); - content_layout->addStretch(); - - mainLayout->addWidget(content); - - mainLayout->setCurrentIndex(1); - - setStyleSheet(R"( - #primeWidget { - border-radius: 10px; - background-color: #333333; - } - )"); - - // Retain size while hidden - QSizePolicy sp_retain = sizePolicy(); - sp_retain.setRetainSizeWhenHidden(true); - setSizePolicy(sp_retain); - - QObject::connect(uiState()->prime_state, &PrimeState::changed, [this](PrimeState::Type type) { - if (type == PrimeState::PRIME_TYPE_UNPAIRED) { - mainLayout->setCurrentIndex(0); // Display "Pair your device" widget - } else { - popup->reject(); - mainLayout->setCurrentIndex(1); // Display Wi-Fi prompt widget - } - }); -} diff --git a/selfdrive/ui/qt/widgets/prime.h b/selfdrive/ui/qt/widgets/prime.h deleted file mode 100644 index 266a90a92c..0000000000 --- a/selfdrive/ui/qt/widgets/prime.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "selfdrive/ui/qt/widgets/input.h" - -// pairing QR code -class PairingQRWidget : public QWidget { - Q_OBJECT - -public: - explicit PairingQRWidget(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; - -private slots: - void refresh(); -}; - - -// pairing popup widget -class PairingPopup : public DialogBase { - Q_OBJECT - -public: - explicit PairingPopup(QWidget* parent); - int exec() override; -}; - - -// widget for paired users with prime -class PrimeUserWidget : public QFrame { - Q_OBJECT - -public: - explicit PrimeUserWidget(QWidget* parent = 0); -}; - - -// widget for paired users without prime -class PrimeAdWidget : public QFrame { - Q_OBJECT -public: - explicit PrimeAdWidget(QWidget* parent = 0); -}; - - -// container widget -class SetupWidget : public QFrame { - Q_OBJECT - -public: - explicit SetupWidget(QWidget* parent = 0); - -signals: - void openSettings(int index = 0, const QString ¶m = ""); - -private: - PairingPopup *popup; - QStackedWidget *mainLayout; -}; diff --git a/selfdrive/ui/qt/widgets/scrollview.cc b/selfdrive/ui/qt/widgets/scrollview.cc deleted file mode 100644 index 978bf83a63..0000000000 --- a/selfdrive/ui/qt/widgets/scrollview.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include "selfdrive/ui/qt/widgets/scrollview.h" - -#include -#include - -// TODO: disable horizontal scrolling and resize - -ScrollView::ScrollView(QWidget *w, QWidget *parent) : QScrollArea(parent) { - setWidget(w); - setWidgetResizable(true); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setStyleSheet("background-color: transparent; border:none"); - - QString style = R"( - QScrollBar:vertical { - border: none; - background: transparent; - width: 10px; - margin: 0; - } - QScrollBar::handle:vertical { - min-height: 0px; - border-radius: 5px; - background-color: white; - } - QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - height: 0px; - } - QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: none; - } - )"; - verticalScrollBar()->setStyleSheet(style); - horizontalScrollBar()->setStyleSheet(style); - - QScroller *scroller = QScroller::scroller(this->viewport()); - QScrollerProperties sp = scroller->scrollerProperties(); - - sp.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff)); - sp.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff)); - sp.setScrollMetric(QScrollerProperties::MousePressEventDelay, 0.01); - scroller->grabGesture(this->viewport(), QScroller::LeftMouseButtonGesture); - scroller->setScrollerProperties(sp); -} - -void ScrollView::hideEvent(QHideEvent *e) { - verticalScrollBar()->setValue(0); -} diff --git a/selfdrive/ui/qt/widgets/scrollview.h b/selfdrive/ui/qt/widgets/scrollview.h deleted file mode 100644 index 024331aa39..0000000000 --- a/selfdrive/ui/qt/widgets/scrollview.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -class ScrollView : public QScrollArea { - Q_OBJECT - -public: - explicit ScrollView(QWidget *w = nullptr, QWidget *parent = nullptr); -protected: - void hideEvent(QHideEvent *e) override; -}; diff --git a/selfdrive/ui/qt/widgets/ssh_keys.cc b/selfdrive/ui/qt/widgets/ssh_keys.cc deleted file mode 100644 index 26743952de..0000000000 --- a/selfdrive/ui/qt/widgets/ssh_keys.cc +++ /dev/null @@ -1,64 +0,0 @@ -#include "selfdrive/ui/qt/widgets/ssh_keys.h" - -#include "common/params.h" -#include "selfdrive/ui/qt/api.h" -#include "selfdrive/ui/qt/widgets/input.h" - -SshControl::SshControl() : - ButtonControl(tr("SSH Keys"), "", tr("Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " - "other than your own. A comma employee will NEVER ask you to add their GitHub username.")) { - - QObject::connect(this, &ButtonControl::clicked, [=]() { - if (text() == tr("ADD")) { - QString username = InputDialog::getText(tr("Enter your GitHub username"), this); - if (username.length() > 0) { - setText(tr("LOADING")); - setEnabled(false); - getUserKeys(username); - } - } else { - params.remove("GithubUsername"); - params.remove("GithubSshKeys"); - refresh(); - } - }); - - refresh(); -} - -void SshControl::refresh() { - QString param = QString::fromStdString(params.get("GithubSshKeys")); - if (param.length()) { - setValue(QString::fromStdString(params.get("GithubUsername"))); - setText(tr("REMOVE")); - } else { - setValue(""); - setText(tr("ADD")); - } - setEnabled(true); -} - -void SshControl::getUserKeys(const QString &username) { - HttpRequest *request = new HttpRequest(this, false); - QObject::connect(request, &HttpRequest::requestDone, [=](const QString &resp, bool success) { - if (success) { - if (!resp.isEmpty()) { - params.put("GithubUsername", username.toStdString()); - params.put("GithubSshKeys", resp.toStdString()); - } else { - ConfirmationDialog::alert(tr("Username '%1' has no keys on GitHub").arg(username), this); - } - } else { - if (request->timeout()) { - ConfirmationDialog::alert(tr("Request timed out"), this); - } else { - ConfirmationDialog::alert(tr("Username '%1' doesn't exist on GitHub").arg(username), this); - } - } - - refresh(); - request->deleteLater(); - }); - - request->sendRequest("https://github.com/" + username + ".keys"); -} diff --git a/selfdrive/ui/qt/widgets/ssh_keys.h b/selfdrive/ui/qt/widgets/ssh_keys.h deleted file mode 100644 index 920bd651e2..0000000000 --- a/selfdrive/ui/qt/widgets/ssh_keys.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/widgets/controls.h" - -// SSH enable toggle -class SshToggle : public ToggleControl { - Q_OBJECT - -public: - SshToggle() : ToggleControl(tr("Enable SSH"), "", "", Hardware::get_ssh_enabled()) { - QObject::connect(this, &SshToggle::toggleFlipped, [=](bool state) { - Hardware::set_ssh_enabled(state); - }); - } -}; - -// SSH key management widget -class SshControl : public ButtonControl { - Q_OBJECT - -public: - SshControl(); - -private: - Params params; - - void refresh(); - void getUserKeys(const QString &username); -}; diff --git a/selfdrive/ui/qt/widgets/toggle.cc b/selfdrive/ui/qt/widgets/toggle.cc deleted file mode 100644 index 82302ad5bc..0000000000 --- a/selfdrive/ui/qt/widgets/toggle.cc +++ /dev/null @@ -1,83 +0,0 @@ -#include "selfdrive/ui/qt/widgets/toggle.h" - -#include - -Toggle::Toggle(QWidget *parent) : QAbstractButton(parent), -_height(80), -_height_rect(60), -on(false), -_anim(new QPropertyAnimation(this, "offset_circle", this)) -{ - _radius = _height / 2; - _x_circle = _radius; - _y_circle = _radius; - _y_rect = (_height - _height_rect)/2; - circleColor = QColor(0xffffff); // placeholder - green = QColor(0xffffff); // placeholder - setEnabled(true); -} - -void Toggle::paintEvent(QPaintEvent *e) { - this->setFixedHeight(_height); - QPainter p(this); - p.setPen(Qt::NoPen); - p.setRenderHint(QPainter::Antialiasing, true); - - // Draw toggle background left - p.setBrush(green); - p.drawRoundedRect(QRect(0, _y_rect, _x_circle + _radius, _height_rect), _height_rect/2, _height_rect/2); - - // Draw toggle background right - p.setBrush(QColor(0x393939)); - p.drawRoundedRect(QRect(_x_circle - _radius, _y_rect, width() - (_x_circle - _radius), _height_rect), _height_rect/2, _height_rect/2); - - // Draw toggle circle - p.setBrush(circleColor); - p.drawEllipse(QRectF(_x_circle - _radius, _y_circle - _radius, 2 * _radius, 2 * _radius)); -} - -void Toggle::mouseReleaseEvent(QMouseEvent *e) { - if (!enabled) { - return; - } - const int left = _radius; - const int right = width() - _radius; - if ((_x_circle != left && _x_circle != right) || !this->rect().contains(e->localPos().toPoint())) { - // If mouse release isn't in rect or animation is running, don't parse touch events - return; - } - if (e->button() & Qt::LeftButton) { - togglePosition(); - emit stateChanged(on); - } -} - -void Toggle::togglePosition() { - on = !on; - const int left = _radius; - const int right = width() - _radius; - _anim->setStartValue(on ? left + immediateOffset : right - immediateOffset); - _anim->setEndValue(on ? right : left); - _anim->setDuration(animation_duration); - _anim->start(); - repaint(); -} - -void Toggle::enterEvent(QEvent *e) { - QAbstractButton::enterEvent(e); -} - -bool Toggle::getEnabled() { - return enabled; -} - -void Toggle::setEnabled(bool value) { - enabled = value; - if (value) { - circleColor.setRgb(0xfafafa); - green.setRgb(0x33ab4c); - } else { - circleColor.setRgb(0x888888); - green.setRgb(0x227722); - } -} diff --git a/selfdrive/ui/qt/widgets/toggle.h b/selfdrive/ui/qt/widgets/toggle.h deleted file mode 100644 index e7263a008f..0000000000 --- a/selfdrive/ui/qt/widgets/toggle.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include - -class Toggle : public QAbstractButton { - Q_OBJECT - Q_PROPERTY(int offset_circle READ offset_circle WRITE set_offset_circle CONSTANT) - -public: - Toggle(QWidget* parent = nullptr); - void togglePosition(); - bool on; - int animation_duration = 150; - int immediateOffset = 0; - int offset_circle() const { - return _x_circle; - } - - void set_offset_circle(int o) { - _x_circle = o; - update(); - } - bool getEnabled(); - void setEnabled(bool value); - -protected: - void paintEvent(QPaintEvent*) override; - void mouseReleaseEvent(QMouseEvent*) override; - void enterEvent(QEvent*) override; - -private: - QColor circleColor; - QColor green; - bool enabled = true; - int _x_circle, _y_circle; - int _height, _radius; - int _height_rect, _y_rect; - QPropertyAnimation *_anim = nullptr; - -signals: - void stateChanged(bool new_state); -}; diff --git a/selfdrive/ui/qt/widgets/wifi.cc b/selfdrive/ui/qt/widgets/wifi.cc deleted file mode 100644 index d7eb8beeb3..0000000000 --- a/selfdrive/ui/qt/widgets/wifi.cc +++ /dev/null @@ -1,45 +0,0 @@ -#include "selfdrive/ui/qt/widgets/wifi.h" - -#include -#include -#include -#include - -WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) { - // Setup Firehose Mode - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(56, 40, 56, 40); - main_layout->setSpacing(42); - - QLabel *title = new QLabel(tr("🔥 Firehose Mode 🔥")); - title->setStyleSheet("font-size: 64px; font-weight: 500;"); - main_layout->addWidget(title); - - QLabel *desc = new QLabel(tr("Maximize your training data uploads to improve openpilot's driving models.")); - desc->setStyleSheet("font-size: 40px; font-weight: 400;"); - desc->setWordWrap(true); - main_layout->addWidget(desc); - - QPushButton *settings_btn = new QPushButton(tr("Open")); - connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(1, "FirehosePanel"); }); - settings_btn->setStyleSheet(R"( - QPushButton { - font-size: 48px; - font-weight: 500; - border-radius: 10px; - background-color: #465BEA; - padding: 32px; - } - QPushButton:pressed { - background-color: #3049F4; - } - )"); - main_layout->addWidget(settings_btn); - - setStyleSheet(R"( - WiFiPromptWidget { - background-color: #333333; - border-radius: 10px; - } - )"); -} \ No newline at end of file diff --git a/selfdrive/ui/qt/widgets/wifi.h b/selfdrive/ui/qt/widgets/wifi.h deleted file mode 100644 index 3e68a15b7b..0000000000 --- a/selfdrive/ui/qt/widgets/wifi.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -class WiFiPromptWidget : public QFrame { - Q_OBJECT - -public: - explicit WiFiPromptWidget(QWidget* parent = 0); - -signals: - void openSettings(int index = 0, const QString ¶m = ""); -}; diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc deleted file mode 100644 index 6b579fcc5d..0000000000 --- a/selfdrive/ui/qt/window.cc +++ /dev/null @@ -1,98 +0,0 @@ -#include "selfdrive/ui/qt/window.h" - -#include - -#include "system/hardware/hw.h" - -MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { - main_layout = new QStackedLayout(this); - main_layout->setMargin(0); - - homeWindow = new HomeWindow(this); - main_layout->addWidget(homeWindow); - QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings); - QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings); - - settingsWindow = new SettingsWindow(this); - main_layout->addWidget(settingsWindow); - QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings); - QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() { - onboardingWindow->showTrainingGuide(); - main_layout->setCurrentWidget(onboardingWindow); - }); - QObject::connect(settingsWindow, &SettingsWindow::showDriverView, [=] { - homeWindow->showDriverView(true); - }); - - onboardingWindow = new OnboardingWindow(this); - main_layout->addWidget(onboardingWindow); - QObject::connect(onboardingWindow, &OnboardingWindow::onboardingDone, [=]() { - main_layout->setCurrentWidget(homeWindow); - }); - if (!onboardingWindow->completed()) { - main_layout->setCurrentWidget(onboardingWindow); - } - - QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { - if (!offroad) { - closeSettings(); - } - }); - QObject::connect(device(), &Device::interactiveTimeout, [=]() { - if (main_layout->currentWidget() == settingsWindow) { - closeSettings(); - } - }); - - // load fonts - QFontDatabase::addApplicationFont("../assets/fonts/Inter-Black.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/Inter-Bold.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/Inter-ExtraBold.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/Inter-ExtraLight.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/Inter-Medium.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/Inter-Regular.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/Inter-SemiBold.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/Inter-Thin.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/JetBrainsMono-Medium.ttf"); - - // no outline to prevent the focus rectangle - setStyleSheet(R"( - * { - font-family: Inter; - outline: none; - } - )"); - setAttribute(Qt::WA_NoSystemBackground); -} - -void MainWindow::openSettings(int index, const QString ¶m) { - main_layout->setCurrentWidget(settingsWindow); - settingsWindow->setCurrentPanel(index, param); -} - -void MainWindow::closeSettings() { - main_layout->setCurrentWidget(homeWindow); - - if (uiState()->scene.started) { - homeWindow->showSidebar(false); - } -} - -bool MainWindow::eventFilter(QObject *obj, QEvent *event) { - bool ignore = false; - switch (event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - case QEvent::MouseButtonPress: - case QEvent::MouseMove: { - // ignore events when device is awakened by resetInteractiveTimeout - ignore = !device()->isAwake(); - device()->resetInteractiveTimeout(); - break; - } - default: - break; - } - return ignore; -} diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h deleted file mode 100644 index 05b61e1f76..0000000000 --- a/selfdrive/ui/qt/window.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -#include "selfdrive/ui/qt/home.h" -#include "selfdrive/ui/qt/offroad/onboarding.h" -#include "selfdrive/ui/qt/offroad/settings.h" - -class MainWindow : public QWidget { - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = 0); - -private: - bool eventFilter(QObject *obj, QEvent *event) override; - void openSettings(int index = 0, const QString ¶m = ""); - void closeSettings(); - - QStackedLayout *main_layout; - HomeWindow *homeWindow; - SettingsWindow *settingsWindow; - OnboardingWindow *onboardingWindow; -}; diff --git a/selfdrive/ui/tests/create_test_translations.sh b/selfdrive/ui/tests/create_test_translations.sh deleted file mode 100755 index 1587a88205..0000000000 --- a/selfdrive/ui/tests/create_test_translations.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -set -e - -UI_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"/.. -TEST_TEXT="(WRAPPED_SOURCE_TEXT)" -TEST_TS_FILE=$UI_DIR/translations/test_en.ts -TEST_QM_FILE=$UI_DIR/translations/test_en.qm - -# translation strings -UNFINISHED="<\/translation>" -TRANSLATED="$TEST_TEXT<\/translation>" - -mkdir -p $UI_DIR/translations -rm -f $TEST_TS_FILE $TEST_QM_FILE -lupdate -recursive "$UI_DIR" -ts $TEST_TS_FILE -sed -i "s/$UNFINISHED/$TRANSLATED/" $TEST_TS_FILE -lrelease $TEST_TS_FILE diff --git a/selfdrive/ui/tests/test_runner.cc b/selfdrive/ui/tests/test_runner.cc deleted file mode 100644 index 4bde921696..0000000000 --- a/selfdrive/ui/tests/test_runner.cc +++ /dev/null @@ -1,26 +0,0 @@ -#define CATCH_CONFIG_RUNNER -#include "catch2/catch.hpp" - -#include -#include -#include -#include - -int main(int argc, char **argv) { - // unit tests for Qt - QApplication app(argc, argv); - - QString language_file = "test_en"; - // FIXME: pytest-cpp considers this print as a test case - qDebug() << "Loading language:" << language_file; - - QTranslator translator; - QString translationsPath = QDir::cleanPath(qApp->applicationDirPath() + "/../translations"); - if (!translator.load(language_file, translationsPath)) { - qDebug() << "Failed to load translation file!"; - } - app.installTranslator(&translator); - - const int res = Catch::Session().run(argc, argv); - return (res < 0xff ? res : 0xff); -} diff --git a/selfdrive/ui/tests/test_translations.cc b/selfdrive/ui/tests/test_translations.cc deleted file mode 100644 index fcefc5784f..0000000000 --- a/selfdrive/ui/tests/test_translations.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include "catch2/catch.hpp" - -#include "common/params.h" -#include "selfdrive/ui/qt/window.h" - -const QString TEST_TEXT = "(WRAPPED_SOURCE_TEXT)"; // what each string should be translated to -QRegExp RE_NUM("\\d*"); - -QStringList getParentWidgets(QWidget* widget){ - QStringList parentWidgets; - while (widget->parentWidget() != Q_NULLPTR) { - widget = widget->parentWidget(); - parentWidgets.append(widget->metaObject()->className()); - } - return parentWidgets; -} - -template -void checkWidgetTrWrap(MainWindow &w) { - for (auto widget : w.findChildren()) { - const QString text = widget->text(); - bool isNumber = RE_NUM.exactMatch(text); - bool wrapped = text.contains(TEST_TEXT); - QString parentWidgets = getParentWidgets(widget).join("->"); - - if (!text.isEmpty() && !isNumber && !wrapped) { - FAIL(("\"" + text + "\" must be wrapped. Parent widgets: " + parentWidgets).toStdString()); - } - - // warn if source string wrapped, but UI adds text - // TODO: add way to ignore this - if (wrapped && text != TEST_TEXT) { - WARN(("\"" + text + "\" is dynamic and needs a custom retranslate function. Parent widgets: " + parentWidgets).toStdString()); - } - } -} - -// Tests all strings in the UI are wrapped with tr() -TEST_CASE("UI: test all strings wrapped") { - Params().remove("LanguageSetting"); - Params().remove("HardwareSerial"); - Params().remove("DongleId"); - qputenv("TICI", "1"); - - MainWindow w; - checkWidgetTrWrap(w); - checkWidgetTrWrap(w); -} diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py index edd9a30412..5308a44ea5 100644 --- a/selfdrive/ui/tests/test_translations.py +++ b/selfdrive/ui/tests/test_translations.py @@ -6,8 +6,7 @@ import xml.etree.ElementTree as ET import string import requests from parameterized import parameterized_class - -from openpilot.selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE +from openpilot.system.ui.lib.multilang import TRANSLATIONS_DIR, LANGUAGES_FILE with open(LANGUAGES_FILE) as f: translation_files = json.load(f) @@ -17,6 +16,7 @@ LOCATION_TAG = " - - - - AbstractAlert - - Close - إغلاق - - - Reboot and Update - إعادة التشغيل والتحديث - - - - AdvancedNetworking - - Back - السابق - - - Enable Tethering - تمكين الربط - - - Tethering Password - كلمة مرور الربط - - - EDIT - تعديل - - - Enter new tethering password - أدخل كلمة مرور الربط الجديدة - - - IP Address - عنوان IP - - - Enable Roaming - تمكين التجوال - - - APN Setting - إعدادات APN - - - Enter APN - إدخال APN - - - leave blank for automatic configuration - اتركه فارغاً من أجل التكوين التلقائي - - - Cellular Metered - محدود بالاتصال الخلوي - - - Hidden Network - شبكة مخفية - - - CONNECT - الاتصال - - - Enter SSID - أدخل SSID - - - Enter password - أدخل كلمة المرور - - - for "%1" - من أجل "%1" - - - Prevent large data uploads when on a metered cellular connection - - - - default - - - - metered - - - - unmetered - - - - Wi-Fi Network Metered - - - - Prevent large data uploads when on a metered Wi-Fi connection - - - - - ConfirmationDialog - - Ok - موافق - - - Cancel - إلغاء - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - يجب عليك قبول الشروط والأحكام من أجل استخدام openpilot. - - - Back - السابق - - - Decline, uninstall %1 - رفض، إلغاء التثبيت %1 - - - - DeveloperPanel - - Joystick Debug Mode - وضع تصحيح أخطاء عصا التحكم - - - Longitudinal Maneuver Mode - وضع المناورة الطولية - - - openpilot Longitudinal Control (Alpha) - التحكم الطولي openpilot (ألفا) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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 افتراضياً بالشكل المدمج في التحكم التكيفي في السرعة بدلاً من التحكم الطولي. قم بتمكين هذا الخيار من أجل الانتقال إلى التحكم الطولي. يوصى بتمكين الوضع التجريبي عند استخدام وضع التحكم الطولي ألفا من openpilot. - - - Enable ADB - تمكين ADB - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - أداة ADB (Android Debug Bridge) تسمح بالاتصال بجهازك عبر USB أو عبر الشبكة. راجع هذا الرابط: https://docs.comma.ai/how-to/connect-to-comma لمزيد من المعلومات. - - - - DevicePanel - - Dongle ID - معرف دونجل - - - N/A - غير متاح - - - Serial - الرقم التسلسلي - - - Driver Camera - كاميرة السائق - - - PREVIEW - معاينة - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - قم بمعاينة الكاميرا المواجهة للسائق للتأكد من أن نظام مراقبة السائق يتمتع برؤية جيدة. (يجب أن تكون السيارة متوقفة) - - - Reset Calibration - إعادة ضبط المعايرة - - - RESET - إعادة الضبط - - - Are you sure you want to reset calibration? - هل أنت متأكد أنك تريد إعادة ضبط المعايرة؟ - - - Review Training Guide - مراجعة دليل التدريب - - - REVIEW - مراجعة - - - Review the rules, features, and limitations of openpilot - مراجعة الأدوار والميزات والقيود في openpilot - - - Are you sure you want to review the training guide? - هل أنت متأكد أنك تريد مراجعة دليل التدريب؟ - - - Regulatory - التنظيمية - - - VIEW - عرض - - - Change Language - تغيير اللغة - - - CHANGE - تغيير - - - Select a language - اختر لغة - - - Reboot - إعادة التشغيل - - - Power Off - إيقاف التشغيل - - - Your device is pointed %1° %2 and %3° %4. - يشير جهازك إلى %1 درجة %2، و%3 درجة %4. - - - down - نحو الأسفل - - - up - نحو الأعلى - - - left - نحو اليسار - - - right - نحو اليمين - - - Are you sure you want to reboot? - هل أنت متأكد أنك تريد إعادة التشغيل؟ - - - Disengage to Reboot - فك الارتباط من أجل إعادة التشغيل - - - Are you sure you want to power off? - هل أنت متأكد أنك تريد إيقاف التشغيل؟ - - - Disengage to Power Off - فك الارتباط من أجل إيقاف التشغيل - - - Reset - إعادة الضبط - - - Review - مراجعة - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - اقرن جهازك بجهاز (connect.comma.ai) واحصل على عرضك من comma prime. - - - Pair Device - إقران الجهاز - - - PAIR - إقران - - - Disengage to Reset Calibration - - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - - - - - -Steering lag calibration is %1% complete. - - - - - -Steering lag calibration is complete. - - - - Steering torque response calibration is %1% complete. - - - - Steering torque response calibration is complete. - - - - - DriverViewWindow - - camera starting - بدء تشغيل الكاميرا - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - تشغيل الوضع التجريبي - - - CHILL MODE ON - تشغيل وضع الراحة - - - - FirehosePanel - - 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 - نشط - - - 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 من قيادتك في مجموعة بيانات التدريب. - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - - - - Firehose Mode - - - - - HudRenderer - - km/h - كم/س - - - mph - ميل/س - - - MAX - MAX - - - - InputDialog - - Cancel - إلغاء - - - Need at least %n character(s)! - - تحتاج إلى حرف %n على الأقل! - تحتاج إلى حرف %n على الأقل! - تحتاج إلى حرفين %n على الأقل! - تحتاج إلى %n أحرف على الأقل! - تحتاج إلى %n أحرف على الأقل! - تحتاج إلى %n حرف على الأقل! - - - - - MultiOptionDialog - - Select - اختيار - - - Cancel - إلغاء - - - - Networking - - Advanced - متقدم - - - Enter password - أدخل كلمة المرور - - - for "%1" - من أجل "%1" - - - Wrong password - كلمة مرور خاطئة - - - - OffroadAlert - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - درجة حرارة الجهاز مرتفعة جداً. يقوم النظام بالتبريد قبل البدء. درجة الحرارة الحالية للمكونات الداخلية: %1 - - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - اتصل فوراً بالإنترنت للتحقق من وجود تحديثات. إذا لم تكم متصلاً بالإنترنت فإن openpilot لن يساهم في %1 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - اتصل بالإنترنت للتحقق من وجود تحديثات. لا يعمل openpilot تلقائياً إلا إذا اتصل بالإنترنت من أجل التحقق من التحديثات. - - - Unable to download updates -%1 - غير قادر على تحميل التحديثات -%1 - - - Taking camera snapshots. System won't start until finished. - التقاط لقطات كاميرا. لن يبدأ النظام حتى تنتهي هذه العملية. - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - يتم تنزيل تحديث لنظام تشغيل جهازك في الخلفية. سيطلَب منك التحديث عندما يصبح جاهزاً للتثبيت. - - - 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 قادراً على تحديد سيارتك. إما أن تكون سيارتك غير مدعومة أو أنه لم يتم التعرف على وحدة التحكم الإلكتروني (ECUs) فيها. يرجى تقديم طلب سحب من أجل إضافة نسخ برمجيات ثابتة إلى السيارة المناسبة. هل تحتاج إلى أي مساعدة؟ لا تتردد في التواصل مع doscord.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. - لقد اكتشف openpilot تغييراً في موقع تركيب الجهاز. تأكد من تثبيت الجهاز بشكل كامل في موقعه وتثبيته بإحكام على الزجاج الأمامي. - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - - - - Acknowledge Excessive Actuation - - - - Snooze Update - تأخير التحديث - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - - - - - OffroadHome - - UPDATE - تحديث - - - ALERTS - التنبهات - - - ALERT - تنبيه - - - - OnroadAlerts - - openpilot Unavailable - openpilot غير متوفر - - - TAKE CONTROL IMMEDIATELY - تحكم على الفور - - - Reboot Device - إعادة التشغيل - - - Waiting to start - في انتظار البدء - - - System Unresponsive - النظام لا يستجيب - - - - PairingPopup - - Pair your device to your comma account - اقرن جهازك مع حسابك على comma - - - Go to https://connect.comma.ai on your phone - انتقل إلى https://connect.comma.ai على جوالك - - - Click "add new device" and scan the QR code on the right - انقر "،إضافة جهاز جديد"، وامسح رمز الاستجابة السريعة (QR) على اليمين - - - Bookmark connect.comma.ai to your home screen to use it like an app - اجعل لـconnect.comma.ai إشارة مرجعية على شاشتك الرئيسية من أجل استخدامه مثل أي تطبيق - - - Please connect to Wi-Fi to complete initial pairing - يرجى الاتصال بشبكة الواي فاي لإكمال الاقتران الأولي - - - - ParamControl - - Enable - تمكين - - - Cancel - إلغاء - - - - PrimeAdWidget - - Upgrade Now - الترقية الآن - - - Become a comma prime member at connect.comma.ai - كن عضوًا في comma prime على connect.comma.ai - - - PRIME FEATURES: - الميزات الأساسية: - - - Remote access - التحكم عن بعد - - - 24/7 LTE connectivity - اتصال LTE على مدار الساعة 24/7 - - - 1 year of drive storage - سنة واحدة من تخزين القرص - - - Remote snapshots - لقطات عن بُعد - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ مشترك - - - comma prime - comma prime - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - منذ %n دقيقة - منذ %n دقيقة - منذ دقيقتين %n - منذ %n دقائق - منذ %n دقائق - منذ %n دقيقة - - - - %n hour(s) ago - - منذ %n ساعة - منذ %n ساعة - منذ ساعتين %n - منذ %n ساعات - منذ %n ساعات - منذ %n ساعة - - - - %n day(s) ago - - منذ %n يوم - منذ %n يوم - منذ يومين %n - منذ %n أيام - منذ %n أيام - منذ %n يوم - - - - now - الآن - - - - SettingsWindow - - × - × - - - Device - الجهاز - - - Network - الشبكة - - - Toggles - المثبتتات - - - Software - البرنامج - - - Developer - المطور - - - Firehose - خرطوم الحريق - - - - SetupWidget - - Finish Setup - إنهاء الإعداد - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - اقرن جهازك بجهاز (connect.comma.ai) واحصل على عرضك من comma prime. - - - Pair device - اقتران الجهاز - - - - Sidebar - - CONNECT - الاتصال - - - OFFLINE - غير متصل - - - ONLINE - متصل - - - ERROR - خطأ - - - TEMP - درجة الحرارة - - - HIGH - مرتفع - - - GOOD - جيد - - - OK - موافق - - - VEHICLE - المركبة - - - NO - لا - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - UNINSTALL - إلغاء التثبيت - - - Uninstall %1 - إلغاء التثبيت %1 - - - Are you sure you want to uninstall? - هل أنت متأكد أنك تريد إلغاء التثبيت؟ - - - CHECK - التحقق - - - Updates are only downloaded while the car is off. - يتم تحميل التحديثات فقط عندما تكون السيارة متوقفة. - - - Current Version - النسخة الحالية - - - Download - تنزيل - - - Install Update - تثبيت التحديث - - - INSTALL - تثبيت - - - Target Branch - فرع الهدف - - - SELECT - اختيار - - - Select a branch - اختر فرعاً - - - Uninstall - إلغاء التثبيت - - - failed to check for update - فشل التحقق من التحديث - - - DOWNLOAD - تنزيل - - - update available - يتوفر تحديث - - - never - إطلاقاً - - - up to date, last checked %1 - أحدث نسخة، آخر تحقق %1 - - - - SshControl - - SSH Keys - مفاتيح SSH - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - تنبيه: هذا يمنح SSH إمكانية الوصول إلى جميع المفاتيح العامة في إعدادات GitHub. لا تقم بإدخال اسم مستخدم GitHub بدلاً من اسمك. لن تطلب منك comma employee إطلاقاً أن تضيف اسم مستخدم GitHub الخاص بهم. - - - ADD - إضافة - - - Enter your GitHub username - ادخل اسم المستخدم GitHub الخاص بك - - - LOADING - يتم التحميل - - - REMOVE - إزالة - - - Username '%1' has no keys on GitHub - لا يحتوي اسم المستخدم '%1' أي مفاتيح على GitHub - - - Request timed out - انتهى وقت الطلب - - - Username '%1' doesn't exist on GitHub - اسم المستخدم '%1' غير موجود على GitHub - - - - SshToggle - - Enable SSH - تمكين SSH - - - - TermsPage - - Decline - رفض - - - Agree - أوافق - - - Welcome to openpilot - مرحباً بكم في 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. - يجب عليك قبول الشروط والأحكام لاستخدام openpilot. اقرأ أحدث الشروط على <span style='color: #465BEA;'>https://comma.ai/terms</span> قبل الاستمرار. - - - - TogglesPanel - - Enable openpilot - تمكين openpilot - - - Enable Lane Departure Warnings - قم بتمكين تحذيرات مغادرة المسار - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - تلقي التنبيهات من أجل الالتفاف للعودة إلى المسار عندما تنحرف سيارتك فوق الخط المحدد للمسار دون تشغيل إشارة الانعطاف عند القيادة لمسافة تزيد عن 31 ميل/سا (50 كم/سا). - - - Use Metric System - استخدام النظام المتري - - - Display speed in km/h instead of mph. - عرض السرعة بواحدات كم/سا بدلاً من ميل/سا. - - - Record and Upload Driver Camera - تسجيل وتحميل كاميرا السائق - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - تحميل البيانات من الكاميرا المواجهة للسائق، والمساعدة في تحسين خوارزمية مراقبة السائق. - - - Disengage on Accelerator Pedal - فك الارتباط عن دواسة الوقود - - - When enabled, pressing the accelerator pedal will disengage openpilot. - عند تمكين هذه الميزة، فإن الضغط على دواسة الوقود سيؤدي إلى فك ارتباط openpilot. - - - Experimental Mode - الوضع التجريبي - - - Aggressive - الهجومي - - - Standard - القياسي - - - Relaxed - الراحة - - - Driving Personality - شخصية القيادة - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - يتم وضع openpilot بشكل قياسي في <b>وضعية الراحة</b>. يمكن الوضع التجريبي <b>ميزات المستوى ألفا</b> التي لا تكون جاهزة في وضع الراحة: - - - End-to-End Longitudinal Control - التحكم الطولي من طرف إلى طرف - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - دع نظام القيادة يتحكم بالوقود والمكابح. سيقوم openpilot بالقيادة كما لو أنه كائن بشري، بما في ذلك التوقف عند الإشارة الحمراء، وإشارات التوقف. وبما أن نمط القيادة يحدد سرعة القيادة، فإن السرعة المضبوطة تشكل الحد الأقصى فقط. هذه خاصية الجودة ألفا، فيجب توقع حدوث الأخطاء. - - - New Driving Visualization - تصور القيادة الديد - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - الوضع التجريبي غير متوفر حالياً في هذه السيارة نظراً لاستخدام رصيد التحكم التكيفي بالسرعة من أجل التحكم الطولي. - - - openpilot longitudinal control may come in a future update. - قد يتم الحصول على التحكم الطولي في openpilot في عمليات التحديث المستقبلية. - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - يمكن اختبار نسخة ألفا من التحكم الطولي من openpilot، مع الوضع التجريبي، لكن على الفروع غير المطلقة. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - تمكين التحكم الطولي من openpilot (ألفا) للسماح بالوضع التجريبي. - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - يوصى بالمعيار. في الوضع العدواني، سيتبع الطيار المفتوح السيارات الرائدة بشكل أقرب ويكون أكثر عدوانية مع البنزين والفرامل. في الوضع المريح، سيبقى openpilot بعيدًا عن السيارات الرائدة. في السيارات المدعومة، يمكنك التنقل بين هذه الشخصيات باستخدام زر مسافة عجلة القيادة. - - - 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 - مراقبة السائق المستمرة - - - Enable driver monitoring even when openpilot is not engaged. - تمكين مراقبة السائق حتى عندما لا يكون نظام OpenPilot مُفعّلاً. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - - - Changing this setting will restart openpilot if the car is powered on. - - - - Record and Upload Microphone Audio - - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - - - - WiFiPromptWidget - - Open - انفتح - - - 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> - - - - WifiUI - - Scanning for networks... - يتم البحث عن شبكات... - - - CONNECTING... - يتم الاتصال... - - - FORGET - نسيان هذه الشبكة - - - Forget Wi-Fi Network "%1"? - هل تريد نسيان شبكة الواي فاي "%1"؟ - - - Forget - نسيان - - - diff --git a/selfdrive/ui/translations/create_badges.py b/selfdrive/ui/translations/create_badges.py deleted file mode 100755 index 3e14c33255..0000000000 --- a/selfdrive/ui/translations/create_badges.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 -import json -import os -import requests -import xml.etree.ElementTree as ET - -from openpilot.common.basedir import BASEDIR -from openpilot.selfdrive.ui.tests.test_translations import UNFINISHED_TRANSLATION_TAG -from openpilot.selfdrive.ui.update_translations import LANGUAGES_FILE, TRANSLATIONS_DIR - -TRANSLATION_TAG = " 90 else (204, 55, 27)}" - - # Download badge - badge_label = f"LANGUAGE {name}" - badge_message = f"{percent_finished}% complete" - if unfinished_translations != 0: - badge_message += f" ({unfinished_translations} unfinished)" - - r = requests.get(f"{SHIELDS_URL}/{badge_label}-{badge_message}-{color}", timeout=10) - assert r.status_code == 200, "Error downloading badge" - content_svg = r.content.decode("utf-8") - - xml = ET.fromstring(content_svg) - assert "width" in xml.attrib - max_badge_width = max(max_badge_width, int(xml.attrib["width"])) - - # Make tag ids in each badge unique to combine them into one svg - for tag in ("r", "s"): - content_svg = content_svg.replace(f'id="{tag}"', f'id="{tag}{idx}"') - content_svg = content_svg.replace(f'"url(#{tag})"', f'"url(#{tag}{idx})"') - - badge_svg.extend([f'', content_svg, ""]) - - badge_svg.insert(0, '') - badge_svg.append("") - - with open(os.path.join(BASEDIR, "translation_badge.svg"), "w") as badge_f: - badge_f.write("\n".join(badge_svg)) diff --git a/selfdrive/ui/translations/de.ts b/selfdrive/ui/translations/de.ts deleted file mode 100644 index 28b07029eb..0000000000 --- a/selfdrive/ui/translations/de.ts +++ /dev/null @@ -1,1072 +0,0 @@ - - - - - AbstractAlert - - Close - Schließen - - - Reboot and Update - Aktualisieren und neu starten - - - - AdvancedNetworking - - Back - Zurück - - - Enable Tethering - Tethering aktivieren - - - Tethering Password - Tethering Passwort - - - EDIT - ÄNDERN - - - Enter new tethering password - Neues tethering Passwort eingeben - - - IP Address - IP Adresse - - - Enable Roaming - Roaming aktivieren - - - APN Setting - APN Einstellungen - - - Enter APN - APN eingeben - - - leave blank for automatic configuration - für automatische Konfiguration leer lassen - - - Cellular Metered - Getaktete Verbindung - - - Hidden Network - Verborgenes Netzwerk - - - CONNECT - VERBINDEN - - - Enter SSID - SSID eingeben - - - Enter password - Passwort eingeben - - - for "%1" - für "%1" - - - Prevent large data uploads when on a metered cellular connection - - - - default - - - - metered - - - - unmetered - - - - Wi-Fi Network Metered - - - - Prevent large data uploads when on a metered Wi-Fi connection - - - - - ConfirmationDialog - - Ok - Ok - - - Cancel - Abbrechen - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Du musst die Nutzungsbedingungen akzeptieren, um Openpilot zu benutzen. - - - Back - Zurück - - - Decline, uninstall %1 - Ablehnen, deinstallieren %1 - - - - DeveloperPanel - - Joystick Debug Mode - Joystick Debug-Modus - - - Longitudinal Maneuver Mode - Längsmanöver-Modus - - - openpilot Longitudinal Control (Alpha) - openpilot Längsregelung (Alpha) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - WARNUNG: Die openpilot Längsregelung befindet sich für dieses Fahrzeug im Alpha-Stadium und deaktiviert das automatische Notbremsen (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. - Bei diesem Fahrzeug verwendet openpilot standardmäßig den eingebauten Tempomaten anstelle der openpilot Längsregelung. Aktiviere diese Option, um auf die openpilot Längsregelung umzuschalten. Es wird empfohlen, den experimentellen Modus zu aktivieren, wenn die openpilot Längsregelung (Alpha) aktiviert wird. - - - Enable ADB - ADB aktivieren - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB (Android Debug Bridge) ermöglicht die Verbindung zu deinem Gerät über USB oder Netzwerk. Siehe https://docs.comma.ai/how-to/connect-to-comma für weitere Informationen. - - - - DevicePanel - - Dongle ID - Dongle ID - - - N/A - Nicht verfügbar - - - Serial - Seriennummer - - - Driver Camera - Fahrerkamera - - - PREVIEW - VORSCHAU - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - Vorschau der auf den Fahrer gerichteten Kamera, um sicherzustellen, dass die Fahrerüberwachung eine gute Sicht hat. (Fahrzeug muss aus sein) - - - Reset Calibration - Neu kalibrieren - - - RESET - RESET - - - Are you sure you want to reset calibration? - Bist du sicher, dass du die Kalibrierung zurücksetzen möchtest? - - - Review Training Guide - Trainingsanleitung wiederholen - - - REVIEW - TRAINING - - - Review the rules, features, and limitations of openpilot - Wiederhole die Regeln, Fähigkeiten und Limitierungen von Openpilot - - - Are you sure you want to review the training guide? - Bist du sicher, dass du die Trainingsanleitung wiederholen möchtest? - - - Regulatory - Rechtliche Hinweise - - - VIEW - ANSEHEN - - - Change Language - Sprache ändern - - - CHANGE - ÄNDERN - - - Select a language - Sprache wählen - - - Reboot - Neustart - - - Power Off - Ausschalten - - - Your device is pointed %1° %2 and %3° %4. - Deine Geräteausrichtung ist %1° %2 und %3° %4. - - - down - unten - - - up - oben - - - left - links - - - right - rechts - - - Are you sure you want to reboot? - Bist du sicher, dass du das Gerät neu starten möchtest? - - - Disengage to Reboot - Für Neustart deaktivieren - - - Are you sure you want to power off? - Bist du sicher, dass du das Gerät ausschalten möchtest? - - - Disengage to Power Off - Zum Ausschalten deaktivieren - - - Reset - Zurücksetzen - - - Review - Überprüfen - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Koppele dein Gerät mit Comma Connect (connect.comma.ai) und sichere dir dein Comma Prime Angebot. - - - Pair Device - Gerät koppeln - - - PAIR - KOPPELN - - - Disengage to Reset Calibration - - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - - - - - -Steering lag calibration is %1% complete. - - - - - -Steering lag calibration is complete. - - - - Steering torque response calibration is %1% complete. - - - - Steering torque response calibration is complete. - - - - - DriverViewWindow - - camera starting - Kamera startet - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - EXPERIMENTELLER MODUS AN - - - CHILL MODE ON - ENTSPANNTER MODUS AN - - - - FirehosePanel - - 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 lernt das Fahren, indem es Menschen wie dir beim Fahren zuschaut. - -Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximieren, um die Fahrmodelle von openpilot zu verbessern. Mehr Daten bedeuten größere Modelle, was zu einem besseren Experimentellen Modus führt. - - - Firehose Mode: ACTIVE - Firehose-Modus: AKTIV - - - ACTIVE - AKTIV - - - 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. - Für maximale Effektivität bring dein Gerät jede Woche nach drinnen und verbinde es mit einem guten USB-C-Adapter und WLAN.<br><br>Der Firehose-Modus funktioniert auch während der Fahrt, wenn das Gerät mit einem Hotspot oder einer ungedrosselten SIM-Karte verbunden ist.<br><br><br><b>Häufig gestellte Fragen</b><br><br><i>Spielt es eine Rolle, wie oder wo ich fahre?</i> Nein, fahre einfach wie gewohnt.<br><br><i>Werden im Firehose-Modus alle meine Segmente hochgeladen?</i> Nein, wir wählen selektiv nur einen Teil deiner Segmente aus.<br><br><i>Welcher USB-C-Adapter ist gut?</i> Jedes Schnellladegerät für Handy oder Laptop sollte ausreichen.<br><br><i>Spielt es eine Rolle, welche Software ich nutze?</i> Ja, nur das offizielle Upstream‑openpilot (und bestimmte Forks) kann für das Training verwendet werden. - - - <b>%n segment(s)</b> of your driving is in the training dataset so far. - - <b>%n Segment</b> deiner Fahrten ist bisher im Trainingsdatensatz. - <b>%n Segmente</b> deiner Fahrten sind bisher im Trainingsdatensatz. - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INAKTIV</span>: Verbinde dich mit einem ungedrosselten Netzwerk - - - Firehose Mode - - - - - HudRenderer - - km/h - km/h - - - mph - mph - - - MAX - MAX - - - - InputDialog - - Cancel - Abbrechen - - - Need at least %n character(s)! - - Mindestens %n Buchstabe benötigt! - Mindestens %n Buchstaben benötigt! - - - - - MultiOptionDialog - - Select - Auswählen - - - Cancel - Abbrechen - - - - Networking - - Advanced - Erweitert - - - Enter password - Passwort eingeben - - - for "%1" - für "%1" - - - Wrong password - Falsches Passwort - - - - OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - Stelle sofort eine Internetverbindung her, um nach Updates zu suchen. Wenn du keine Verbindung herstellst, kann openpilot in %1 nicht mehr aktiviert werden. - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - Verbinde dich mit dem Internet, um nach Updates zu suchen. openpilot startet nicht automatisch, bis eine Internetverbindung besteht und nach Updates gesucht wurde. - - - Unable to download updates -%1 - Updates konnten nicht heruntergeladen werden -%1 - - - Taking camera snapshots. System won't start until finished. - Kamera-Snapshots werden aufgenommen. Das System startet erst, wenn dies abgeschlossen ist. - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - Ein Update für das Betriebssystem deines Geräts wird im Hintergrund heruntergeladen. Du wirst aufgefordert, das Update zu installieren, sobald es bereit ist. - - - 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 konnte dein Auto nicht identifizieren. Dein Auto wird entweder nicht unterstützt oder die Steuergeräte (ECUs) werden nicht erkannt. Bitte reiche einen Pull Request ein, um die Firmware-Versionen für das richtige Fahrzeug hinzuzufügen. Hilfe findest du auf 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. - openpilot hat eine Änderung der Montageposition des Geräts erkannt. Stelle sicher, dass das Gerät vollständig in der Halterung sitzt und die Halterung fest an der Windschutzscheibe befestigt ist. - - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - Gerätetemperatur zu hoch. Das System kühlt ab, bevor es startet. Aktuelle interne Komponententemperatur: %1 - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - - - - Acknowledge Excessive Actuation - - - - Snooze Update - Update pausieren - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - - - - - OffroadHome - - UPDATE - Aktualisieren - - - ALERTS - HINWEISE - - - ALERT - HINWEIS - - - - OnroadAlerts - - openpilot Unavailable - openpilot nicht verfügbar - - - TAKE CONTROL IMMEDIATELY - ÜBERNIMM SOFORT DIE KONTROLLE - - - Reboot Device - Gerät neu starten - - - Waiting to start - Warten auf Start - - - System Unresponsive - System reagiert nicht - - - - PairingPopup - - Pair your device to your comma account - Verbinde dein Gerät mit deinem comma Konto - - - Go to https://connect.comma.ai on your phone - Gehe zu https://connect.comma.ai auf deinem Handy - - - Click "add new device" and scan the QR code on the right - Klicke auf "neues Gerät hinzufügen" und scanne den QR code rechts - - - Bookmark connect.comma.ai to your home screen to use it like an app - Füge connect.comma.ai als Lesezeichen auf deinem Homescreen hinzu um es wie eine App zu verwenden - - - Please connect to Wi-Fi to complete initial pairing - Bitte verbinde dich mit WLAN, um die Koppelung abzuschließen. - - - - ParamControl - - Cancel - Abbrechen - - - Enable - Aktivieren - - - - PrimeAdWidget - - Upgrade Now - Jetzt abonieren - - - Become a comma prime member at connect.comma.ai - Werde Comma Prime Mitglied auf connect.comma.ai - - - PRIME FEATURES: - PRIME FUNKTIONEN: - - - Remote access - Fernzugriff - - - 24/7 LTE connectivity - 24/7 LTE-Verbindung - - - 1 year of drive storage - Fahrdaten-Speicherung für 1 Jahr - - - Remote snapshots - Remote-Snapshots - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ ABBONIERT - - - comma prime - comma prime - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - vor %n Minute - vor %n Minuten - - - - %n hour(s) ago - - vor %n Stunde - vor %n Stunden - - - - %n day(s) ago - - vor %n Tag - vor %n Tagen - - - - now - jetzt - - - - SettingsWindow - - × - x - - - Device - Gerät - - - Network - Netzwerk - - - Toggles - Schalter - - - Software - Software - - - Developer - Entwickler - - - Firehose - Firehose - - - - SetupWidget - - Finish Setup - Einrichtung beenden - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Koppele dein Gerät mit Comma Connect (connect.comma.ai) und sichere dir dein Comma Prime Angebot. - - - Pair device - Gerät koppeln - - - - Sidebar - - CONNECT - This is a brand/service name for comma connect, don't translate - CONNECT - - - OFFLINE - OFFLINE - - - ONLINE - ONLINE - - - ERROR - FEHLER - - - TEMP - TEMP - - - HIGH - HOCH - - - GOOD - GUT - - - OK - OK - - - VEHICLE - FAHRZEUG - - - NO - KEIN - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - WLAN - - - ETH - LAN - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - UNINSTALL - Too long for UI - DEINSTALL - - - Uninstall %1 - Deinstalliere %1 - - - Are you sure you want to uninstall? - Bist du sicher, dass du Openpilot entfernen möchtest? - - - CHECK - ÜBERPRÜFEN - - - Updates are only downloaded while the car is off. - Updates werden nur heruntergeladen, wenn das Auto aus ist. - - - Current Version - Aktuelle Version - - - Download - Download - - - Install Update - Update installieren - - - INSTALL - INSTALLIEREN - - - Target Branch - Ziel Branch - - - SELECT - AUSWÄHLEN - - - Select a branch - Wähle einen Branch - - - Uninstall - Deinstallieren - - - failed to check for update - Update-Prüfung fehlgeschlagen - - - up to date, last checked %1 - Auf dem neuesten Stand, zuletzt geprüft am %1 - - - DOWNLOAD - HERUNTERLADEN - - - update available - Update verfügbar - - - never - nie - - - - SshControl - - SSH Keys - SSH Schlüssel - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - Warnung: Dies ermöglicht SSH zugriff für alle öffentlichen Schlüssel in deinen Github Einstellungen. Gib niemals einen anderen Benutzernamen, als deinen Eigenen an. Comma Angestellte fragen dich niemals danach ihren Github Benutzernamen hinzuzufügen. - - - ADD - HINZUFÜGEN - - - Enter your GitHub username - Gib deinen GitHub Benutzernamen ein - - - LOADING - LADEN - - - REMOVE - LÖSCHEN - - - Username '%1' has no keys on GitHub - Benutzername '%1' hat keine Schlüssel auf GitHub - - - Request timed out - Zeitüberschreitung der Anforderung - - - Username '%1' doesn't exist on GitHub - Benutzername '%1' existiert nicht auf GitHub - - - - SshToggle - - Enable SSH - SSH aktivieren - - - - TermsPage - - Decline - Ablehnen - - - Agree - Zustimmen - - - Welcome to openpilot - Willkommen bei 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. - Du musst die Nutzungsbedingungen akzeptieren, um openpilot zu verwenden. Lies die aktuellen Bedingungen unter <span style='color: #465BEA;'>https://comma.ai/terms</span>, bevor du fortfährst. - - - - TogglesPanel - - Enable openpilot - Openpilot aktivieren - - - Enable Lane Departure Warnings - Spurverlassenswarnungen aktivieren - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - Erhalte Warnungen, zurück in die Spur zu lenken, wenn dein Auto über eine erkannte Fahrstreifenmarkierung ohne aktivierten Blinker mit mehr als 50 km/h fährt. - - - Use Metric System - Benutze das metrische System - - - Display speed in km/h instead of mph. - Zeige die Geschwindigkeit in km/h anstatt von mph. - - - Record and Upload Driver Camera - Fahrerkamera aufnehmen und hochladen - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - Lade Daten der Fahreraufmerksamkeitsüberwachungskamera hoch, um die Fahreraufmerksamkeitsüberwachungsalgorithmen zu verbessern. - - - When enabled, pressing the accelerator pedal will disengage openpilot. - Wenn aktiviert, deaktiviert sich Openpilot sobald das Gaspedal betätigt wird. - - - Experimental Mode - Experimenteller Modus - - - Disengage on Accelerator Pedal - Bei Gasbetätigung ausschalten - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - Openpilot fährt standardmäßig im <b>entspannten Modus</b>. Der Experimentelle Modus aktiviert<b>Alpha-level Funktionen</b>, die noch nicht für den entspannten Modus bereit sind. Die experimentellen Funktionen sind die Folgenden: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - Lass das Fahrmodell Gas und Bremse kontrollieren. Openpilot wird so fahren, wie es dies von einem Menschen erwarten würde; inklusive des Anhaltens für Ampeln und Stoppschildern. Da das Fahrmodell entscheidet wie schnell es fährt stellt die gesetzte Geschwindigkeit lediglich das obere Limit dar. Dies ist ein Alpha-level Funktion. Fehler sind zu erwarten. - - - New Driving Visualization - Neue Fahrvisualisierung - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - Der experimentelle Modus ist momentan für dieses Auto nicht verfügbar da es den eingebauten adaptiven Tempomaten des Autos benutzt. - - - Aggressive - Aggressiv - - - Standard - Standard - - - Relaxed - Entspannt - - - Driving Personality - Fahrstil - - - End-to-End Longitudinal Control - Ende-zu-Ende Längsregelung - - - openpilot longitudinal control may come in a future update. - Die openpilot Längsregelung könnte in einem zukünftigen Update verfügbar sein. - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Eine Alpha-Version der openpilot Längsregelung kann zusammen mit dem Experimentellen Modus auf non-stable Branches getestet werden. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Aktiviere den Schalter für openpilot Längsregelung (Alpha), um den Experimentellen Modus zu erlauben. - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Standard wird empfohlen. Im aggressiven Modus folgt openpilot vorausfahrenden Fahrzeugen enger und ist beim Gasgeben und Bremsen aggressiver. Im entspannten Modus hält openpilot mehr Abstand zu vorausfahrenden Fahrzeugen. Bei unterstützten Fahrzeugen kannst du mit der Abstandstaste am Lenkrad zwischen diesen Fahrstilen wechseln. - - - 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. - Die Fahrvisualisierung wechselt bei niedrigen Geschwindigkeiten auf die nach vorne gerichtete Weitwinkelkamera, um Kurven besser darzustellen. Das Logo des Experimentellen Modus wird außerdem oben rechts angezeigt. - - - Always-On Driver Monitoring - Dauerhaft aktive Fahrerüberwachung - - - Enable driver monitoring even when openpilot is not engaged. - Fahrerüberwachung auch aktivieren, wenn openpilot nicht aktiv ist. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - - - Changing this setting will restart openpilot if the car is powered on. - - - - Record and Upload Microphone Audio - - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - - - - WiFiPromptWidget - - Open - Öffnen - - - Maximize your training data uploads to improve openpilot's driving models. - Maximiere deine Trainingsdaten-Uploads, um die Fahrmodelle von openpilot zu verbessern. - - - <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> Firehose-Modus <span style='font-family: Noto Color Emoji;'>🔥</span> - - - - WifiUI - - Scanning for networks... - Suche nach Netzwerken... - - - CONNECTING... - VERBINDEN... - - - FORGET - VERGESSEN - - - Forget Wi-Fi Network "%1"? - WLAN Netzwerk "%1" vergessen? - - - Forget - Vergessen - - - diff --git a/selfdrive/ui/translations/en.ts b/selfdrive/ui/translations/en.ts deleted file mode 100644 index fbccbedb20..0000000000 --- a/selfdrive/ui/translations/en.ts +++ /dev/null @@ -1,48 +0,0 @@ - - - - - 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 - - Need at least %n character(s)! - - Need at least %n character! - Need at least %n characters! - - - - - QObject - - %n minute(s) ago - - %n minute ago - %n minutes ago - - - - %n hour(s) ago - - %n hour ago - %n hours ago - - - - %n day(s) ago - - %n day ago - %n days ago - - - - diff --git a/selfdrive/ui/translations/es.ts b/selfdrive/ui/translations/es.ts deleted file mode 100644 index e8d57dddbe..0000000000 --- a/selfdrive/ui/translations/es.ts +++ /dev/null @@ -1,1074 +0,0 @@ - - - - - AbstractAlert - - Close - Cerrar - - - Reboot and Update - Reiniciar y Actualizar - - - - AdvancedNetworking - - Back - Volver - - - Enable Tethering - Activar Tether - - - Tethering Password - Contraseña de Tethering - - - EDIT - EDITAR - - - Enter new tethering password - Nueva contraseña de tethering - - - IP Address - Dirección IP - - - Enable Roaming - Activar Roaming - - - APN Setting - Configuración de APN - - - Enter APN - Insertar APN - - - leave blank for automatic configuration - dejar en blanco para configuración automática - - - Cellular Metered - Plano de datos limitado - - - Hidden Network - Red Oculta - - - CONNECT - CONECTAR - - - Enter SSID - Ingrese SSID - - - Enter password - Ingrese contraseña - - - for "%1" - para "%1" - - - Prevent large data uploads when on a metered cellular connection - Evite cargas de grandes cantidades de datos cuando utilice una conexión celular medida - - - default - por defecto - - - metered - medido - - - unmetered - sin medidor - - - Wi-Fi Network Metered - Red Wi-Fi medida - - - Prevent large data uploads when on a metered Wi-Fi connection - Evite cargas de grandes cantidades de datos cuando esté en una conexión Wi-Fi medida - - - - ConfirmationDialog - - Ok - OK - - - Cancel - Cancelar - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Debe aceptar los términos y condiciones para poder utilizar openpilot. - - - Back - Atrás - - - Decline, uninstall %1 - Rechazar, desinstalar %1 - - - - DeveloperPanel - - Joystick Debug Mode - Modo de depuración de joystick - - - Longitudinal Maneuver Mode - Modo de maniobra longitudinal - - - openpilot Longitudinal Control (Alpha) - Control longitudinal de openpilot (fase experimental) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - AVISO: el control longitudinal de openpilot está en fase experimental para este automóvil y desactivará el Frenado Automático de Emergencia (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. - En este automóvil, openpilot se configura de manera predeterminada con el Autocrucero Adaptativo (ACC) incorporado en el automóvil en lugar del control longitudinal de openpilot. Habilita esta opción para cambiar al control longitudinal de openpilot. Se recomienda activar el modo experimental al habilitar el control longitudinal de openpilot (aún en fase experimental). - - - Enable ADB - Activar ADB - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB (Android Debug Bridge) permite conectar a su dispositivo por USB o por red. Visite https://docs.comma.ai/how-to/connect-to-comma para más información. - - - - DevicePanel - - Dongle ID - Dongle ID - - - N/A - N/A - - - Serial - Serial - - - Pair Device - Emparejar Dispositivo - - - PAIR - EMPAREJAR - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Empareja tu dispositivo con comma connect (connect.comma.ai) y reclama tu oferta de comma prime. - - - Driver Camera - Cámara del conductor - - - PREVIEW - VISUALIZAR - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - Previsualizar la cámara del conductor para garantizar que la monitorización del sistema tenga buena visibilidad (el vehículo tiene que estar apagado) - - - Reset Calibration - Formatear Calibración - - - RESET - REINICIAR - - - Are you sure you want to reset calibration? - ¿Seguro que quiere formatear la calibración? - - - Reset - Formatear - - - Review Training Guide - Revisar la Guía de Entrenamiento - - - REVIEW - REVISAR - - - Review the rules, features, and limitations of openpilot - Revisar las reglas, características y limitaciones de openpilot - - - Are you sure you want to review the training guide? - ¿Seguro que quiere revisar la guía de entrenamiento? - - - Review - Revisar - - - Regulatory - Regulador - - - VIEW - VER - - - Change Language - Cambiar Idioma - - - CHANGE - CAMBIAR - - - Select a language - Seleccione el idioma - - - Reboot - Reiniciar - - - Power Off - Apagar - - - Your device is pointed %1° %2 and %3° %4. - Su dispositivo está apuntando %1° %2 y %3° %4. - - - down - abajo - - - up - arriba - - - left - izquierda - - - right - derecha - - - Are you sure you want to reboot? - ¿Seguro qué quiere reiniciar? - - - Disengage to Reboot - Desactivar para Reiniciar - - - Are you sure you want to power off? - ¿Seguro qué quiere apagar? - - - Disengage to Power Off - Desactivar para apagar - - - Disengage to Reset Calibration - Desactivar para restablecer la calibración - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - openpilot requiere que el dispositivo esté montado dentro de los 4° hacia la izquierda o la derecha y dentro de los 5° hacia arriba o 9° hacia abajo. - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - Openpilot se calibra continuamente, por lo que rara vez es necesario reiniciarlo. Restablecer la calibración reiniciará Openpilot si el automóvil está encendido. - - - - -Steering lag calibration is %1% complete. - - -La calibración del retraso de dirección está %1% completada. - - - - -Steering lag calibration is complete. - - -La calibración del retraso de la dirección está completa. - - - Steering torque response calibration is %1% complete. - La calibración de la respuesta del par de dirección está %1% completada. - - - Steering torque response calibration is complete. - La calibración de la respuesta del par de dirección está completa. - - - - DriverViewWindow - - camera starting - iniciando cámara - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - MODO EXPERIMENTAL - - - CHILL MODE ON - MODO CHILL - - - - FirehosePanel - - 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 aprende a conducir observando a humanos, como tú, conducir. - -El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para mejorar los modelos de conducción de openpilot. Más datos significan modelos más grandes, lo que significa un mejor Modo Experimental. - - - Firehose Mode: ACTIVE - Modo Firehose: ACTIVO - - - ACTIVE - ACTIVO - - - 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 máxima efectividad, traiga su dispositivo adentro y conéctelo a un buen adaptador USB-C y Wi-Fi semanalmente.<br><br>El Modo Firehose también puede funcionar mientras conduce si está conectado a un punto de acceso o tarjeta SIM ilimitada.<br><br><br><b>Preguntas Frecuentes</b><br><br><i>¿Importa cómo o dónde conduzco?</i> No, solo conduzca como lo haría normalmente.<br><br><i>¿Se extraen todos mis segmentos en el Modo Firehose?</i> No, seleccionamos selectivamente un subconjunto de sus segmentos.<br><br><i>¿Qué es un buen adaptador USB-C?</i> Cualquier cargador rápido de teléfono o portátil debería funcionar.<br><br><i>¿Importa qué software ejecuto?</i> Sí, solo el openpilot original (y forks específicos) pueden usarse para el entrenamiento. - - - <b>%n segment(s)</b> of your driving is in the training dataset so far. - - <b>%n segmento</b> de tu conducción está en el conjunto de datos de entrenamiento hasta ahora. - <b>%n segmentos</b> de tu conducción están en el conjunto de datos de entrenamiento hasta ahora. - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVO</span>: conéctate a una red sin límite de datos - - - Firehose Mode - Modo manguera contra incendios - - - - HudRenderer - - km/h - km/h - - - mph - mph - - - MAX - MAX - - - - InputDialog - - Cancel - Cancelar - - - Need at least %n character(s)! - - ¡Necesita mínimo %n caracter! - ¡Necesita mínimo %n caracteres! - - - - - MultiOptionDialog - - Select - Seleccionar - - - Cancel - Cancelar - - - - Networking - - Advanced - Avanzado - - - Enter password - Ingresar contraseña - - - for "%1" - para "%1" - - - Wrong password - Contraseña incorrecta - - - - OffroadAlert - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - La temperatura del dispositivo es muy alta. El sistema se está enfriando antes de iniciar. Temperatura actual del componente interno: %1 - - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - Conéctese inmediatamente al internet para buscar actualizaciones. Si no se conecta al internet, openpilot no iniciará en %1 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - Conectese al internet para buscar actualizaciones. openpilot no iniciará automáticamente hasta conectarse al internet para buscar actualizaciones. - - - Unable to download updates -%1 - Incapaz de descargar actualizaciones. -%1 - - - Taking camera snapshots. System won't start until finished. - Tomando capturas de las cámaras. El sistema no se iniciará hasta que finalice. - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - Se está descargando una actualización del sistema operativo de su dispositivo en segundo plano. Se le pedirá que actualice cuando esté listo para instalarse. - - - 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 no pudo identificar su automóvil. Su automóvil no es compatible o no se reconocen sus ECU. Por favor haga un pull request para agregar las versiones de firmware del vehículo adecuado. ¿Necesita ayuda? Únase a 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. - openpilot detectó un cambio en la posición de montaje del dispositivo. Asegúrese de que el dispositivo esté completamente asentado en el soporte y que el soporte esté firmemente asegurado al parabrisas. - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - - - - Acknowledge Excessive Actuation - - - - Snooze Update - Posponer Actualización - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - - - - - OffroadHome - - UPDATE - ACTUALIZAR - - - ALERTS - ALERTAS - - - ALERT - ALERTA - - - - OnroadAlerts - - openpilot Unavailable - openpilot no disponible - - - TAKE CONTROL IMMEDIATELY - TOME CONTROL INMEDIATAMENTE - - - Reboot Device - Reiniciar Dispositivo - - - Waiting to start - Esperando para iniciar - - - System Unresponsive - Systema no responde - - - - PairingPopup - - Pair your device to your comma account - Empareje su dispositivo con su cuenta de comma - - - Go to https://connect.comma.ai on your phone - Vaya a https://connect.comma.ai en su teléfono - - - Click "add new device" and scan the QR code on the right - Seleccione "agregar nuevo dispositivo" y escanee el código QR a la derecha - - - Bookmark connect.comma.ai to your home screen to use it like an app - Añada connect.comma.ai a su pantalla de inicio para usarlo como una aplicación - - - Please connect to Wi-Fi to complete initial pairing - Conéctese a Wi-Fi para completar el emparejamiento inicial - - - - ParamControl - - Enable - Activar - - - Cancel - Cancelar - - - - PrimeAdWidget - - Upgrade Now - Actualizar Ahora - - - Become a comma prime member at connect.comma.ai - Hazte miembro de comma prime en connect.comma.ai - - - PRIME FEATURES: - BENEFICIOS PRIME: - - - Remote access - Acceso remoto - - - 24/7 LTE connectivity - Conectividad LTE 24/7 - - - 1 year of drive storage - 1 año de almacenamiento - - - Remote snapshots - Capturas remotas - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ SUSCRITO - - - comma prime - comma prime - - - - QObject - - openpilot - openpilot - - - now - ahora - - - %n minute(s) ago - - hace %n min - hace %n mins - - - - %n hour(s) ago - - hace %n hora - hace %n horas - - - - %n day(s) ago - - hace %n día - hace %n días - - - - - SettingsWindow - - × - × - - - Device - Dispositivo - - - Network - Red - - - Toggles - Ajustes - - - Software - Software - - - Developer - Desarrollador - - - Firehose - Firehose - - - - SetupWidget - - Finish Setup - Terminar configuración - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Empareje su dispositivo con comma connect (connect.comma.ai) y reclame su oferta de comma prime. - - - Pair device - Emparejar dispositivo - - - - Sidebar - - CONNECT - CONNECT - - - OFFLINE - OFFLINE - - - ONLINE - EN LÍNEA - - - ERROR - ERROR - - - TEMP - TEMP - - - HIGH - ALTA - - - GOOD - BUENA - - - OK - OK - - - VEHICLE - VEHÍCULO - - - NO - SIN - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Updates are only downloaded while the car is off. - Actualizaciones solo se descargan con el auto apagado. - - - Current Version - Versión Actual - - - Download - Descargar - - - CHECK - VERIFICAR - - - Install Update - Actualizar - - - INSTALL - INSTALAR - - - Target Branch - Rama objetivo - - - SELECT - SELECCIONAR - - - Select a branch - Selecione una rama - - - Uninstall %1 - Desinstalar %1 - - - UNINSTALL - DESINSTALAR - - - Are you sure you want to uninstall? - ¿Seguro qué desea desinstalar? - - - Uninstall - Desinstalar - - - failed to check for update - no se pudo buscar actualizaciones - - - DOWNLOAD - DESCARGAR - - - update available - actualización disponible - - - never - nunca - - - up to date, last checked %1 - actualizado, último chequeo %1 - - - - SshControl - - SSH Keys - Clave SSH - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - Aviso: Esto otorga acceso SSH a todas las claves públicas en su Github. Nunca ingrese un nombre de usuario de Github que no sea suyo. Un empleado de comma NUNCA le pedirá que añada un usuario de Github que no sea el suyo. - - - ADD - AÑADIR - - - Enter your GitHub username - Ingrese su usuario de GitHub - - - LOADING - CARGANDO - - - REMOVE - ELIMINAR - - - Username '%1' has no keys on GitHub - El usuario "%1” no tiene claves en GitHub - - - Request timed out - Solicitud expirada - - - Username '%1' doesn't exist on GitHub - El usuario '%1' no existe en Github - - - - SshToggle - - Enable SSH - Habilitar SSH - - - - TermsPage - - Decline - Rechazar - - - Agree - Aceptar - - - Welcome to openpilot - Bienvenido a 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. - Debe aceptar los Términos y Condiciones para usar openpilot. Lea los términos más recientes en <span style='color: #465BEA;'>https://comma.ai/terms</span> antes de continuar. - - - - TogglesPanel - - Enable openpilot - Activar openpilot - - - Experimental Mode - Modo Experimental - - - Disengage on Accelerator Pedal - Desactivar con el Acelerador - - - When enabled, pressing the accelerator pedal will disengage openpilot. - Cuando esté activado, presionar el acelerador deshabilitará openpilot. - - - Enable Lane Departure Warnings - Activar Avisos de Salida de Carril - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - Recibir alertas para volver al carril cuando su vehículo se salga fuera del carril sin que esté activada la señal de giro y esté conduciendo por encima de 50 km/h (31 mph). - - - Always-On Driver Monitoring - Monitoreo Permanente del Conductor - - - Enable driver monitoring even when openpilot is not engaged. - Habilitar el monitoreo del conductor incluso cuando Openpilot no esté activado. - - - Record and Upload Driver Camera - Grabar y Subir Cámara del Conductor - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - Subir datos de la cámara del conductor para ayudar a mejorar el algoritmo de monitoreo del conductor. - - - Use Metric System - Usar Sistema Métrico - - - Display speed in km/h instead of mph. - Mostrar velocidad en km/h en vez de mph. - - - Aggressive - Agresivo - - - Standard - Estándar - - - Relaxed - Relajado - - - Driving Personality - Personalidad de conducción - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Se recomienda el modo estándar. En el modo agresivo, openpilot seguirá más cerca a los autos delante suyo y será más agresivo con el acelerador y el freno. En modo relajado, openpilot se mantendrá más alejado de los autos delante suyo. En automóviles compatibles, puede recorrer estas personalidades con el botón de distancia del volante. - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot por defecto conduce en <b>modo chill</b>. El modo Experimental activa <b>funcionalidades en fase experimental</b>, que no están listas para el modo chill. Las funcionalidades del modo expeimental están listados abajo: - - - End-to-End Longitudinal Control - Control Longitudinal de Punta a Punta - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - Dajar que el modelo de conducción controle la aceleración y el frenado. openpilot conducirá como piensa que lo haría una persona, incluiyendo parar en los semáforos en rojo y las señales de alto. Dado que el modelo decide la velocidad de conducción, la velocidad de crucero establecida solo actuará como el límite superior. Este recurso aún está en fase experimental; deberían esperarse errores. - - - New Driving Visualization - Nueva Visualización de la conducción - - - 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. - La visualización de la conducción cambiará a la cámara que enfoca la carretera a velocidades bajas para mostrar mejor los giros. El logo del modo experimental también se mostrará en la esquina superior derecha. - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - El modo Experimental no está disponible actualmente para este auto, ya que el ACC default del auto está siendo usado para el control longitudinal. - - - openpilot longitudinal control may come in a future update. - El control longitudinal de openpilot podrá llegar en futuras actualizaciones. - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Se puede probar una versión experimental del control longitudinal openpilot, junto con el modo Experimental, en ramas no liberadas. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Activar el control longitudinal (fase experimental) para permitir el modo Experimental. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - Utilice el sistema openpilot para el control de crucero adaptativo y la asistencia al conductor para mantenerse en el carril. Se requiere su atención en todo momento para utilizar esta función. - - - Changing this setting will restart openpilot if the car is powered on. - Cambiar esta configuración reiniciará Openpilot si el automóvil está encendido. - - - Record and Upload Microphone Audio - Grabar y cargar audio de micrófono - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - Graba y almacena el audio del micrófono mientras conduces. El audio se incluirá en el video de la cámara del tablero en comma connect. - - - - WiFiPromptWidget - - Open - Abrir - - - Maximize your training data uploads to improve openpilot's driving models. - Maximice sus cargas de datos de entrenamiento para mejorar los modelos de conducción de 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> Modo Firehose <span style='font-family: Noto Color Emoji;'>🔥</span> - - - - WifiUI - - Scanning for networks... - Buscando redes... - - - CONNECTING... - CONECTANDO... - - - FORGET - OLVIDAR - - - Forget Wi-Fi Network "%1"? - ¿Olvidar la Red de Wi-Fi "%1"? - - - Forget - Olvidar - - - diff --git a/selfdrive/ui/translations/fr.ts b/selfdrive/ui/translations/fr.ts deleted file mode 100644 index 4efb92d0c8..0000000000 --- a/selfdrive/ui/translations/fr.ts +++ /dev/null @@ -1,1068 +0,0 @@ - - - - - AbstractAlert - - Close - Fermer - - - Reboot and Update - Redémarrer et mettre à jour - - - - AdvancedNetworking - - Back - Retour - - - Enable Tethering - Activer le partage de connexion - - - Tethering Password - Mot de passe du partage de connexion - - - EDIT - MODIFIER - - - Enter new tethering password - Entrez le nouveau mot de passe du partage de connexion - - - IP Address - Adresse IP - - - Enable Roaming - Activer l'itinérance - - - APN Setting - Paramètre APN - - - Enter APN - Entrer le nom du point d'accès - - - leave blank for automatic configuration - laisser vide pour une configuration automatique - - - Cellular Metered - Connexion cellulaire limitée - - - Hidden Network - Réseau Caché - - - CONNECT - CONNECTER - - - Enter SSID - Entrer le SSID - - - Enter password - Entrer le mot de passe - - - for "%1" - pour "%1" - - - Prevent large data uploads when on a metered cellular connection - - - - default - - - - metered - - - - unmetered - - - - Wi-Fi Network Metered - - - - Prevent large data uploads when on a metered Wi-Fi connection - - - - - ConfirmationDialog - - Ok - Ok - - - Cancel - Annuler - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Vous devez accepter les conditions générales pour utiliser openpilot. - - - Back - Retour - - - Decline, uninstall %1 - Refuser, désinstaller %1 - - - - DeveloperPanel - - Joystick Debug Mode - Mode débogage au joystick - - - Longitudinal Maneuver Mode - Mode manœuvre longitudinale - - - openpilot Longitudinal Control (Alpha) - Contrôle longitudinal openpilot (Alpha) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - ATTENTION : le contrôle longitudinal openpilot est en alpha pour cette voiture et désactivera le freinage d'urgence automatique (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. - Sur cette voiture, openpilot utilise par défaut le régulateur de vitesse adaptatif intégré à la voiture plutôt que le contrôle longitudinal d'openpilot. Activez ceci pour passer au contrôle longitudinal openpilot. Il est recommandé d'activer le mode expérimental lors de l'activation du contrôle longitudinal openpilot alpha. - - - Enable ADB - - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - - - - - DevicePanel - - Dongle ID - Dongle ID - - - N/A - N/A - - - Serial - N° de série - - - Driver Camera - Caméra conducteur - - - PREVIEW - APERÇU - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - Aperçu de la caméra orientée vers le conducteur pour assurer une bonne visibilité de la surveillance du conducteur. (véhicule doit être éteint) - - - Reset Calibration - Réinitialiser la calibration - - - RESET - RÉINITIALISER - - - Are you sure you want to reset calibration? - Êtes-vous sûr de vouloir réinitialiser la calibration ? - - - Reset - Réinitialiser - - - Review Training Guide - Revoir le guide de formation - - - REVIEW - REVOIR - - - Review the rules, features, and limitations of openpilot - Revoir les règles, fonctionnalités et limitations d'openpilot - - - Are you sure you want to review the training guide? - Êtes-vous sûr de vouloir revoir le guide de formation ? - - - Review - Revoir - - - Regulatory - Réglementaire - - - VIEW - VOIR - - - Change Language - Changer de langue - - - CHANGE - CHANGER - - - Select a language - Choisir une langue - - - Reboot - Redémarrer - - - Power Off - Éteindre - - - Your device is pointed %1° %2 and %3° %4. - Votre appareil est orienté %1° %2 et %3° %4. - - - down - bas - - - up - haut - - - left - gauche - - - right - droite - - - Are you sure you want to reboot? - Êtes-vous sûr de vouloir redémarrer ? - - - Disengage to Reboot - Désengager pour redémarrer - - - Are you sure you want to power off? - Êtes-vous sûr de vouloir éteindre ? - - - Disengage to Power Off - Désengager pour éteindre - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Associez votre appareil avec comma connect (connect.comma.ai) et profitez de l'offre comma prime. - - - Pair Device - Associer l'appareil - - - PAIR - ASSOCIER - - - Disengage to Reset Calibration - - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - - - - - -Steering lag calibration is %1% complete. - - - - - -Steering lag calibration is complete. - - - - Steering torque response calibration is %1% complete. - - - - Steering torque response calibration is complete. - - - - - DriverViewWindow - - camera starting - démarrage de la caméra - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - MODE EXPÉRIMENTAL ACTIVÉ - - - CHILL MODE ON - MODE DÉTENTE ACTIVÉ - - - - FirehosePanel - - 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 - - - - 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>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. - - - - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - - - - Firehose Mode - - - - - HudRenderer - - km/h - km/h - - - mph - mi/h - - - MAX - MAX - - - - InputDialog - - Cancel - Annuler - - - Need at least %n character(s)! - - Besoin d'au moins %n caractère ! - Besoin d'au moins %n caractères ! - - - - - MultiOptionDialog - - Select - Sélectionner - - - Cancel - Annuler - - - - Networking - - Advanced - Avancé - - - Enter password - Entrer le mot de passe - - - for "%1" - pour "%1" - - - Wrong password - Mot de passe incorrect - - - - OffroadAlert - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - Température de l'appareil trop élevée. Le système doit refroidir avant de démarrer. Température actuelle de l'appareil : %1 - - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - Connectez-vous immédiatement à internet pour vérifier les mises à jour. Si vous ne vous connectez pas à internet, openpilot ne s'engagera pas dans %1 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - Connectez l'appareil à internet pour vérifier les mises à jour. openpilot ne démarrera pas automatiquement tant qu'il ne se connecte pas à internet pour vérifier les mises à jour. - - - Unable to download updates -%1 - Impossible de télécharger les mises à jour -%1 - - - Taking camera snapshots. System won't start until finished. - Capture de clichés photo. Le système ne démarrera pas tant qu'il n'est pas terminé. - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - Une mise à jour du système d'exploitation de votre appareil est en cours de téléchargement en arrière-plan. Vous serez invité à effectuer la mise à jour lorsqu'elle sera prête à être installée. - - - 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 n'a pas pu identifier votre voiture. Votre voiture n'est pas supportée ou ses ECUs ne sont pas reconnues. Veuillez soumettre un pull request pour ajouter les versions de firmware au véhicule approprié. Besoin d'aide ? Rejoignez 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. - openpilot a détecté un changement dans la position de montage de l'appareil. Assurez-vous que l'appareil est totalement inséré dans le support et que le support est fermement fixé au pare-brise. - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - - - - Acknowledge Excessive Actuation - - - - Snooze Update - Reporter la mise à jour - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - - - - - OffroadHome - - UPDATE - MISE À JOUR - - - ALERTS - ALERTES - - - ALERT - ALERTE - - - - OnroadAlerts - - openpilot Unavailable - openpilot indisponible - - - TAKE CONTROL IMMEDIATELY - REPRENEZ LE CONTRÔLE IMMÉDIATEMENT - - - Reboot Device - Redémarrer l'appareil - - - Waiting to start - En attente de démarrage - - - System Unresponsive - Système inopérant - - - - PairingPopup - - Pair your device to your comma account - Associez votre appareil à votre compte comma - - - Go to https://connect.comma.ai on your phone - Allez sur https://connect.comma.ai sur votre téléphone - - - Click "add new device" and scan the QR code on the right - Cliquez sur "ajouter un nouvel appareil" et scannez le code QR à droite - - - Bookmark connect.comma.ai to your home screen to use it like an app - Ajoutez connect.comma.ai à votre écran d'accueil pour l'utiliser comme une application - - - Please connect to Wi-Fi to complete initial pairing - Connectez-vous au Wi-Fi pour terminer l'appairage initial - - - - ParamControl - - Enable - Activer - - - Cancel - Annuler - - - - PrimeAdWidget - - Upgrade Now - Mettre à niveau - - - Become a comma prime member at connect.comma.ai - Devenez membre comma prime sur connect.comma.ai - - - PRIME FEATURES: - FONCTIONNALITÉS PRIME : - - - Remote access - Accès à distance - - - 24/7 LTE connectivity - Connexion LTE 24/7 - - - 1 year of drive storage - 1 an de stockage de trajets - - - Remote snapshots - Captures à distance - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ ABONNÉ - - - comma prime - comma prime - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - il y a %n minute - il y a %n minutes - - - - %n hour(s) ago - - il y a %n heure - il y a %n heures - - - - %n day(s) ago - - il y a %n jour - il y a %n jours - - - - now - maintenant - - - - SettingsWindow - - × - × - - - Device - Appareil - - - Network - Réseau - - - Toggles - Options - - - Software - Logiciel - - - Developer - Dév. - - - Firehose - - - - - SetupWidget - - Finish Setup - Terminer l'installation - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Associez votre appareil avec comma connect (connect.comma.ai) et profitez de l'offre comma prime. - - - Pair device - Associer l'appareil - - - - Sidebar - - CONNECT - CONNECTER - - - OFFLINE - HORS LIGNE - - - ONLINE - EN LIGNE - - - ERROR - ERREUR - - - TEMP - TEMP - - - HIGH - HAUT - - - GOOD - BON - - - OK - OK - - - VEHICLE - VÉHICULE - - - NO - NON - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Updates are only downloaded while the car is off. - Les MàJ sont téléchargées uniquement si la voiture est éteinte. - - - Current Version - Version actuelle - - - Download - Télécharger - - - CHECK - VÉRIFIER - - - Install Update - Installer la mise à jour - - - INSTALL - INSTALLER - - - Target Branch - Branche cible - - - SELECT - SÉLECTIONNER - - - Select a branch - Sélectionner une branche - - - Uninstall %1 - Désinstaller %1 - - - UNINSTALL - DÉSINSTALLER - - - Are you sure you want to uninstall? - Êtes-vous sûr de vouloir désinstaller ? - - - Uninstall - Désinstaller - - - failed to check for update - échec de la vérification de la mise à jour - - - DOWNLOAD - TÉLÉCHARGER - - - update available - mise à jour disponible - - - never - jamais - - - up to date, last checked %1 - à jour, dernière vérification %1 - - - - SshControl - - SSH Keys - Clés SSH - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - Attention : Ceci accorde l'accès SSH à toutes les clés publiques de vos paramètres GitHub. N'entrez jamais un nom d'utilisateur GitHub autre que le vôtre. Un employé de comma ne vous demandera JAMAIS d'ajouter son nom d'utilisateur GitHub. - - - ADD - AJOUTER - - - Enter your GitHub username - Entrez votre nom d'utilisateur GitHub - - - LOADING - CHARGEMENT - - - REMOVE - SUPPRIMER - - - Username '%1' has no keys on GitHub - L'utilisateur '%1' n'a pas de clés sur GitHub - - - Request timed out - Délai de la demande dépassé - - - Username '%1' doesn't exist on GitHub - L'utilisateur '%1' n'existe pas sur GitHub - - - - SshToggle - - Enable SSH - Activer SSH - - - - TermsPage - - Decline - Refuser - - - Agree - Accepter - - - 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. - - - - - TogglesPanel - - Enable openpilot - Activer openpilot - - - Experimental Mode - Mode expérimental - - - Disengage on Accelerator Pedal - Désengager avec la pédale d'accélérateur - - - When enabled, pressing the accelerator pedal will disengage openpilot. - Lorsqu'il est activé, appuyer sur la pédale d'accélérateur désengagera openpilot. - - - Enable Lane Departure Warnings - Activer les avertissements de sortie de voie - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - Recevez des alertes pour revenir dans la voie lorsque votre véhicule dérive au-delà d'une ligne de voie détectée sans clignotant activé en roulant à plus de 31 mph (50 km/h). - - - Record and Upload Driver Camera - Enregistrer et télécharger la caméra conducteur - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - Publiez les données de la caméra orientée vers le conducteur et aidez à améliorer l'algorithme de surveillance du conducteur. - - - Use Metric System - Utiliser le système métrique - - - Display speed in km/h instead of mph. - Afficher la vitesse en km/h au lieu de mph. - - - Aggressive - Aggressif - - - Standard - Standard - - - Relaxed - Détendu - - - Driving Personality - Personnalité de conduite - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - Par défaut, openpilot conduit en <b>mode détente</b>. Le mode expérimental permet d'activer des <b>fonctionnalités alpha</b> qui ne sont pas prêtes pour le mode détente. Les fonctionnalités expérimentales sont listées ci-dessous : - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - Laissez le modèle de conduite contrôler l'accélérateur et les freins. openpilot conduira comme il pense qu'un humain le ferait, y compris s'arrêter aux feux rouges et aux panneaux stop. Comme le modèle de conduite décide de la vitesse à adopter, la vitesse définie ne servira que de limite supérieure. Cette fonctionnalité est de qualité alpha ; des erreurs sont à prévoir. - - - New Driving Visualization - Nouvelle visualisation de la conduite - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - Le mode expérimental est actuellement indisponible pour cette voiture car le régulateur de vitesse adaptatif d'origine est utilisé pour le contrôle longitudinal. - - - openpilot longitudinal control may come in a future update. - Le contrôle longitudinal openpilot pourrait être disponible dans une future mise à jour. - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Une version alpha du contrôle longitudinal openpilot peut être testée, avec le mode expérimental, sur des branches non publiées. - - - End-to-End Longitudinal Control - Contrôle longitudinal de bout en bout - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Activer le contrôle longitudinal d'openpilot (en alpha) pour autoriser le mode expérimental. - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Le mode Standard est recommandé. En mode Agressif, openpilot suivra les véhicules de plus près et sera plus dynamique avec l'accélérateur et le frein. En mode Détendu, openpilot maintiendra une distance plus importante avec les véhicules qui précèdent. Sur les véhicules compatibles, vous pouvez alterner entre ces personnalités à l'aide du bouton de distance au volant. - - - 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. - La visualisation de la conduite passera sur la caméra grand angle dirigée vers la route à faible vitesse afin de mieux montrer certains virages. Le logo du mode expérimental s'affichera également dans le coin supérieur droit. - - - Always-On Driver Monitoring - Surveillance continue du conducteur - - - Enable driver monitoring even when openpilot is not engaged. - Activer la surveillance conducteur lorsque openpilot n'est pas actif. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - - - Changing this setting will restart openpilot if the car is powered on. - - - - Record and Upload Microphone Audio - - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - - - - WiFiPromptWidget - - Open - - - - Maximize your training data uploads to improve openpilot's driving models. - - - - <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> - - - - - WifiUI - - Scanning for networks... - Recherche de réseaux... - - - CONNECTING... - CONNEXION... - - - FORGET - OUBLIER - - - Forget Wi-Fi Network "%1"? - Oublier le réseau Wi-Fi "%1" ? - - - Forget - Oublier - - - diff --git a/selfdrive/ui/translations/ja.ts b/selfdrive/ui/translations/ja.ts deleted file mode 100644 index 4307cee91f..0000000000 --- a/selfdrive/ui/translations/ja.ts +++ /dev/null @@ -1,1069 +0,0 @@ - - - - - AbstractAlert - - Close - 閉じる - - - Reboot and Update - 再起動してアップデート - - - - AdvancedNetworking - - Back - 戻る - - - Enable Tethering - テザリング有効 - - - Tethering Password - テザリングパスワード - - - EDIT - 編集 - - - Enter new tethering password - 新しいテザリングパスワードを入力 - - - IP Address - IPアドレス - - - Enable Roaming - ローミング有効 - - - APN Setting - APN設定 - - - Enter APN - APNを入力 - - - leave blank for automatic configuration - 自動で設定するには空白のままにしてください - - - Cellular Metered - 従量制通信設定 - - - Hidden Network - ネットワーク非表示 - - - CONNECT - 接続 - - - Enter SSID - SSIDを入力 - - - Enter password - パスワードを入力 - - - for "%1" - [%1] - - - Prevent large data uploads when on a metered cellular connection - モバイルデータ回線を使用しているときは大容量データをアップロードしません - - - default - 標準設定 - - - metered - 従量制 - - - unmetered - 定額制 - - - Wi-Fi Network Metered - 従量制のWi-Fiネットワーク - - - Prevent large data uploads when on a metered Wi-Fi connection - 通信制限のあるWi-Fi接続では大容量データをアップロードしません - - - - ConfirmationDialog - - Ok - OK - - - Cancel - キャンセル - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - openpilotを使用するためには利用規約に同意する必要があります - - - Back - 戻る - - - Decline, uninstall %1 - 同意しない(%1をアンインストール) - - - - DeveloperPanel - - Joystick Debug Mode - ジョイスティックデバッグモード - - - Longitudinal Maneuver Mode - アクセル制御マニューバー - - - openpilot Longitudinal Control (Alpha) - openpilotアクセル制御(Alpha) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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に切り替えるにはこの設定を有効にしてください。また同時にExperimentalモードを推奨します。 - - - Enable ADB - ADBを有効にする - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB(Android Debug Bridge)により、USBまたはネットワーク経由でデバイスに接続できます。詳細は、https://docs.comma.ai/how-to/connect-to-comma を参照してください。 - - - - DevicePanel - - Dongle ID - ドングルID - - - N/A - 該当なし - - - Serial - シリアル番号 - - - Driver Camera - 車内カメラ - - - PREVIEW - プレビュー - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - 車内カメラでドライバー監視システムのカメラ画像を確認できます。(車両のパワーOFF時の機能です) - - - Reset Calibration - キャリブレーションリセット - - - RESET - リセット - - - Are you sure you want to reset calibration? - キャリブレーションをリセットしますか? - - - Review Training Guide - トレーニングガイドを見る - - - REVIEW - 確認 - - - Review the rules, features, and limitations of openpilot - openpilotのルール、機能、および制限を確認してください - - - Are you sure you want to review the training guide? - トレーニングガイドを始めてもよろしいですか? - - - Regulatory - 規約 - - - VIEW - 確認 - - - Change Language - 多言語対応 - - - CHANGE - 変更 - - - Select a language - 言語を選択 - - - Reboot - 再起動 - - - Power Off - パワーオフ - - - Your device is pointed %1° %2 and %3° %4. - このデバイスは%2 %1°、%4 %3°の向きに設置されています。 - - - down - - - - up - - - - left - - - - right - - - - Are you sure you want to reboot? - 再起動してもよろしいですか? - - - Disengage to Reboot - 再起動するには車を一旦停止してください - - - Are you sure you want to power off? - パワーオフしてもよろしいですか? - - - Disengage to Power Off - パワーオフするには車を一旦停止してください - - - Reset - リセット - - - Review - 見る - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - デバイスをcommaコネクト(connect.comma.ai)でペアリングしてcommaプライムの特典を受け取ってください。 - - - Pair Device - デバイスのペアリング - - - PAIR - OK - - - Disengage to Reset Calibration - キャリブレーションをリセットするには運転支援を解除して下さい。 - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - openpilotの本体は左右4°以内、上5°下9°以内の角度で取付ける必要があります。 - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - openpilot は継続的にキャリブレーションを行っており、リセットが必要になることはほとんどありません。キャリブレーションをリセットすると、車の電源が入っている場合はopenpilotが再起動します。 - - - - -Steering lag calibration is %1% complete. - - -ステアリング遅延のキャリブレーションが%1%完了。 - - - - -Steering lag calibration is complete. - - -ステアリング遅延のキャリブレーション完了。 - - - Steering torque response calibration is %1% complete. - ステアリングトルク応答のキャリブレーションが%1%完了。 - - - Steering torque response calibration is complete. - ステアリングトルク応答のキャリブレーション完了。 - - - - DriverViewWindow - - camera starting - カメラ起動中 - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - EXPERIMENTALモード - - - CHILL MODE ON - CHILLモード - - - - FirehosePanel - - 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は人間であるあなたの運転から学び、AI学習します。 - -Firehoseモードを有効にすると学習データを最大限アップロードし、openpilotの運転モデルを改善することができます。より多くのデータはより大きなモデルとなり、Experimentalモードの精度を向上させます。 - - - Firehose Mode: ACTIVE - Firehoseモード: 作動中 - - - 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>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モードは公衆無線LANや大容量契約のSIMカードに接続していれば、運転中でも動作します。<br><br><br><b>よくある質問(FAQ)</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>がこれまでのトレーニングデータに含まれています。 - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>動作停止</span>: 大容量のネットワークに接続してください - - - Firehose Mode - Firehoseモード - - - - HudRenderer - - km/h - km/h - - - mph - mph - - - MAX - 最大速度 - - - - InputDialog - - Cancel - キャンセル - - - Need at least %n character(s)! - - %n文字以上にして下さい! - - - - - MultiOptionDialog - - Select - 選択 - - - Cancel - キャンセル - - - - Networking - - Advanced - 詳細 - - - Enter password - パスワードを入力 - - - for "%1" - [%1] - - - Wrong password - パスワードが違います - - - - OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - インターネットへ接続してアップデートを確認してください。未接続のままではopenpilotを使用できなくなります。あと[%1] - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - インターネットに接続してアップデートを確認してください。接続するまでopenpilotは使用できません。 - - - Unable to download updates -%1 - 更新をダウンロードできませんでした -%1 - - - Taking camera snapshots. System won't start until finished. - スナップショットを撮影中です。完了するまでシステムは起動しません。 - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - オペレーティングシステムがバックグラウンドでダウンロードされています。インストールの準備が整うと更新を促されます。 - - - 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が認識されていない可能性があります。該当車両のファームウェアバージョンを追加するためにプルリクエストしてください。サポートが必要な場合は 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. - openpilotがデバイスの取り付け位置にずれを検出しました。デバイスの固定とマウントがフロントガラスにしっかりと取り付けられていることを確認してください。 - - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - デバイスの温度が高すぎるためシステム起動前の冷却中です。現在のデバイス内部温度: %1 - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - 製品のcomma.aiへの登録に失敗しました。このデバイスはcomma.aiのサーバーに接続したりアップロードを行ったりすることはできず、comma.aiからのサポートも受けられません。もしcomma.ai/shopで購入した場合は、https://comma.ai/support にてサポートチケットをご提出ください。 - - - Acknowledge Excessive Actuation - 過剰な作動の検知 - - - Snooze Update - また後で更新する - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - - - - - OffroadHome - - UPDATE - 更新 - - - ALERTS - 警告 - - - ALERT - 警告 - - - - OnroadAlerts - - openpilot Unavailable - openpilotは使用できません - - - TAKE CONTROL IMMEDIATELY - 直ちに車の運転に戻って下さい - - - Reboot Device - デバイスを再起動してください - - - Waiting to start - 始動を待機しています - - - System Unresponsive - システムが応答しません - - - - PairingPopup - - Pair your device to your comma account - デバイスとcommaアカウントを連携して下さい - - - Go to https://connect.comma.ai on your phone - スマートフォンで https://connect.comma.ai にアクセスしてください - - - Click "add new device" and scan the QR code on the right - 「add new device」を押して右側のQRコードをスキャンしてください - - - Bookmark connect.comma.ai to your home screen to use it like an app - connect.comma.aiのサイトをホーム画面に追加して、アプリのように使うことができます。 - - - Please connect to Wi-Fi to complete initial pairing - 最初にペアリングするため、Wi-Fiに接続してください - - - - ParamControl - - Cancel - キャンセル - - - Enable - 有効にする - - - - PrimeAdWidget - - Upgrade Now - 今すぐアップグレード - - - Become a comma prime member at connect.comma.ai - connect.comma.ai からプライム会員に登録できます - - - PRIME FEATURES: - 特典: - - - Remote access - リモートアクセス - - - 24/7 LTE connectivity - 24時間365日のLTE接続 - - - 1 year of drive storage - 1年間分のドライブストレージ - - - Remote snapshots - リモートスナップショット - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ 有効です - - - comma prime - commaプライム - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - %n分前 - - - - %n hour(s) ago - - %n時間前 - - - - %n day(s) ago - - %n日前 - - - - now - たった今 - - - - SettingsWindow - - × - × - - - Device - デバイス - - - Network - ネット - - - Toggles - 機能 - - - Software - ソフト - - - Developer - 開発 - - - Firehose - データ学習 - - - - SetupWidget - - Finish Setup - セットアップの完了 - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - デバイスをcommaコネクト(connect.comma.ai)でペアリングしてcommaプライムの特典を受け取ってください。 - - - Pair device - デバイスのペアリング - - - - Sidebar - - CONNECT - 接続 - - - OFFLINE - オフライン - - - ONLINE - オンライン - - - ERROR - エラー - - - TEMP - 温度 - - - HIGH - 高温 - - - GOOD - 最適 - - - OK - OK - - - VEHICLE - 車両 - - - NO - NO - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Updates are only downloaded while the car is off. - 車の電源がオフの間のみアップデートがダウンロードできます。 - - - Current Version - 現在のバージョン - - - Download - ダウンロード - - - Install Update - アップデート - - - INSTALL - インストール - - - Target Branch - 対象のブランチ - - - SELECT - 選択 - - - Select a branch - ブランチを選択 - - - UNINSTALL - 実行 - - - Uninstall %1 - %1をアンインストール - - - Are you sure you want to uninstall? - アンインストールしてもよろしいですか? - - - CHECK - 確認 - - - Uninstall - アンインストール - - - failed to check for update - アップデートの確認に失敗しました。 - - - up to date, last checked %1 - 最新の状態です。最終確認日時:%1 - - - DOWNLOAD - ダウンロード - - - update available - アップデートが利用可能です - - - never - 無効 - - - - SshControl - - SSH Keys - SSH 鍵 - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - 警告: これはGitHubの設定にあるすべての公開鍵への SSH アクセスを許可するものです。自分以外のGitHubユーザー名を入力しないでください。commaのスタッフがGitHubのユーザー名を追加するようお願いすることはありません。 - - - ADD - 追加 - - - Enter your GitHub username - GitHubのユーザー名を入力してください - - - LOADING - 読み込み中 - - - REMOVE - 削除 - - - Username '%1' has no keys on GitHub - ユーザー名“%1”は GitHub に公開鍵がありません - - - Request timed out - リクエストタイムアウト - - - Username '%1' doesn't exist on GitHub - ユーザー名”%1”は GitHub に存在しません - - - - SshToggle - - Enable SSH - SSHの有効化 - - - - TermsPage - - Decline - 拒否 - - - Agree - 同意 - - - Welcome to openpilot - 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. - openpilotを使用するには利用規約に同意する必要があります。続行する前に最新の規約を以下でご確認ください: <span style='color: #465BEA;'>https://comma.ai/terms</span> - - - - TogglesPanel - - Enable openpilot - openpilotを有効化 - - - Enable Lane Departure Warnings - 車線逸脱警報の有効化 - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - 時速31マイル(50km)以上のスピードで走行中、ウインカーを作動させずに検出したレーン上に車両が触れた場合、手動で車線内に戻るように警告を行います。 - - - Use Metric System - メートル法の使用 - - - Display speed in km/h instead of mph. - 速度は mph ではなく km/h で表示されます。 - - - Record and Upload Driver Camera - 車内カメラの録画とアップロード - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - 車内カメラの映像をアップロードし、ドライバー監視システムのアルゴリズムの向上に役立てます。 - - - Disengage on Accelerator Pedal - アクセルを踏むと運転サポートを中断 - - - When enabled, pressing the accelerator pedal will disengage openpilot. - この機能を有効化すると、openpilotを利用中にアクセルを踏むとopenpilotによる運転サポートを中断します。 - - - Experimental Mode - Experimentalモード - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilotは標準ではゆっくりとくつろげる運転を提供します。このExperimental(実験)モードを有効にすると、以下のアグレッシブな開発中の機能を利用する事ができます。 - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - openpilotにアクセルとブレーキを任せます。openpilotは赤信号や一時停止サインでの停止を含み、人間と同じように考えて運転を行います。openpilotが運転速度を決定するため、あなたが設定する速度は上限速度になります。この機能は実験段階のため、openpilotの運転ミスに常に備えて注意してください。 - - - New Driving Visualization - 新しい運転画面 - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - 車両の標準ACC(アダプティブ・クルーズ・コントロール)がアクセル制御に使用されているため、現在Experimentalモードは利用できません。 - - - Aggressive - アグレッシブ - - - Standard - 標準 - - - Relaxed - リラックス - - - Driving Personality - 運転傾向 - - - End-to-End Longitudinal Control - End-to-Endアクセル制御 - - - openpilot longitudinal control may come in a future update. - openpilotのアクセル制御は将来のアップデートで利用できる可能性があります。 - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - openpilotのアルファ版アクセル制御は、Experimentalモードと共に非リリースのブランチでテストすることができます。 - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - openpilotのアクセル制御機能(アルファ)を有効にして、Experimentalモードを許可してください。 - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - 標準モードが推奨されます。アグレッシブモードではopenpilotは先行車に近づいて追従し、アクセルとブレーキがより強気になります。リラックスモードではopenpilotは先行車から距離を取って走行します。サポートされている車両ではステアリングホイールの距離ボタンでこれらのモードを切り替えることができます。 - - - 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. - 運転時の画面効果として、低速時にカーブをより良く表示するために道路用の広角カメラに切り替わります。またExperimentalモードのロゴが右上隅に表示されます。 - - - Always-On Driver Monitoring - 運転者の常時モニタリング - - - Enable driver monitoring even when openpilot is not engaged. - openpilotが作動していない場合でも運転者モニタリングを有効にする。 - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - openpilotシステムを使用してアダプティブクルーズコントロールおよび車線維持支援を行います。システム使用中は常にドライバーが事故を起こさないように注意を払ってください。 - - - Changing this setting will restart openpilot if the car is powered on. - この設定を変更すると車の電源が入っている場合はopenpilotが再起動します。 - - - Record and Upload Microphone Audio - マイク音声の録音とアップロード - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - 運転中にマイク音声を録音・保存します。音声は comma connect のドライブレコーダー映像に含まれます。 - - - - WiFiPromptWidget - - Open - 開く - - - 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> Firehoseモード <span style='font-family: Noto Color Emoji;'>🔥</span> - - - - WifiUI - - Scanning for networks... - ネットワークをスキャン中... - - - CONNECTING... - 接続中... - - - FORGET - 削除 - - - Forget Wi-Fi Network "%1"? - Wi-Fiネットワーク%1を削除してもよろしいですか? - - - Forget - 削除 - - - diff --git a/selfdrive/ui/translations/ko.ts b/selfdrive/ui/translations/ko.ts deleted file mode 100644 index 9ab43dd9b8..0000000000 --- a/selfdrive/ui/translations/ko.ts +++ /dev/null @@ -1,1069 +0,0 @@ - - - - - AbstractAlert - - Close - 닫기 - - - Reboot and Update - 업데이트 및 재부팅 - - - - AdvancedNetworking - - Back - 뒤로 - - - Enable Tethering - 테더링 사용 - - - Tethering Password - 테더링 비밀번호 - - - EDIT - 편집 - - - Enter new tethering password - 새 테더링 비밀번호를 입력하세요 - - - IP Address - IP 주소 - - - Enable Roaming - 로밍 사용 - - - APN Setting - APN 설정 - - - Enter APN - APN 입력 - - - leave blank for automatic configuration - 자동 설정하려면 빈 칸으로 두세요 - - - Cellular Metered - 모바일 데이터 종량제 - - - Hidden Network - 숨겨진 네트워크 - - - CONNECT - 연결됨 - - - Enter SSID - SSID 입력 - - - Enter password - 비밀번호를 입력하세요 - - - for "%1" - "%1"에 접속하려면 비밀번호가 필요합니다 - - - Prevent large data uploads when on a metered cellular connection - 모바일 데이터 종량제 사용 시 대용량 데이터 업로드 방지 - - - default - 기본 - - - metered - 종량제 - - - unmetered - 무제한 - - - Wi-Fi Network Metered - 제한된 Wi-Fi 네트워크 - - - Prevent large data uploads when on a metered Wi-Fi connection - 제한된 Wi-Fi 사용 시 대용량 데이터 업로드 방지 - - - - ConfirmationDialog - - Ok - 확인 - - - Cancel - 취소 - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - 오픈파일럿을 사용하려면 이용약관에 동의해야 합니다. - - - Back - 뒤로 - - - Decline, uninstall %1 - 거절, %1 제거 - - - - DeveloperPanel - - Joystick Debug Mode - 조이스틱 디버그 모드 - - - Longitudinal Maneuver Mode - 가감속 제어 조작 모드 - - - openpilot Longitudinal Control (Alpha) - 오픈파일럿 가감속 제어 (알파) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - 경고: 오픈파일럿 가감속 제어는 알파 기능으로 차량의 자동긴급제동(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. - 이 차량에서 오픈파일럿은 오픈파일럿 가감속 제어 대신 기본적으로 차량의 ACC로 가감속을 제어합니다. 오픈파일럿 가감속 제어로 전환하려면 이 기능을 활성화하세요. 오픈파일럿 가감속 제어 알파 기능을 활성화하는 경우 실험 모드 활성화를 권장합니다. - - - Enable ADB - ADB 사용 - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB (안드로이드 디버그 브릿지) USB 또는 네트워크를 통해 장치에 연결할 수 있습니다. 자세한 내용은 https://docs.comma.ai/how-to/connect-to-comma를 참조하세요. - - - - DevicePanel - - Dongle ID - 동글 ID - - - N/A - N/A - - - Serial - 시리얼 - - - Driver Camera - 운전자 카메라 - - - PREVIEW - 미리보기 - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - 운전자 모니터링이 잘 되는지 확인하기 위해 후면 카메라를 미리 봅니다. (차량 시동이 꺼져 있어야 합니다) - - - Reset Calibration - 캘리브레이션 - - - RESET - 초기화 - - - Are you sure you want to reset calibration? - 캘리브레이션을 초기화하시겠습니까? - - - Review Training Guide - 트레이닝 가이드 - - - REVIEW - 다시보기 - - - Review the rules, features, and limitations of openpilot - 오픈파일럿의 규칙, 기능 및 제한 다시 확인 - - - Are you sure you want to review the training guide? - 트레이닝 가이드를 다시 확인하시겠습니까? - - - Regulatory - 규제 - - - VIEW - 보기 - - - Change Language - 언어 변경 - - - CHANGE - 변경 - - - Select a language - 언어를 선택하세요 - - - Reboot - 재부팅 - - - Power Off - 전원 끄기 - - - Your device is pointed %1° %2 and %3° %4. - 사용자의 장치는 %2 %1° 및 %4 %3° 의 방향으로 장착되어 있습니다. - - - down - 아래로 - - - up - 위로 - - - left - 좌측으로 - - - right - 우측으로 - - - Are you sure you want to reboot? - 재부팅하시겠습니까? - - - Disengage to Reboot - 재부팅하려면 연결을 해제하세요 - - - Are you sure you want to power off? - 전원을 끄시겠습니까? - - - Disengage to Power Off - 전원을 끄려면 연결을 해제하세요 - - - Reset - 초기화 - - - Review - 다시보기 - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - 장치를 comma connect (connect.comma.ai)에서 동기화하고 comma prime 무료 이용권을 사용하세요. - - - Pair Device - 장치 동기화 - - - PAIR - 동기화 - - - Disengage to Reset Calibration - 캘리브레이션을 재설정하려면 해제하세요 - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - 오픈파일럿 장치는 좌우측으로는 4° 또는 위로는 5° 아래로는 9° 이내에 장착해야합니다. - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - 오픈파일럿은 지속적으로 갤리브레이션되어 재설정이 거의 필요하지 않습니다. 차량과 연결된 경우 캘리브레이션 재설정이 오픈파일럿을 재시작합니다. - - - - -Steering lag calibration is %1% complete. - - -조향 지연 캘리브레이션이 %1% 진행되었습니다. - - - - -Steering lag calibration is complete. - - -조향 지연 캘리브레이션이 완료되었습니다. - - - Steering torque response calibration is %1% complete. - 조향 토크 응답 캘리브레이션이 %1% 진행되었습니다. - - - Steering torque response calibration is complete. - 조향 토크 응답 캘리브레이션이 완료되었습니다. - - - - DriverViewWindow - - camera starting - 카메라 시작 중 - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - 실험 모드 사용 - - - CHILL MODE ON - 안정 모드 사용 - - - - FirehosePanel - - 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 - 파이어호스 모드: 활성화 - - - 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>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>파이어호스 모드는 핫스팟 또는 무제한 네트워크에 연결된 경우 주행 중에도 작동할 수 있습니다.<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> 예, 오직 공식 오픈파일럿의 특정 포크만 트레이닝에 사용할 수 있습니다. - - - <b>%n segment(s)</b> of your driving is in the training dataset so far. - - <b>%n 구간</b> 의 운전이 지금까지의 학습 데이터셋에 포함되어 있습니다. - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>비활성 상태</span>: 무제한 네트워크에 연결 하세요 - - - Firehose Mode - 파이어호스 모드 - - - - HudRenderer - - km/h - km/h - - - mph - mph - - - MAX - MAX - - - - InputDialog - - Cancel - 취소 - - - Need at least %n character(s)! - - 최소 %n자 이상이어야 합니다! - - - - - MultiOptionDialog - - Select - 선택 - - - Cancel - 취소 - - - - Networking - - Advanced - 고급 설정 - - - Enter password - 비밀번호를 입력하세요 - - - for "%1" - "%1"에 접속하려면 비밀번호가 필요합니다 - - - Wrong password - 비밀번호가 틀렸습니다 - - - - OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - 즉시 인터넷에 연결하여 업데이트를 확인하세요. 인터넷에 연결되어 있지 않으면 %1 이후에는 오픈파일럿이 활성화되지 않습니다. - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - 업데이트 확인을 위해 인터넷 연결이 필요합니다. 오픈파일럿은 업데이트 확인을 위해 인터넷에 연결될 때까지 자동으로 시작되지 않습니다. - - - Unable to download updates -%1 - 업데이트를 다운로드할 수 없습니다 -%1 - - - Taking camera snapshots. System won't start until finished. - 카메라 스냅샷 찍기가 완료될 때까지 시스템이 시작되지 않습니다. - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - 백그라운드에서 운영 체제에 대한 업데이트가 다운로드되고 있습니다. 설치가 준비되면 업데이트 메시지가 표시됩니다. - - - 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. - 오픈파일럿이 차량을 식별할 수 없습니다. 지원되지 않는 차량이거나 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. - 오픈파일럿 장치의 장착 위치가 변경되었습니다. 장치가 마운트에 완전히 장착되고 마운트가 앞유리에 단단히 고정되었는지 확인하세요. - - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - 장치 온도가 너무 높습니다. 시작하기 전에 시스템을 냉각하고 있습니다. 현재 내부 구성 요소 온도: %1 - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - 장치를 comma.ai 백엔드에 등록하지 못했습니다. comma.ai 서버에 연결하거나 업로드하지 않으며 comma.ai로부터 지원을받지 않습니다. comma.ai shop에서 구매 한 장치 인 경우 https://comma.ai/support에서 티켓을 여십시오. - - - Acknowledge Excessive Actuation - 과도한 작동을 인정하십시오 - - - Snooze Update - 업데이트 일시 중지 - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - - - - - OffroadHome - - UPDATE - 업데이트 - - - ALERTS - 알림 - - - ALERT - 알림 - - - - OnroadAlerts - - openpilot Unavailable - 오픈파일럿을 사용할수없습니다 - - - TAKE CONTROL IMMEDIATELY - 핸들을 잡아주세요 - - - Reboot Device - 장치를 재부팅하세요 - - - Waiting to start - 시작을 기다리는중 - - - System Unresponsive - 시스템이 응답하지않습니다 - - - - PairingPopup - - Pair your device to your comma account - 장치를 comma 계정에 동기화합니다 - - - Go to https://connect.comma.ai on your phone - https://connect.comma.ai에 접속하세요 - - - Click "add new device" and scan the QR code on the right - "새 장치 추가"를 클릭하고 오른쪽 QR 코드를 스캔하세요 - - - Bookmark connect.comma.ai to your home screen to use it like an app - connect.comma.ai를 앱처럼 사용하려면 홈 화면에 바로가기를 만드세요 - - - Please connect to Wi-Fi to complete initial pairing - 초기 동기화를 완료하려면 Wi-Fi에 연결하세요. - - - - ParamControl - - Cancel - 취소 - - - Enable - 활성화 - - - - PrimeAdWidget - - Upgrade Now - 지금 업그레이드하세요 - - - Become a comma prime member at connect.comma.ai - connect.comma.ai에서 comma prime 사용자로 등록하세요 - - - PRIME FEATURES: - PRIME 기능: - - - Remote access - 원격 접속 - - - 24/7 LTE connectivity - 항상 LTE 연결 - - - 1 year of drive storage - 1년간 주행 로그 저장 - - - Remote snapshots - 원격 스냅샷 - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ 구독함 - - - comma prime - comma prime - - - - QObject - - openpilot - 오픈파일럿 - - - %n minute(s) ago - - %n 분 전 - - - - %n hour(s) ago - - %n 시간 전 - - - - %n day(s) ago - - %n 일 전 - - - - now - now - - - - SettingsWindow - - × - × - - - Device - 장치 - - - Network - 네트워크 - - - Toggles - 토글 - - - Software - 소프트웨어 - - - Developer - 개발자 - - - Firehose - 파이어호스 - - - - SetupWidget - - Finish Setup - 설정 완료 - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - 장치를 comma connect (connect.comma.ai)에서 동기화하고 comma prime 무료 이용권을 사용하세요. - - - Pair device - 장치 동기화 - - - - Sidebar - - CONNECT - 커넥트 - - - OFFLINE - 연결 안됨 - - - ONLINE - 온라인 - - - ERROR - 오류 - - - TEMP - 온도 - - - HIGH - 높음 - - - GOOD - 좋음 - - - OK - OK - - - VEHICLE - 차량 - - - NO - NO - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - LAN - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Updates are only downloaded while the car is off. - 업데이트는 차량 시동이 꺼졌을 때 다운로드됩니다. - - - Current Version - 현재 버전 - - - Download - 다운로드 - - - Install Update - 업데이트 설치 - - - INSTALL - 설치 - - - Target Branch - 대상 브랜치 - - - SELECT - 선택 - - - Select a branch - 브랜치 선택 - - - UNINSTALL - 제거 - - - Uninstall %1 - %1 제거 - - - Are you sure you want to uninstall? - 제거하시겠습니까? - - - CHECK - 확인 - - - Uninstall - 제거 - - - failed to check for update - 업데이트를 확인하지 못했습니다 - - - up to date, last checked %1 - 최신 버전입니다. 마지막 확인: %1 - - - DOWNLOAD - 다운로드 - - - update available - 업데이트 가능 - - - never - 업데이트 안함 - - - - SshControl - - SSH Keys - SSH 키 - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - 경고: 이 설정은 GitHub에 등록된 모든 공용 키에 대해 SSH 액세스 권한을 부여합니다. 본인의 GitHub 사용자 아이디 이외에는 입력하지 마십시오. comma에서는 GitHub 아이디를 추가하라는 요청을 하지 않습니다. - - - ADD - 추가 - - - Enter your GitHub username - GitHub 사용자 ID - - - LOADING - 로딩 중 - - - REMOVE - 삭제 - - - Username '%1' has no keys on GitHub - 사용자 '%1'의 GitHub에 키가 등록되어 있지 않습니다 - - - Request timed out - 요청 시간 초과 - - - Username '%1' doesn't exist on GitHub - GitHub 사용자 '%1'를 찾지 못했습니다 - - - - SshToggle - - Enable SSH - SSH 사용 - - - - TermsPage - - Decline - 거절 - - - Agree - 동의 - - - 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> 에서 최신 약관을 읽은 후 계속하세요. - - - - TogglesPanel - - Enable openpilot - 오픈파일럿 사용 - - - Enable Lane Departure Warnings - 차선 이탈 경고 활성화 - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - 차량이 50km/h(31mph) 이상의 속도로 주행할 때 방향지시등이 켜지지 않은 상태에서 차선을 벗어나면 경고합니다. - - - Use Metric System - 미터법 사용 - - - Display speed in km/h instead of mph. - mph 대신 km/h로 속도를 표시합니다. - - - Record and Upload Driver Camera - 운전자 카메라 녹화 및 업로드 - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - 운전자 카메라의 영상 데이터를 업로드하여 운전자 모니터링 알고리즘을 개선합니다. - - - Disengage on Accelerator Pedal - 가속페달 조작 시 해제 - - - When enabled, pressing the accelerator pedal will disengage openpilot. - 활성화된 경우 가속 페달을 밟으면 오픈파일럿이 해제됩니다. - - - Experimental Mode - 실험 모드 - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - 오픈파일럿은 기본적으로 <b>안정 모드</b>로 주행합니다. 실험 모드는 안정화되지 않은 <b>알파 수준의 기능</b>을 활성화합니다. 실험 모드의 기능은 아래와 같습니다: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - 주행모델이 가감속을 제어하도록 합니다. 오픈파일럿은 빨간불과 정지신호를 보고 정지하는것을 포함하여 사람이 운전하는 방식대로 작동하며 주행 모델이 속도를 결정하므로 설정 속도는 최대 제한 속도로만 작동합니다. 이는 알파 수준의 기능이며 오류가 발생할수있으니 사용에 주의해야 합니다. - - - New Driving Visualization - 새로운 주행 시각화 - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - 차량의 ACC가 가감속 제어에 사용되기 때문에, 이 차량에서는 실험 모드를 사용할 수 없습니다. - - - openpilot longitudinal control may come in a future update. - 오픈파일럿 가감속 제어는 향후 업데이트에서 지원될 수 있습니다. - - - Aggressive - 공격적 - - - Standard - 표준 - - - Relaxed - 편안한 - - - Driving Personality - 주행 모드 - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - 오픈파일럿 가감속 제어 알파 버전은 비 릴리즈 브랜치에서 실험 모드와 함께 테스트할 수 있습니다. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - 실험 모드를 사용하려면 오픈파일럿 E2E 가감속 제어 (알파) 토글을 활성화하세요. - - - End-to-End Longitudinal Control - E2E 가감속 제어 - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - 표준 모드를 권장합니다. 공격적 모드의 오픈파일럿은 선두 차량을 더 가까이 따라가고 가감속제어를 사용하여 더욱 공격적으로 움직입니다. 편안한 모드의 오픈파일럿은 선두 차량으로부터 더 멀리 떨어져 있습니다. 지원되는 차량에서는 차간거리 버튼을 사용하여 이러한 특성을 순환할 수 있습니다. - - - 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 - 상시 운전자 모니터링 - - - Enable driver monitoring even when openpilot is not engaged. - 오픈파일럿이 활성화되지 않은 경우에도 드라이버 모니터링을 활성화합니다. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - ACC 및 차선 유지 지원을 위해 오픈파일럿 시스템을 사용하십시오. 이 기능을 사용하려면 항상주의를 기울여야합니다. - - - Changing this setting will restart openpilot if the car is powered on. - 이 설정을 변경하면 차량이 재가동된후 오픈파일럿이 시작됩니다. - - - Record and Upload Microphone Audio - 마이크 오디오 녹음 및 업로드 - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - 운전 중에 마이크 오디오를 녹음하고 저장하십시오. 오디오는 comma connect의 대시캠 비디오에 포함됩니다. - - - - WiFiPromptWidget - - Open - 열기 - - - Maximize your training data uploads to improve openpilot's driving models. - 오픈파일럿의 주행 모델 개선을 위해 학습 데이터 업로드를 최대화하세요. - - - <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> - - - - WifiUI - - Scanning for networks... - 네트워크 검색 중... - - - CONNECTING... - 연결 중... - - - FORGET - 삭제 - - - Forget Wi-Fi Network "%1"? - Wi-Fi "%1"에 자동으로 연결하지 않겠습니까? - - - Forget - 삭제 - - - diff --git a/selfdrive/ui/translations/nl.ts b/selfdrive/ui/translations/nl.ts deleted file mode 100644 index 10651a4160..0000000000 --- a/selfdrive/ui/translations/nl.ts +++ /dev/null @@ -1,1120 +0,0 @@ - - - - - AbstractAlert - - Close - Sluit - - - Snooze Update - Update uitstellen - - - Reboot and Update - Opnieuw Opstarten en Updaten - - - - AdvancedNetworking - - Back - Terug - - - Enable Tethering - Tethering Inschakelen - - - Tethering Password - Tethering Wachtwoord - - - EDIT - AANPASSEN - - - Enter new tethering password - Voer nieuw tethering wachtwoord in - - - IP Address - IP Adres - - - Enable Roaming - Roaming Inschakelen - - - APN Setting - APN Instelling - - - Enter APN - Voer APN in - - - leave blank for automatic configuration - laat leeg voor automatische configuratie - - - Cellular Metered - - - - Prevent large data uploads when on a metered connection - - - - - AnnotatedCameraWidget - - km/h - km/u - - - mph - mph - - - MAX - MAX - - - SPEED - SPEED - - - LIMIT - LIMIT - - - - ConfirmationDialog - - Ok - Ok - - - Cancel - Annuleren - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - U moet de Algemene Voorwaarden accepteren om openpilot te gebruiken. - - - Back - Terug - - - Decline, uninstall %1 - Afwijzen, verwijder %1 - - - - DevicePanel - - Dongle ID - Dongle ID - - - N/A - Nvt - - - Serial - Serienummer - - - Driver Camera - Bestuurders Camera - - - PREVIEW - BEKIJKEN - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - Bekijk de naar de bestuurder gerichte camera om ervoor te zorgen dat het monitoren van de bestuurder goed zicht heeft. (Voertuig moet uitgschakeld zijn) - - - Reset Calibration - Kalibratie Resetten - - - RESET - RESET - - - Are you sure you want to reset calibration? - Weet u zeker dat u de kalibratie wilt resetten? - - - Review Training Guide - Doorloop de Training Opnieuw - - - REVIEW - BEKIJKEN - - - Review the rules, features, and limitations of openpilot - Bekijk de regels, functies en beperkingen van openpilot - - - Are you sure you want to review the training guide? - Weet u zeker dat u de training opnieuw wilt doorlopen? - - - Regulatory - Regelgeving - - - VIEW - BEKIJKEN - - - Change Language - Taal Wijzigen - - - CHANGE - WIJZIGEN - - - Select a language - Selecteer een taal - - - Reboot - Opnieuw Opstarten - - - Power Off - Uitschakelen - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot vereist dat het apparaat binnen 4° links of rechts en binnen 5° omhoog of 8° omlaag wordt gemonteerd. openpilot kalibreert continu, resetten is zelden nodig. - - - Your device is pointed %1° %2 and %3° %4. - Uw apparaat is gericht op %1° %2 en %3° %4. - - - down - omlaag - - - up - omhoog - - - left - links - - - right - rechts - - - Are you sure you want to reboot? - Weet u zeker dat u opnieuw wilt opstarten? - - - Disengage to Reboot - Deactiveer openpilot om opnieuw op te starten - - - Are you sure you want to power off? - Weet u zeker dat u wilt uitschakelen? - - - Disengage to Power Off - Deactiveer openpilot om uit te schakelen - - - - DriveStats - - Drives - Ritten - - - Hours - Uren - - - ALL TIME - TOTAAL - - - PAST WEEK - AFGELOPEN WEEK - - - KM - Km - - - Miles - Mijl - - - - DriverViewScene - - camera starting - Camera wordt gestart - - - - InputDialog - - Cancel - Annuleren - - - Need at least %n character(s)! - - Heeft minstens %n karakter nodig! - Heeft minstens %n karakters nodig! - - - - - Installer - - Installing... - Installeren... - - - Receiving objects: - Objecten ontvangen: - - - Resolving deltas: - Deltas verwerken: - - - Updating files: - Bestanden bijwerken: - - - - MapETA - - eta - eta - - - min - min - - - hr - uur - - - km - km - - - mi - mi - - - - MapInstructions - - km - km - - - m - m - - - mi - mi - - - ft - ft - - - - MapPanel - - Current Destination - Huidige Bestemming - - - CLEAR - LEEGMAKEN - - - Recent Destinations - Recente Bestemmingen - - - Try the Navigation Beta - Probeer de Navigatie Bèta - - - Get turn-by-turn directions displayed and more with a comma -prime subscription. Sign up now: https://connect.comma.ai - Krijg stapsgewijze routebeschrijving en meer met een comma -prime abonnement. Meld u nu aan: https://connect.comma.ai - - - No home -location set - Geen thuislocatie -ingesteld - - - No work -location set - Geen werklocatie -ingesteld - - - no recent destinations - geen recente bestemmingen - - - - MapWindow - - Map Loading - Kaart wordt geladen - - - Waiting for GPS - Wachten op GPS - - - - MultiOptionDialog - - Select - Selecteer - - - Cancel - Annuleren - - - - Networking - - Advanced - Geavanceerd - - - Enter password - Voer wachtwoord in - - - for "%1" - voor "%1" - - - Wrong password - Verkeerd wachtwoord - - - - OffroadHome - - UPDATE - UPDATE - - - ALERTS - WAARSCHUWINGEN - - - ALERT - WAARSCHUWING - - - - PairingPopup - - Pair your device to your comma account - Koppel uw apparaat aan uw comma-account - - - Go to https://connect.comma.ai on your phone - Ga naar https://connect.comma.ai op uw telefoon - - - Click "add new device" and scan the QR code on the right - Klik op "add new device" en scan de QR-code aan de rechterkant - - - Bookmark connect.comma.ai to your home screen to use it like an app - Voeg connect.comma.ai toe op uw startscherm om het als een app te gebruiken - - - - PrimeAdWidget - - Upgrade Now - Upgrade nu - - - Become a comma prime member at connect.comma.ai - Word een comma prime lid op connect.comma.ai - - - PRIME FEATURES: - PRIME BEVAT: - - - Remote access - Toegang op afstand - - - 1 year of storage - 1 jaar lang opslag - - - Developer perks - Voordelen voor ontwikkelaars - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ GEABONNEERD - - - comma prime - comma prime - - - CONNECT.COMMA.AI - CONNECT.COMMA.AI - - - COMMA POINTS - COMMA PUNTEN - - - - QObject - - Reboot - Opnieuw Opstarten - - - Exit - Afsluiten - - - dashcam - dashcam - - - openpilot - openpilot - - - %n minute(s) ago - - %n minuut geleden - %n minuten geleden - - - - %n hour(s) ago - - %n uur geleden - %n uur geleden - - - - %n day(s) ago - - %n dag geleden - %n dagen geleden - - - - - Reset - - Reset failed. Reboot to try again. - Opnieuw instellen mislukt. Start opnieuw op om opnieuw te proberen. - - - Are you sure you want to reset your device? - Weet u zeker dat u uw apparaat opnieuw wilt instellen? - - - Resetting device... - Apparaat opnieuw instellen... - - - System Reset - Systeemreset - - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - Systeemreset geactiveerd. Druk op bevestigen om alle inhoud en instellingen te wissen. Druk op Annuleren om het opstarten te hervatten. - - - Cancel - Annuleren - - - Reboot - Opnieuw Opstarten - - - Confirm - Bevestigen - - - Unable to mount data partition. Press confirm to reset your device. - Kan gegevenspartitie niet koppelen. Druk op bevestigen om uw apparaat te resetten. - - - - RichTextDialog - - Ok - Ok - - - - SettingsWindow - - × - × - - - Device - Apparaat - - - Network - Netwerk - - - Toggles - Opties - - - Software - Software - - - Navigation - Navigatie - - - - Setup - - WARNING: Low Voltage - WAARCHUWING: Lage Spanning - - - Power your device in a car with a harness or proceed at your own risk. - Voorzie uw apparaat van stroom in een auto met een harnas (car harness) of ga op eigen risico verder. - - - Power off - Uitschakelen - - - Continue - Doorgaan - - - Getting Started - Aan de slag - - - Before we get on the road, let’s finish installation and cover some details. - Laten we, voordat we op pad gaan, de installatie afronden en enkele details bespreken. - - - Connect to Wi-Fi - Maak verbinding met Wi-Fi - - - Back - Terug - - - Continue without Wi-Fi - Doorgaan zonder Wi-Fi - - - Waiting for internet - Wachten op internet - - - Choose Software to Install - Kies Software om te Installeren - - - Dashcam - Dashcam - - - Custom Software - Andere Software - - - Enter URL - Voer URL in - - - for Custom Software - voor Andere Software - - - Downloading... - Downloaden... - - - Download Failed - Downloaden Mislukt - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Zorg ervoor dat de ingevoerde URL geldig is en dat de internetverbinding van het apparaat goed is. - - - Reboot device - Apparaat opnieuw opstarten - - - Start over - Begin opnieuw - - - - SetupWidget - - Finish Setup - Installatie voltooien - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Koppel uw apparaat met comma connect (connect.comma.ai) en claim uw comma prime-aanbieding. - - - Pair device - Apparaat koppelen - - - - Sidebar - - CONNECT - VERBINDING - - - OFFLINE - OFFLINE - - - ONLINE - ONLINE - - - ERROR - FOUT - - - TEMP - TEMP - - - HIGH - HOOG - - - GOOD - GOED - - - OK - OK - - - VEHICLE - VOERTUIG - - - NO - GEEN - - - PANDA - PANDA - - - GPS - GPS - - - SEARCH - ZOEKEN - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - 4G - - - 5G - 5G - - - - SoftwarePanel - - Git Branch - Git Branch - - - Git Commit - Git Commit - - - OS Version - OS Versie - - - Version - Versie - - - Last Update Check - Laatste Updatecontrole - - - The last time openpilot successfully checked for an update. The updater only runs while the car is off. - De laatste keer dat openpilot met succes heeft gecontroleerd op een update. De updater werkt alleen als de auto is uitgeschakeld. - - - Check for Update - Controleer op Updates - - - CHECKING - CONTROLEER - - - Switch Branch - Branch Verwisselen - - - ENTER - INVOEREN - - - The new branch will be pulled the next time the updater runs. - Tijdens de volgende update wordt de nieuwe branch opgehaald. - - - Enter branch name - Voer branch naam in - - - Uninstall %1 - Verwijder %1 - - - UNINSTALL - VERWIJDER - - - Are you sure you want to uninstall? - Weet u zeker dat u de installatie ongedaan wilt maken? - - - failed to fetch update - ophalen van update mislukt - - - CHECK - CONTROLEER - - - Updates are only downloaded while the car is off. - - - - Current Version - - - - Download - - - - Install Update - - - - INSTALL - - - - Target Branch - - - - SELECT - - - - Select a branch - - - - - SshControl - - SSH Keys - SSH Sleutels - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - Waarschuwing: dit geeft SSH toegang tot alle openbare sleutels in uw GitHub-instellingen. Voer nooit een andere GitHub-gebruikersnaam in dan die van uzelf. Een medewerker van comma zal u NOOIT vragen om zijn GitHub-gebruikersnaam toe te voegen. - - - ADD - TOEVOEGEN - - - Enter your GitHub username - Voer uw GitHub gebruikersnaam in - - - LOADING - LADEN - - - REMOVE - VERWIJDEREN - - - Username '%1' has no keys on GitHub - Gebruikersnaam '%1' heeft geen SSH sleutels op GitHub - - - Request timed out - Time-out van aanvraag - - - Username '%1' doesn't exist on GitHub - Gebruikersnaam '%1' bestaat niet op GitHub - - - - SshToggle - - Enable SSH - SSH Inschakelen - - - - TermsPage - - Terms & Conditions - Algemene Voorwaarden - - - Decline - Afwijzen - - - Scroll to accept - Scroll om te accepteren - - - Agree - Akkoord - - - - TogglesPanel - - Enable openpilot - openpilot Inschakelen - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Gebruik het openpilot-systeem voor adaptieve cruisecontrol en rijstrookassistentie. Uw aandacht is te allen tijde vereist om deze functie te gebruiken. Het wijzigen van deze instelling wordt van kracht wanneer de auto wordt uitgeschakeld. - - - Enable Lane Departure Warnings - Waarschuwingen bij Verlaten Rijstrook Inschakelen - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - Ontvang waarschuwingen om terug naar de rijstrook te sturen wanneer uw voertuig over een gedetecteerde rijstrookstreep drijft zonder dat de richtingaanwijzer wordt geactiveerd terwijl u harder rijdt dan 50 km/u (31 mph). - - - Use Metric System - Gebruik Metrisch Systeem - - - Display speed in km/h instead of mph. - Geef snelheid weer in km/u in plaats van mph. - - - Record and Upload Driver Camera - Opnemen en Uploaden van de Bestuurders Camera - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - Upload gegevens van de bestuurders camera en help het algoritme voor het monitoren van de bestuurder te verbeteren. - - - Disengage on Accelerator Pedal - Deactiveren Met Gaspedaal - - - When enabled, pressing the accelerator pedal will disengage openpilot. - Indien ingeschakeld, zal het indrukken van het gaspedaal openpilot deactiveren. - - - Show ETA in 24h Format - Toon verwachte aankomsttijd in 24-uurs formaat - - - Use 24h format instead of am/pm - Gebruik 24-uurs formaat in plaats van AM en PM - - - Show Map on Left Side of UI - Toon kaart aan linkerkant van het scherm - - - Show map on left side when in split screen view. - Toon kaart links in gesplitste schermweergave. - - - openpilot Longitudinal Control - openpilot Longitudinale Controle - - - openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB! - openpilot zal de radar van de auto uitschakelen en de controle over gas en remmen overnemen. Waarschuwing: hierdoor wordt AEB (automatische noodrem) uitgeschakeld! - - - 🌮 End-to-end longitudinal (extremely alpha) 🌮 - - - - Experimental openpilot Longitudinal Control - - - - <b>WARNING: openpilot longitudinal control is experimental for this car and will disable AEB.</b> - - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would. Super experimental. - - - - openpilot longitudinal control is not currently available for this car. - - - - Enable experimental longitudinal control to enable this. - - - - - Updater - - Update Required - Update Vereist - - - An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. - Een update van het besturingssysteem is vereist. Verbind je apparaat met Wi-Fi voor de snelste update-ervaring. De downloadgrootte is ongeveer 1 GB. - - - Connect to Wi-Fi - Maak verbinding met Wi-Fi - - - Install - Installeer - - - Back - Terug - - - Loading... - Aan het laden... - - - Reboot - Opnieuw Opstarten - - - Update failed - Update mislukt - - - - WifiUI - - Scanning for networks... - Scannen naar netwerken... - - - CONNECTING... - VERBINDEN... - - - FORGET - VERGETEN - - - Forget Wi-Fi Network "%1"? - Vergeet Wi-Fi Netwerk "%1"? - - - diff --git a/selfdrive/ui/translations/pl.ts b/selfdrive/ui/translations/pl.ts deleted file mode 100644 index 4f8b03ef50..0000000000 --- a/selfdrive/ui/translations/pl.ts +++ /dev/null @@ -1,1124 +0,0 @@ - - - - - AbstractAlert - - Close - Zamknij - - - Snooze Update - Zaktualizuj później - - - Reboot and Update - Uruchom ponownie i zaktualizuj - - - - AdvancedNetworking - - Back - Wróć - - - Enable Tethering - Włącz hotspot osobisty - - - Tethering Password - Hasło do hotspotu - - - EDIT - EDYTUJ - - - Enter new tethering password - Wprowadź nowe hasło do hotspotu - - - IP Address - Adres IP - - - Enable Roaming - Włącz roaming danych - - - APN Setting - Ustawienia APN - - - Enter APN - Wprowadź APN - - - leave blank for automatic configuration - Pozostaw puste, aby użyć domyślnej konfiguracji - - - Cellular Metered - - - - Prevent large data uploads when on a metered connection - - - - - AnnotatedCameraWidget - - km/h - km/h - - - mph - mph - - - MAX - MAX - - - SPEED - PRĘDKOŚĆ - - - LIMIT - OGRANICZENIE - - - - ConfirmationDialog - - Ok - Ok - - - Cancel - Anuluj - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Aby korzystać z openpilota musisz zaakceptować regulamin. - - - Back - Wróć - - - Decline, uninstall %1 - Odrzuć, odinstaluj %1 - - - - DevicePanel - - Dongle ID - ID adaptera - - - N/A - N/A - - - Serial - Numer seryjny - - - Driver Camera - Kamera kierowcy - - - PREVIEW - PODGLĄD - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - Wyświetl podgląd z kamery skierowanej na kierowcę, aby upewnić się, że monitoring kierowcy ma dobry zakres widzenia. (pojazd musi być wyłączony) - - - Reset Calibration - Zresetuj kalibrację - - - RESET - ZRESETUJ - - - Are you sure you want to reset calibration? - Czy na pewno chcesz zresetować kalibrację? - - - Review Training Guide - Zapoznaj się z samouczkiem - - - REVIEW - ZAPOZNAJ SIĘ - - - Review the rules, features, and limitations of openpilot - Zapoznaj się z zasadami, funkcjami i ograniczeniami openpilota - - - Are you sure you want to review the training guide? - Czy na pewno chcesz się zapoznać z samouczkiem? - - - Regulatory - Regulacja - - - VIEW - WIDOK - - - Change Language - Zmień język - - - CHANGE - ZMIEŃ - - - Select a language - Wybierz język - - - Reboot - Uruchom ponownie - - - Power Off - Wyłącz - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot wymaga, aby urządzenie było zamontowane z maksymalnym odchyłem 4° poziomo, 5° w górę oraz 8° w dół. openpilot jest ciągle kalibrowany, rzadko konieczne jest resetowania urządzenia. - - - Your device is pointed %1° %2 and %3° %4. - Twoje urządzenie jest skierowane %1° %2 oraz %3° %4. - - - down - w dół - - - up - w górę - - - left - w lewo - - - right - w prawo - - - Are you sure you want to reboot? - Czy na pewno chcesz uruchomić ponownie urządzenie? - - - Disengage to Reboot - Aby uruchomić ponownie, odłącz sterowanie - - - Are you sure you want to power off? - Czy na pewno chcesz wyłączyć urządzenie? - - - Disengage to Power Off - Aby wyłączyć urządzenie, odłącz sterowanie - - - - DriveStats - - Drives - Przejazdy - - - Hours - Godziny - - - ALL TIME - CAŁKOWICIE - - - PAST WEEK - OSTATNI TYDZIEŃ - - - KM - KM - - - Miles - Mile - - - - DriverViewScene - - camera starting - uruchamianie kamery - - - - InputDialog - - Cancel - Anuluj - - - Need at least %n character(s)! - - Wpisana wartość powinna składać się przynajmniej z %n znaku! - Wpisana wartość powinna skłądać się przynajmniej z %n znaków! - Wpisana wartość powinna skłądać się przynajmniej z %n znaków! - - - - - Installer - - Installing... - Instalowanie... - - - Receiving objects: - Odbieranie obiektów: - - - Resolving deltas: - Rozwiązywanie różnic: - - - Updating files: - Aktualizacja plików: - - - - MapETA - - eta - przewidywany czas - - - min - min - - - hr - godz - - - km - km - - - mi - mi - - - - MapInstructions - - km - km - - - m - m - - - mi - mi - - - ft - ft - - - - MapPanel - - Current Destination - Miejsce docelowe - - - CLEAR - WYCZYŚĆ - - - Recent Destinations - Ostatnie miejsca docelowe - - - Try the Navigation Beta - Wypróbuj nawigację w wersji beta - - - Get turn-by-turn directions displayed and more with a comma -prime subscription. Sign up now: https://connect.comma.ai - Odblokuj nawigację zakręt po zakęcie i wiele więcej subskrybując -comma prime. Zarejestruj się teraz: https://connect.comma.ai - - - No home -location set - Lokalizacja domu -nie została ustawiona - - - No work -location set - Miejsce pracy -nie zostało ustawione - - - no recent destinations - brak ostatnich miejsc docelowych - - - - MapWindow - - Map Loading - Ładowanie Mapy - - - Waiting for GPS - Oczekiwanie na sygnał GPS - - - - MultiOptionDialog - - Select - Wybierz - - - Cancel - Anuluj - - - - Networking - - Advanced - Zaawansowane - - - Enter password - Wprowadź hasło - - - for "%1" - do "%1" - - - Wrong password - Niepoprawne hasło - - - - OffroadHome - - UPDATE - UAKTUALNIJ - - - ALERTS - ALERTY - - - ALERT - ALERT - - - - PairingPopup - - Pair your device to your comma account - Sparuj swoje urzadzenie ze swoim kontem comma - - - Go to https://connect.comma.ai on your phone - Wejdź na stronę https://connect.comma.ai na swoim telefonie - - - Click "add new device" and scan the QR code on the right - Kliknij "add new device" i zeskanuj kod QR znajdujący się po prawej stronie - - - Bookmark connect.comma.ai to your home screen to use it like an app - Dodaj connect.comma.ai do zakładek na swoim ekranie początkowym, aby korzystać z niej jak z aplikacji - - - - PrimeAdWidget - - Upgrade Now - Uaktualnij teraz - - - Become a comma prime member at connect.comma.ai - Zostań członkiem comma prime na connect.comma.ai - - - PRIME FEATURES: - FUNKCJE PRIME: - - - Remote access - Zdalny dostęp - - - 1 year of storage - 1 rok przechowywania danych - - - Developer perks - Udogodnienia dla programistów - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ ZASUBSKRYBOWANO - - - comma prime - comma prime - - - CONNECT.COMMA.AI - CONNECT.COMMA.AI - - - COMMA POINTS - COMMA POINTS - - - - QObject - - Reboot - Uruchom Ponownie - - - Exit - Wyjdź - - - dashcam - wideorejestrator - - - openpilot - openpilot - - - %n minute(s) ago - - %n minutę temu - %n minuty temu - %n minut temu - - - - %n hour(s) ago - - % godzinę temu - %n godziny temu - %n godzin temu - - - - %n day(s) ago - - %n dzień temu - %n dni temu - %n dni temu - - - - - Reset - - Reset failed. Reboot to try again. - Wymazywanie zakończone niepowodzeniem. Aby spróbować ponownie, uruchom ponownie urządzenie. - - - Are you sure you want to reset your device? - Czy na pewno chcesz wymazać urządzenie? - - - Resetting device... - Wymazywanie urządzenia... - - - System Reset - Przywróć do ustawień fabrycznych - - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - Przywracanie do ustawień fabrycznych. Wciśnij potwierdź, aby usunąć wszystkie dane oraz ustawienia. Wciśnij anuluj, aby wznowić uruchamianie. - - - Cancel - Anuluj - - - Reboot - Uruchom ponownie - - - Confirm - Potwiedź - - - Unable to mount data partition. Press confirm to reset your device. - Partycja nie została zamontowana poprawnie. Wciśnij potwierdź, aby uruchomić ponownie urządzenie. - - - - RichTextDialog - - Ok - Ok - - - - SettingsWindow - - × - x - - - Device - Urządzenie - - - Network - Sieć - - - Toggles - Przełączniki - - - Software - Oprogramowanie - - - Navigation - Nawigacja - - - - Setup - - WARNING: Low Voltage - OSTRZEŻENIE: Niskie Napięcie - - - Power your device in a car with a harness or proceed at your own risk. - Podłącz swoje urządzenie do zasilania poprzez podłączenienie go do pojazdu lub kontynuuj na własną odpowiedzialność. - - - Power off - Wyłącz - - - Continue - Kontynuuj - - - Getting Started - Zacznij - - - Before we get on the road, let’s finish installation and cover some details. - Zanim ruszysz w drogę, dokończ instalację i podaj kilka szczegółów. - - - Connect to Wi-Fi - Połącz z Wi-Fi - - - Back - Wróć - - - Continue without Wi-Fi - Kontynuuj bez połączenia z Wif-Fi - - - Waiting for internet - Oczekiwanie na połączenie sieciowe - - - Choose Software to Install - Wybierz oprogramowanie do instalacji - - - Dashcam - Wideorejestrator - - - Custom Software - Własne oprogramowanie - - - Enter URL - Wprowadź adres URL - - - for Custom Software - do własnego oprogramowania - - - Downloading... - Pobieranie... - - - Download Failed - Pobieranie nie powiodło się - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Upewnij się, że wpisany adres URL jest poprawny, a połączenie internetowe działa poprawnie. - - - Reboot device - Uruchom ponownie - - - Start over - Zacznij od początku - - - - SetupWidget - - Finish Setup - Zakończ konfigurację - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Sparuj swoje urządzenie z comma connect (connect.comma.ai) i wybierz swoją ofertę comma prime. - - - Pair device - Sparuj urządzenie - - - - Sidebar - - CONNECT - POŁĄCZENIE - - - OFFLINE - OFFLINE - - - ONLINE - ONLINE - - - ERROR - BŁĄD - - - TEMP - TEMP - - - HIGH - WYSOKA - - - GOOD - DOBRA - - - OK - OK - - - VEHICLE - POJAZD - - - NO - BRAK - - - PANDA - PANDA - - - GPS - GPS - - - SEARCH - SZUKAJ - - - -- - -- - - - Wi-Fi - Wi-FI - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Git Branch - Gałąź Git - - - Git Commit - Git commit - - - OS Version - Wersja systemu - - - Version - Wersja - - - Last Update Check - Ostatnie sprawdzenie aktualizacji - - - The last time openpilot successfully checked for an update. The updater only runs while the car is off. - Ostatni raz kiedy openpilot znalazł aktualizację. Aktualizator może być uruchomiony wyłącznie wtedy, kiedy pojazd jest wyłączony. - - - Check for Update - Sprawdź uaktualnienia - - - CHECKING - SPRAWDZANIE - - - Switch Branch - Zmień gąłąź - - - ENTER - WPROWADŹ - - - The new branch will be pulled the next time the updater runs. - Nowa gałąź będzie pobrana przy następnym uruchomieniu aktualizatora. - - - Enter branch name - Wprowadź nazwę gałęzi - - - Uninstall %1 - Odinstaluj %1 - - - UNINSTALL - ODINSTALUJ - - - Are you sure you want to uninstall? - Czy na pewno chcesz odinstalować? - - - failed to fetch update - pobieranie aktualizacji zakończone niepowodzeniem - - - CHECK - SPRAWDŹ - - - Updates are only downloaded while the car is off. - - - - Current Version - - - - Download - - - - Install Update - - - - INSTALL - - - - Target Branch - - - - SELECT - - - - Select a branch - - - - - SshControl - - SSH Keys - Klucze SSH - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - Ostrzeżenie: To spowoduje przekazanie dostępu do wszystkich Twoich publicznych kuczy z ustawień GitHuba. Nigdy nie wprowadzaj nazwy użytkownika innej niż swoja. Pracownik comma NIGDY nie poprosi o dodanie swojej nazwy uzytkownika. - - - ADD - DODAJ - - - Enter your GitHub username - Wpisz swoją nazwę użytkownika GitHub - - - LOADING - ŁADOWANIE - - - REMOVE - USUŃ - - - Username '%1' has no keys on GitHub - Użytkownik '%1' nie posiada żadnych kluczy na GitHubie - - - Request timed out - Limit czasu rządania - - - Username '%1' doesn't exist on GitHub - Użytkownik '%1' nie istnieje na GitHubie - - - - SshToggle - - Enable SSH - Włącz SSH - - - - TermsPage - - Terms & Conditions - Regulamin - - - Decline - Odrzuć - - - Scroll to accept - Przewiń w dół, aby zaakceptować - - - Agree - Zaakceptuj - - - - TogglesPanel - - Enable openpilot - Włącz openpilota - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Użyj openpilota do zachowania bezpiecznego odstępu między pojazdami i do asystowania w utrzymywaniu pasa ruchu. Twoja pełna uwaga jest wymagana przez cały czas korzystania z tej funkcji. Ustawienie to może być wdrożone wyłącznie wtedy, gdy pojazd jest wyłączony. - - - Enable Lane Departure Warnings - Włącz ostrzeganie przed zmianą pasa ruchu - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - Otrzymuj alerty o powrocie na właściwy pas, kiedy Twój pojazd przekroczy linię bez włączonego kierunkowskazu jadąc powyżej 50 km/h (31 mph). - - - Use Metric System - Korzystaj z systemu metrycznego - - - Display speed in km/h instead of mph. - Wyświetl prędkość w km/h zamiast mph. - - - Record and Upload Driver Camera - Nagraj i prześlij nagranie z kamery kierowcy - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - Prześlij dane z kamery skierowanej na kierowcę i pomóż poprawiać algorytm monitorowania kierowcy. - - - Disengage on Accelerator Pedal - Odłącz poprzez naciśnięcie gazu - - - When enabled, pressing the accelerator pedal will disengage openpilot. - Po włączeniu, naciśnięcie na pedał gazu odłączy openpilota. - - - Show ETA in 24h Format - Pokaż oczekiwany czas dojazdu w formacie 24-godzinnym - - - Use 24h format instead of am/pm - Korzystaj z formatu 24-godzinnego zamiast 12-godzinnego - - - Show Map on Left Side of UI - Pokaż mapę po lewej stronie ekranu - - - Show map on left side when in split screen view. - Pokaż mapę po lewej stronie kiedy ekran jest podzielony. - - - openpilot Longitudinal Control - Kontrola wzdłużna openpilota - - - openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB! - openpilot wyłączy radar samochodu i przejmie kontrolę nad gazem i hamulcem. Ostrzeżenie: wyłączony zostanie system AEB! - - - 🌮 End-to-end longitudinal (extremely alpha) 🌮 - - - - Experimental openpilot Longitudinal Control - - - - <b>WARNING: openpilot longitudinal control is experimental for this car and will disable AEB.</b> - - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would. Super experimental. - - - - openpilot longitudinal control is not currently available for this car. - - - - Enable experimental longitudinal control to enable this. - - - - - Updater - - Update Required - Wymagana Aktualizacja - - - An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. - Wymagana aktualizacja systemu operacyjnego. Aby przyspieszyć proces aktualizacji połącz swoje urzeądzenie do Wi-Fi. Rozmiar pobieranej paczki wynosi około 1GB. - - - Connect to Wi-Fi - Połącz się z Wi-Fi - - - Install - Zainstaluj - - - Back - Wróć - - - Loading... - Ładowanie... - - - Reboot - Uruchom ponownie - - - Update failed - Aktualizacja nie powiodła się - - - - WifiUI - - Scanning for networks... - Wyszukiwanie sieci... - - - CONNECTING... - ŁĄCZENIE... - - - FORGET - ZAPOMNIJ - - - Forget Wi-Fi Network "%1"? - Czy chcesz zapomnieć sieć "%1"? - - - diff --git a/selfdrive/ui/translations/pt-BR.ts b/selfdrive/ui/translations/pt-BR.ts deleted file mode 100644 index 77aa5e07c1..0000000000 --- a/selfdrive/ui/translations/pt-BR.ts +++ /dev/null @@ -1,1074 +0,0 @@ - - - - - AbstractAlert - - Close - Fechar - - - Reboot and Update - Reiniciar e Atualizar - - - - AdvancedNetworking - - Back - Voltar - - - Enable Tethering - Ativar Tether - - - Tethering Password - Senha Tethering - - - EDIT - EDITAR - - - Enter new tethering password - Insira nova senha tethering - - - IP Address - Endereço IP - - - Enable Roaming - Ativar Roaming - - - APN Setting - APN Config - - - Enter APN - Insira APN - - - leave blank for automatic configuration - deixe em branco para configuração automática - - - Cellular Metered - Plano de Dados Limitado - - - Hidden Network - Rede Oculta - - - CONNECT - CONECTE - - - Enter SSID - Digite o SSID - - - Enter password - Insira a senha - - - for "%1" - para "%1" - - - Prevent large data uploads when on a metered cellular connection - Previna o envio de grandes volumes de dados em conexões de celular com franquia de limite de dados - - - default - padrão - - - metered - limitada - - - unmetered - ilimitada - - - Wi-Fi Network Metered - Rede Wi-Fi com Franquia - - - Prevent large data uploads when on a metered Wi-Fi connection - Previna o envio de grandes volumes de dados em conexões Wi-Fi com franquia de limite de dados - - - - ConfirmationDialog - - Ok - OK - - - Cancel - Cancelar - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Você precisa aceitar os Termos e Condições para utilizar openpilot. - - - Back - Voltar - - - Decline, uninstall %1 - Rejeitar, desintalar %1 - - - - DeveloperPanel - - Joystick Debug Mode - Modo Joystick Debug - - - Longitudinal Maneuver Mode - Modo Longitudinal Maneuver - - - openpilot Longitudinal Control (Alpha) - Controle Longitudinal openpilot (Embrionário) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - AVISO: o controle longitudinal openpilot está em estado embrionário para este carro e desativará a Frenagem Automática de Emergência (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. - Neste carro, o openpilot tem como padrão o ACC embutido do carro em vez do controle longitudinal do openpilot. Habilite isso para alternar para o controle longitudinal openpilot. Recomenda-se ativar o modo Experimental ao ativar o embrionário controle longitudinal openpilot. - - - Enable ADB - Habilitar ADB - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB (Android Debug Bridge) permite conectar ao seu dispositivo por meio do USB ou através da rede. Veja https://docs.comma.ai/how-to/connect-to-comma para maiores informações. - - - - DevicePanel - - Dongle ID - Dongle ID - - - N/A - N/A - - - Serial - Serial - - - Driver Camera - Câmera do Motorista - - - PREVIEW - VER - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - Pré-visualizar a câmera voltada para o motorista para garantir que o monitoramento do sistema tenha uma boa visibilidade (veículo precisa estar desligado) - - - Reset Calibration - Reinicializar Calibragem - - - RESET - RESET - - - Are you sure you want to reset calibration? - Tem certeza que quer resetar a calibragem? - - - Review Training Guide - Revisar Guia de Treinamento - - - REVIEW - REVISAR - - - Review the rules, features, and limitations of openpilot - Revisar regras, aprimoramentos e limitações do openpilot - - - Are you sure you want to review the training guide? - Tem certeza que quer rever o treinamento? - - - Regulatory - Regulatório - - - VIEW - VER - - - Change Language - Alterar Idioma - - - CHANGE - ALTERAR - - - Select a language - Selecione o Idioma - - - Reboot - Reiniciar - - - Power Off - Desligar - - - Your device is pointed %1° %2 and %3° %4. - Seu dispositivo está montado %1° %2 e %3° %4. - - - down - baixo - - - up - cima - - - left - esquerda - - - right - direita - - - Are you sure you want to reboot? - Tem certeza que quer reiniciar? - - - Disengage to Reboot - Desacione para Reiniciar - - - Are you sure you want to power off? - Tem certeza que quer desligar? - - - Disengage to Power Off - Desacione para Desligar - - - Reset - Resetar - - - Review - Revisar - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Pareie seu dispositivo com comma connect (connect.comma.ai) e reivindique sua oferta de comma prime. - - - Pair Device - Parear Dispositivo - - - PAIR - PAREAR - - - Disengage to Reset Calibration - Desacione para Resetar a Calibração - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - O openpilot exige que o dispositivo seja montado dentro de 4° para a esquerda ou direita e dentro de 5° para cima ou 9° para baixo. - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - O openpilot está em constante calibração, raramente sendo necessário redefini-lo. Redefinir a calibração reiniciará o openpilot se o carro estiver ligado. - - - - -Steering lag calibration is %1% complete. - - -A calibração do atraso da direção está %1% concluída. - - - - -Steering lag calibration is complete. - - -A calibração do atraso da direção foi concluída. - - - Steering torque response calibration is %1% complete. - A calibração da resposta de torque da direção está %1% concluída. - - - Steering torque response calibration is complete. - A calibração da resposta do torque da direção foi concluída. - - - - DriverViewWindow - - camera starting - câmera iniciando - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - MODO EXPERIMENTAL ON - - - CHILL MODE ON - MODO CHILL ON - - - - FirehosePanel - - 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 - Modo Firehose: ATIVO - - - ACTIVE - 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>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. - - <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. - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INATIVO</span>: conecte-se a uma rede sem limite <br> de dados - - - Firehose Mode - Modo Firehose - - - - HudRenderer - - km/h - km/h - - - mph - mph - - - MAX - LIMITE - - - - InputDialog - - Cancel - Cancelar - - - Need at least %n character(s)! - - Necessita no mínimo %n caractere! - Necessita no mínimo %n caracteres! - - - - - MultiOptionDialog - - Select - Selecione - - - Cancel - Cancelar - - - - Networking - - Advanced - Avançado - - - Enter password - Insira a senha - - - for "%1" - para "%1" - - - Wrong password - Senha incorreta - - - - OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - Conecte-se imediatamente à internet para verificar se há atualizações. Se você não se conectar à internet em %1 não será possível acionar o openpilot. - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - Conecte-se à internet para verificar se há atualizações. O openpilot não será iniciado automaticamente até que ele se conecte à internet para verificar se há atualizações. - - - Unable to download updates -%1 - Não é possível baixar atualizações -%1 - - - Taking camera snapshots. System won't start until finished. - Tirando fotos da câmera. O sistema não será iniciado até terminar. - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - Uma atualização para o sistema operacional do seu dispositivo está sendo baixada em segundo plano. Você será solicitado a atualizar quando estiver pronto para instalar. - - - 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. - O openpilot não conseguiu identificar o seu carro. Seu carro não é suportado ou seus ECUs não são reconhecidos. Envie um pull request para adicionar as versões de firmware ao veículo adequado. Precisa de ajuda? Junte-se 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. - O openpilot detectou uma mudança na posição de montagem do dispositivo. Verifique se o dispositivo está totalmente encaixado no suporte e se o suporte está firmemente preso ao para-brisa. - - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - Temperatura do dispositivo muito alta. O sistema está sendo resfriado antes de iniciar. A temperatura atual do componente interno é: %1 - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - O dispositivo não conseguiu se registrar no backend da comma.ai. Ele não se conecta nem faz upload para os servidores da comma.ai e não recebe suporte da comma.ai. Se este for um dispositivo adquirido em comma.ai/shop, abra um ticket em https://comma.ai/support. - - - Acknowledge Excessive Actuation - Reconhecer Atuação Excessiva - - - Snooze Update - Adiar Atualização - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - openpilot detectou atuação excessiva de %1 na sua última condução. Entre em contato com o suporte em https://comma.ai/support e compartilhe o Dongle ID do seu dispositivo para solução de problemas. - - - - OffroadHome - - UPDATE - ATUALIZAÇÃO - - - ALERTS - ALERTAS - - - ALERT - ALERTA - - - - OnroadAlerts - - openpilot Unavailable - openpilot Indisponível - - - TAKE CONTROL IMMEDIATELY - ASSUMA IMEDIATAMENTE - - - Reboot Device - Reinicie o Dispositivo - - - Waiting to start - Aguardando para iniciar - - - System Unresponsive - Sistema sem Resposta - - - - PairingPopup - - Pair your device to your comma account - Pareie seu dispositivo à sua conta comma - - - Go to https://connect.comma.ai on your phone - navegue até https://connect.comma.ai no seu telefone - - - Click "add new device" and scan the QR code on the right - Clique "add new device" e escaneie o QR code a seguir - - - Bookmark connect.comma.ai to your home screen to use it like an app - Salve connect.comma.ai como sua página inicial para utilizar como um app - - - Please connect to Wi-Fi to complete initial pairing - Por favor conecte ao Wi-Fi para completar o pareamento inicial - - - - ParamControl - - Cancel - Cancelar - - - Enable - Ativar - - - - PrimeAdWidget - - Upgrade Now - Atualizar Agora - - - Become a comma prime member at connect.comma.ai - Seja um membro comma prime em connect.comma.ai - - - PRIME FEATURES: - BENEFÍCIOS PRIME: - - - Remote access - Acesso remoto (proxy comma) - - - 24/7 LTE connectivity - Conectividade LTE (só nos EUA) - - - 1 year of drive storage - 1 ano de dados em nuvem - - - Remote snapshots - Captura remota - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ INSCRITO - - - comma prime - comma prime - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - há %n minuto - há %n minutos - - - - %n hour(s) ago - - há %n hora - há %n horas - - - - %n day(s) ago - - há %n dia - há %n dias - - - - now - agora - - - - SettingsWindow - - × - × - - - Device - Dispositivo - - - Network - Rede - - - Toggles - Ajustes - - - Software - Software - - - Developer - Desenvdor - - - Firehose - Firehose - - - - SetupWidget - - Finish Setup - Concluir - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Pareie seu dispositivo com comma connect (connect.comma.ai) e reivindique sua oferta de comma prime. - - - Pair device - Parear dispositivo - - - - Sidebar - - CONNECT - CONEXÃO - - - OFFLINE - OFFLINE - - - ONLINE - ONLINE - - - ERROR - ERRO - - - TEMP - TEMP - - - HIGH - ALTA - - - GOOD - BOA - - - OK - OK - - - VEHICLE - VEÍCULO - - - NO - SEM - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Updates are only downloaded while the car is off. - Atualizações baixadas durante o motor desligado. - - - Current Version - Versão Atual - - - Download - Download - - - Install Update - Instalar Atualização - - - INSTALL - INSTALAR - - - Target Branch - Alterar Branch - - - SELECT - SELECIONE - - - Select a branch - Selecione uma branch - - - UNINSTALL - REMOVER - - - Uninstall %1 - Desinstalar o %1 - - - Are you sure you want to uninstall? - Tem certeza que quer desinstalar? - - - CHECK - VERIFICAR - - - Uninstall - Desinstalar - - - failed to check for update - falha ao verificar por atualizações - - - up to date, last checked %1 - atualizado, última verificação %1 - - - DOWNLOAD - BAIXAR - - - update available - atualização disponível - - - never - nunca - - - - SshControl - - SSH Keys - Chave SSH - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - Aviso: isso concede acesso SSH a todas as chaves públicas nas configurações do GitHub. Nunca insira um nome de usuário do GitHub que não seja o seu. Um funcionário da comma NUNCA pedirá que você adicione seu nome de usuário do GitHub. - - - ADD - ADICIONAR - - - Enter your GitHub username - Insira seu nome de usuário do GitHub - - - LOADING - CARREGANDO - - - REMOVE - REMOVER - - - Username '%1' has no keys on GitHub - Usuário "%1” não possui chaves no GitHub - - - Request timed out - A solicitação expirou - - - Username '%1' doesn't exist on GitHub - Usuário '%1' não existe no GitHub - - - - SshToggle - - Enable SSH - Habilitar SSH - - - - TermsPage - - Decline - Declinar - - - Agree - Concordo - - - Welcome to openpilot - Bem vindo ao 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. - 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. - - - - TogglesPanel - - Enable openpilot - Ativar openpilot - - - Enable Lane Departure Warnings - Ativar Avisos de Saída de Faixa - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - Receba alertas para voltar para a pista se o seu veículo sair da faixa e a seta não tiver sido acionada previamente quando em velocidades superiores a 50 km/h. - - - Use Metric System - Usar Sistema Métrico - - - Display speed in km/h instead of mph. - Exibir velocidade em km/h invés de mph. - - - Record and Upload Driver Camera - Gravar e Upload Câmera Motorista - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - Upload dados da câmera voltada para o motorista e ajude a melhorar o algoritmo de monitoramentor. - - - Disengage on Accelerator Pedal - Desacionar com Pedal do Acelerador - - - When enabled, pressing the accelerator pedal will disengage openpilot. - Quando ativado, pressionar o pedal do acelerador desacionará o openpilot. - - - Experimental Mode - Modo Experimental - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot por padrão funciona em <b>modo chill</b>. modo Experimental ativa <b>recursos de nível-embrionário</b> que não estão prontos para o modo chill. Recursos experimentais estão listados abaixo: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - Deixe o modelo de IA controlar o acelerador e os freios. O openpilot irá dirigir como pensa que um humano faria, incluindo parar em sinais vermelhos e sinais de parada. Uma vez que o modelo de condução decide a velocidade a conduzir, a velocidade definida apenas funcionará como um limite superior. Este é um recurso de qualidade embrionária; erros devem ser esperados. - - - New Driving Visualization - Nova Visualização de Condução - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - O modo Experimental está atualmente indisponível para este carro já que o ACC original do carro é usado para controle longitudinal. - - - openpilot longitudinal control may come in a future update. - O controle longitudinal openpilot poderá vir em uma atualização futura. - - - Aggressive - Disputa - - - Standard - Neutro - - - Relaxed - Calmo - - - Driving Personality - Temperamento de Direção - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Uma versão embrionária do controle longitudinal openpilot pode ser testada em conjunto com o modo Experimental, em branches que não sejam de produção. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Habilite o controle longitudinal (embrionário) openpilot para permitir o modo Experimental. - - - End-to-End Longitudinal Control - Controle Longitudinal de Ponta a Ponta - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Neutro é o recomendado. No modo disputa o openpilot seguirá o carro da frente mais de perto e será mais agressivo com a aceleração e frenagem. No modo calmo o openpilot se manterá mais longe do carro da frente. Em carros compatíveis, você pode alternar esses temperamentos com o botão de distância do volante. - - - 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. - A visualização de condução fará a transição para a câmera grande angular voltada para a estrada em baixas velocidades para mostrar melhor algumas curvas. O logotipo do modo Experimental também será mostrado no canto superior direito. - - - Always-On Driver Monitoring - Monitoramento do Motorista Sempre Ativo - - - Enable driver monitoring even when openpilot is not engaged. - Habilite o monitoramento do motorista mesmo quando o openpilot não estiver acionado. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - Use o sistema openpilot para controle de cruzeiro adaptativo e assistência ao motorista de manutenção de faixa. Sua atenção é necessária o tempo todo para usar esse recurso. - - - Changing this setting will restart openpilot if the car is powered on. - Alterar esta configuração fará com que o openpilot reinicie se o carro estiver ligado. - - - Record and Upload Microphone Audio - Gravar e Fazer Upload do Áudio do Microfone - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - Grave e armazene o áudio do microfone enquanto estiver dirigindo. O áudio será incluído ao vídeo dashcam no comma connect. - - - - WiFiPromptWidget - - Open - Abrir - - - Maximize your training data uploads to improve openpilot's driving models. - Maximize seus envios de dados de treinamento para melhorar os modelos de direção do 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> Modo Firehose <span style='font-family: Noto Color Emoji;'>🔥</span> - - - - WifiUI - - Scanning for networks... - Procurando redes... - - - CONNECTING... - CONECTANDO... - - - FORGET - ESQUECER - - - Forget Wi-Fi Network "%1"? - Esquecer Rede Wi-Fi "%1"? - - - Forget - Esquecer - - - diff --git a/selfdrive/ui/translations/th.ts b/selfdrive/ui/translations/th.ts deleted file mode 100644 index 9bd6822086..0000000000 --- a/selfdrive/ui/translations/th.ts +++ /dev/null @@ -1,1065 +0,0 @@ - - - - - AbstractAlert - - Close - ปิด - - - Reboot and Update - รีบูตและอัปเดต - - - - AdvancedNetworking - - Back - ย้อนกลับ - - - Enable Tethering - ปล่อยฮอตสปอต - - - Tethering Password - รหัสผ่านฮอตสปอต - - - EDIT - แก้ไข - - - Enter new tethering password - ป้อนรหัสผ่านฮอตสปอตใหม่ - - - IP Address - หมายเลขไอพี - - - Enable Roaming - เปิดใช้งานโรมมิ่ง - - - APN Setting - ตั้งค่า APN - - - Enter APN - ป้อนค่า APN - - - leave blank for automatic configuration - เว้นว่างเพื่อตั้งค่าอัตโนมัติ - - - Cellular Metered - ลดการส่งข้อมูลผ่านเซลลูล่าร์ - - - Hidden Network - เครือข่ายที่ซ่อนอยู่ - - - CONNECT - เชื่อมต่อ - - - Enter SSID - ป้อนค่า SSID - - - Enter password - ใส่รหัสผ่าน - - - for "%1" - สำหรับ "%1" - - - Prevent large data uploads when on a metered cellular connection - - - - default - - - - metered - - - - unmetered - - - - Wi-Fi Network Metered - - - - Prevent large data uploads when on a metered Wi-Fi connection - - - - - ConfirmationDialog - - Ok - ตกลง - - - Cancel - ยกเลิก - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - คุณต้องยอมรับเงื่อนไขและข้อตกลง เพื่อใช้งาน openpilot - - - Back - ย้อนกลับ - - - Decline, uninstall %1 - ปฏิเสธ และถอนการติดตั้ง %1 - - - - DeveloperPanel - - Joystick Debug Mode - โหมดดีบักจอยสติ๊ก - - - Longitudinal Maneuver Mode - โหมดการควบคุมการเร่ง/เบรค - - - openpilot Longitudinal Control (Alpha) - ระบบควบคุมการเร่ง/เบรคโดย openpilot (Alpha) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - คำเตือน: การควบคุมการเร่ง/เบรคโดย openpilot สำหรับรถคันนี้ยังอยู่ในสถานะ alpha และระบบเบรคฉุกเฉินอัตโนมัติ (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 ควบคุมการเร่ง/เบรค ซึ่งอยู่ในสถานะ alpha - - - Enable ADB - เปิด ADB - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB (Android Debug Bridge) อนุญาตให้เชื่อมต่ออุปกรณ์ของคุณผ่าน USB หรือผ่านเครือข่าย ดูข้อมูลเพิ่มเติมที่ https://docs.comma.ai/how-to/connect-to-comma - - - - DevicePanel - - Dongle ID - Dongle ID - - - N/A - ไม่มี - - - Serial - ซีเรียล - - - Driver Camera - กล้องฝั่งคนขับ - - - PREVIEW - แสดงภาพ - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - ดูภาพตัวอย่างกล้องที่หันเข้าหาคนขับเพื่อให้แน่ใจว่าการตรวจสอบคนขับมีทัศนวิสัยที่ดี (รถต้องดับเครื่องยนต์) - - - Reset Calibration - รีเซ็ตการคาลิเบรท - - - RESET - รีเซ็ต - - - Are you sure you want to reset calibration? - คุณแน่ใจหรือไม่ว่าต้องการรีเซ็ตการคาลิเบรท? - - - Review Training Guide - ทบทวนคู่มือการใช้งาน - - - REVIEW - ทบทวน - - - Review the rules, features, and limitations of openpilot - ตรวจสอบกฎ คุณสมบัติ และข้อจำกัดของ openpilot - - - Are you sure you want to review the training guide? - คุณแน่ใจหรือไม่ว่าต้องการทบทวนคู่มือการใช้งาน? - - - Regulatory - ระเบียบข้อบังคับ - - - VIEW - ดู - - - Change Language - เปลี่ยนภาษา - - - CHANGE - เปลี่ยน - - - Select a language - เลือกภาษา - - - Reboot - รีบูต - - - Power Off - ปิดเครื่อง - - - Your device is pointed %1° %2 and %3° %4. - อุปกรณ์ของคุณเอียงไปทาง %2 %1° และ %4 %3° - - - down - ด้านล่าง - - - up - ด้านบน - - - left - ด้านซ้าย - - - right - ด้านขวา - - - Are you sure you want to reboot? - คุณแน่ใจหรือไม่ว่าต้องการรีบูต? - - - Disengage to Reboot - ยกเลิกระบบช่วยขับเพื่อรีบูต - - - Are you sure you want to power off? - คุณแน่ใจหรือไม่ว่าต้องการปิดเครื่อง? - - - Disengage to Power Off - ยกเลิกระบบช่วยขับเพื่อปิดเครื่อง - - - Reset - รีเซ็ต - - - Review - ทบทวน - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - จับคู่อุปกรณ์ของคุณกับ comma connect (connect.comma.ai) และรับข้อเสนอ comma prime ของคุณ - - - Pair Device - จับคู่อุปกรณ์ - - - PAIR - จับคู่ - - - Disengage to Reset Calibration - - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - - - - - -Steering lag calibration is %1% complete. - - - - - -Steering lag calibration is complete. - - - - Steering torque response calibration is %1% complete. - - - - Steering torque response calibration is complete. - - - - - DriverViewWindow - - camera starting - กำลังเปิดกล้อง - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - คุณกำลังใช้โหมดทดลอง - - - CHILL MODE ON - คุณกำลังใช้โหมดชิล - - - - FirehosePanel - - 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 - เปิดใช้งาน - - - 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>โหมดสายยางดับเพลิงยังสามารถทำงานระหว่างขับรถได้ หากเชื่อมต่อกับฮอตสปอตหรือซิมการ์ดที่มีเน็ตไม่จำกัด<br><br><br><b>คำถามที่พบบ่อย</b><br><br><i>วิธีการขับหรือสถานที่ขับขี่มีผลหรือไม่?</i>ไม่มีผล แค่ขับขี่ตามปกติของคุณ<br><br><i>เซกเมนต์ทั้งหมดของฉันจะถูกดึงข้อมูลในโหมดสายยางดับเพลิงหรือไม่?</i>ไม่ใช่ เราจะเลือกดึงข้อมูลเพียงบางส่วนจากเซกเมนต์ของคุณ<br><br><i>อะแดปเตอร์ USB-C แบบไหนดี?</i>ที่ชาร์จเร็วของโทรศัพท์หรือแล็ปท็อปแบบใดก็ได้ สามารถใช้ได้<br><br><i>ซอฟต์แวร์ที่ใช้มีผลหรือไม่?</i>มีผล เฉพาะ openpilot ตัวหลัก (และ fork เฉพาะบางตัว) เท่านั้น ที่สามารถนำข้อมูลไปใช้ฝึกฝนโมเดลได้ - - - <b>%n segment(s)</b> of your driving is in the training dataset so far. - - มีการขับขี่ของคุณ <b>%n เซกเมนต์</b> อยู่ในชุดข้อมูลการฝึกฝนแล้วในขณะนี้ - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>ไม่เปิดใช้งาน</span>: เชื่อมต่อกับเครือข่ายที่ไม่จำกัดข้อมูล - - - Firehose Mode - - - - - HudRenderer - - km/h - กม./ชม. - - - mph - ไมล์/ชม. - - - MAX - สูงสุด - - - - InputDialog - - Cancel - ยกเลิก - - - Need at least %n character(s)! - - ต้องการอย่างน้อย %n ตัวอักษร! - - - - - MultiOptionDialog - - Select - เลือก - - - Cancel - ยกเลิก - - - - Networking - - Advanced - ขั้นสูง - - - Enter password - ใส่รหัสผ่าน - - - for "%1" - สำหรับ "%1" - - - Wrong password - รหัสผ่านผิด - - - - OffroadAlert - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - อุณหภูมิของอุปกรณ์สูงเกินไป ระบบกำลังทำความเย็นก่อนเริ่ม อุณหภูมิของชิ้นส่วนภายในปัจจุบัน: %1 - - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - กรุณาเชื่อมต่ออินเตอร์เน็ตเพื่อตรวจสอบอัปเดทเดี๋ยวนี้ ถ้าคุณไม่เชื่อมต่ออินเตอร์เน็ต openpilot จะไม่ทำงานในอีก %1 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - กรุณาเชื่อมต่ออินเตอร์เน็ตเพื่อตรวจสอบอัปเดท openpilot จะไม่เริ่มทำงานอัตโนมัติจนกว่าจะได้เชื่อมต่อกับอินเตอร์เน็ตเพื่อตรวจสอบอัปเดท - - - Unable to download updates -%1 - ไม่สามารถดาวน์โหลดอัพเดทได้ -%1 - - - Taking camera snapshots. System won't start until finished. - กล้องกำลังถ่ายภาพ ระบบจะไม่เริ่มทำงานจนกว่าจะเสร็จ - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - กำลังดาวน์โหลดอัปเดทสำหรับระบบปฏิบัติการอยู่เบื้องหลัง คุณจะได้รับการแจ้งเตือนเมื่อระบบพร้อมสำหรับการติดตั้ง - - - 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 กรุณาส่ง pull request เพื่อเพิ่มรุ่นของเฟิร์มแวร์ให้กับรถยนต์ที่เหมาะสม หากต้องการความช่วยเหลือให้เข้าร่วม 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. - openpilot ตรวจพบการเปลี่ยนแปลงของตำแหน่งที่ติดตั้ง กรุณาตรวจสอบว่าได้เลื่อนอุปกรณ์เข้ากับจุดติดตั้งจนสุดแล้ว และจุดติดตั้งได้ยึดติดกับกระจกหน้าอย่างแน่นหนา - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - - - - Acknowledge Excessive Actuation - - - - Snooze Update - เลื่อนการอัปเดต - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - - - - - OffroadHome - - UPDATE - อัปเดต - - - ALERTS - การแจ้งเตือน - - - ALERT - การแจ้งเตือน - - - - OnroadAlerts - - openpilot Unavailable - openpilot ไม่สามารถใช้งานได้ - - - TAKE CONTROL IMMEDIATELY - เข้าควบคุมรถเดี๋ยวนี้ - - - Reboot Device - รีบูตอุปกรณ์ - - - Waiting to start - รอเริ่มทำงาน - - - System Unresponsive - ระบบไม่ตอบสนอง - - - - PairingPopup - - Pair your device to your comma account - จับคู่อุปกรณ์ของคุณกับบัญชี comma ของคุณ - - - Go to https://connect.comma.ai on your phone - ไปที่ https://connect.comma.ai ด้วยโทรศัพท์ของคุณ - - - Click "add new device" and scan the QR code on the right - กดที่ "add new device" และสแกนคิวอาร์โค้ดทางด้านขวา - - - Bookmark connect.comma.ai to your home screen to use it like an app - จดจำ connect.comma.ai โดยการเพิ่มไปยังหน้าจอโฮม เพื่อใช้งานเหมือนเป็นแอปพลิเคชัน - - - Please connect to Wi-Fi to complete initial pairing - กรุณาเชื่อมต่อ Wi-Fi เพื่อทำการจับคู่ครั้งแรกให้เสร็จสิ้น - - - - ParamControl - - Enable - เปิดใช้งาน - - - Cancel - ยกเลิก - - - - PrimeAdWidget - - Upgrade Now - อัพเกรดเดี๋ยวนี้ - - - Become a comma prime member at connect.comma.ai - สมัครสมาชิก comma prime ได้ที่ connect.comma.ai - - - PRIME FEATURES: - คุณสมบัติของ PRIME: - - - Remote access - การเข้าถึงระยะไกล - - - 24/7 LTE connectivity - การเชื่อมต่อ LTE แบบ 24/7 - - - 1 year of drive storage - จัดเก็บข้อมูลการขับขี่นาน 1 ปี - - - Remote snapshots - ภาพถ่ายระยะไกล - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ สมัครสำเร็จ - - - comma prime - comma prime - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - %n นาทีที่แล้ว - - - - %n hour(s) ago - - %n ชั่วโมงที่แล้ว - - - - %n day(s) ago - - %n วันที่แล้ว - - - - now - ตอนนี้ - - - - SettingsWindow - - × - × - - - Device - อุปกรณ์ - - - Network - เครือข่าย - - - Toggles - ตัวเลือก - - - Software - ซอฟต์แวร์ - - - Developer - นักพัฒนา - - - Firehose - สายยางดับเพลิง - - - - SetupWidget - - Finish Setup - ตั้งค่าเสร็จสิ้น - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - จับคู่อุปกรณ์ของคุณกับ comma connect (connect.comma.ai) และรับข้อเสนอ comma prime ของคุณ - - - Pair device - จับคู่อุปกรณ์ - - - - Sidebar - - CONNECT - เชื่อมต่อ - - - OFFLINE - ออฟไลน์ - - - ONLINE - ออนไลน์ - - - ERROR - เกิดข้อผิดพลาด - - - TEMP - อุณหภูมิ - - - HIGH - สูง - - - GOOD - ดี - - - OK - พอใช้ - - - VEHICLE - รถยนต์ - - - NO - ไม่พบ - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Uninstall %1 - ถอนการติดตั้ง %1 - - - UNINSTALL - ถอนการติดตั้ง - - - Are you sure you want to uninstall? - คุณแน่ใจหรือไม่ว่าต้องการถอนการติดตั้ง? - - - CHECK - ตรวจสอบ - - - Updates are only downloaded while the car is off. - ตัวอัปเดตจะดำเนินการดาวน์โหลดเมื่อรถดับเครื่องยนต์อยู่เท่านั้น - - - Current Version - เวอร์ชั่นปัจจุบัน - - - Download - ดาวน์โหลด - - - Install Update - ติดตั้งตัวอัปเดต - - - INSTALL - ติดตั้ง - - - Target Branch - Branch ที่เลือก - - - SELECT - เลือก - - - Select a branch - เลือก Branch - - - Uninstall - ถอนการติดตั้ง - - - failed to check for update - ไม่สามารถตรวจสอบอัปเดตได้ - - - DOWNLOAD - ดาวน์โหลด - - - update available - มีอัปเดตใหม่ - - - never - ไม่เคย - - - up to date, last checked %1 - ล่าสุดแล้ว ตรวจสอบครั้งสุดท้ายเมื่อ %1 - - - - SshControl - - SSH Keys - คีย์ SSH - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - คำเตือน: สิ่งนี้ให้สิทธิ์ SSH เข้าถึงคีย์สาธารณะทั้งหมดใน GitHub ของคุณ อย่าป้อนชื่อผู้ใช้ GitHub อื่นนอกเหนือจากของคุณเอง พนักงาน comma จะไม่ขอให้คุณเพิ่มชื่อผู้ใช้ GitHub ของพวกเขา - - - ADD - เพิ่ม - - - Enter your GitHub username - ป้อนชื่อผู้ใช้ GitHub ของคุณ - - - LOADING - กำลังโหลด - - - REMOVE - ลบ - - - Username '%1' has no keys on GitHub - ชื่อผู้ใช้ '%1' ไม่มีคีย์บน GitHub - - - Request timed out - ตรวจสอบไม่สำเร็จ เนื่องจากใช้เวลามากเกินไป - - - Username '%1' doesn't exist on GitHub - ไม่พบชื่อผู้ใช้ '%1' บน GitHub - - - - SshToggle - - Enable SSH - เปิดใช้งาน SSH - - - - TermsPage - - Decline - ปฏิเสธ - - - Agree - ยอมรับ - - - Welcome to openpilot - ยินดีต้อนรับสู่ 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. - คุณต้องยอมรับข้อกำหนดและเงื่อนไขเพื่อใช้งาน openpilot อ่านข้อกำหนดล่าสุดได้ที่ <span style='color: #465BEA;'>https://comma.ai/terms</span> ก่อนดำเนินการต่อ - - - - TogglesPanel - - Enable openpilot - เปิดใช้งาน openpilot - - - Enable Lane Departure Warnings - เปิดใช้งานการเตือนการออกนอกเลน - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - รับการแจ้งเตือนให้เลี้ยวกลับเข้าเลนเมื่อรถของคุณตรวจพบการข้ามช่องจราจรโดยไม่เปิดสัญญาณไฟเลี้ยวในขณะขับขี่ที่ความเร็วเกิน 31 ไมล์ต่อชั่วโมง (50 กม./ชม) - - - Use Metric System - ใช้ระบบเมตริก - - - Display speed in km/h instead of mph. - แสดงความเร็วเป็น กม./ชม. แทน ไมล์/ชั่วโมง - - - Record and Upload Driver Camera - บันทึกและอัปโหลดภาพจากกล้องคนขับ - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - อัปโหลดข้อมูลจากกล้องที่หันหน้าไปทางคนขับ และช่วยปรับปรุงอัลกอริธึมการตรวจสอบผู้ขับขี่ - - - Disengage on Accelerator Pedal - ยกเลิกระบบช่วยขับเมื่อเหยียบคันเร่ง - - - When enabled, pressing the accelerator pedal will disengage openpilot. - เมื่อเปิดใช้งาน การกดแป้นคันเร่งจะเป็นการยกเลิกระบบช่วยขับโดย openpilot - - - Experimental Mode - โหมดทดลอง - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - โดยปกติ openpilot จะขับใน<b>โหมดชิล</b> เปิดโหมดทดลองเพื่อใช้<b>ความสามารถในขั้นพัฒนา</b> ซึ่งยังไม่พร้อมสำหรับโหมดชิล ความสามารถในขั้นพัฒนามีดังนี้: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - ให้ openpilot ควบคุมการเร่ง/เบรค โดย openpilot จะขับอย่างที่มนุษย์คิด รวมถึงการหยุดที่ไฟแดง และป้ายหยุดรถ เนื่องจาก openpilot จะกำหนดความเร็วในการขับด้วยตัวเอง การตั้งความเร็วจะเป็นเพียงการกำหนดความเร็วสูงสูดเท่านั้น ความสามารถนี้ยังอยู่ในขั้นพัฒนา อาจเกิดข้อผิดพลาดขึ้นได้ - - - New Driving Visualization - การแสดงภาพการขับขี่แบบใหม่ - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - ขณะนี้โหมดทดลองไม่สามารถใช้งานได้ในรถคันนี้ เนื่องจากเปิดใช้ระบบควบคุมการเร่ง/เบรคของรถที่ติดตั้งจากโรงงานอยู่ - - - openpilot longitudinal control may come in a future update. - ระบบควบคุมการเร่ง/เบรคโดย openpilot อาจมาในการอัปเดตในอนาคต - - - Aggressive - ดุดัน - - - Standard - มาตรฐาน - - - Relaxed - ผ่อนคลาย - - - Driving Personality - บุคลิกการขับขี่ - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - ระบบควบคุมการเร่ง/เบรคโดย openpilot เวอร์ชัน alpha สามารถทดสอบได้พร้อมกับโหมดการทดลอง บน branch ที่กำลังพัฒนา - - - End-to-End Longitudinal Control - ควบคุมเร่ง/เบรคแบบ End-to-End - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - เปิดระบบควบคุมการเร่ง/เบรคโดย openpilot (alpha) เพื่อเปิดใช้งานโหมดทดลอง - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - แนะนำให้ใช้แบบมาตรฐาน ในโหมดดุดัน openpilot จะตามรถคันหน้าใกล้ขึ้นและเร่งและเบรคแบบดุดันมากขึ้น ในโหมดผ่อนคลาย openpilot จะอยู่ห่างจากรถคันหน้ามากขึ้น ในรถรุ่นที่รองรับคุณสามารถเปลี่ยนบุคลิกไปแบบต่าง ๆ โดยใช้ปุ่มปรับระยะห่างบนพวงมาลัย - - - 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 - การเฝ้าระวังผู้ขับขี่ตลอดเวลา - - - Enable driver monitoring even when openpilot is not engaged. - เปิดใช้งานการเฝ้าระวังผู้ขับขี่แม้เมื่อ openpilot ไม่ได้เข้าควบคุมอยู่ - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - - - Changing this setting will restart openpilot if the car is powered on. - - - - Record and Upload Microphone Audio - - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - - - - WiFiPromptWidget - - Open - เปิด - - - 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> - - - - WifiUI - - Scanning for networks... - กำลังสแกนหาเครือข่าย... - - - CONNECTING... - กำลังเชื่อมต่อ... - - - FORGET - เลิกใช้ - - - Forget Wi-Fi Network "%1"? - เลิกใช้เครือข่าย Wi-Fi "%1"? - - - Forget - เลิกใช้ - - - diff --git a/selfdrive/ui/translations/tr.ts b/selfdrive/ui/translations/tr.ts deleted file mode 100644 index 7c6c692d54..0000000000 --- a/selfdrive/ui/translations/tr.ts +++ /dev/null @@ -1,1062 +0,0 @@ - - - - - AbstractAlert - - Close - Kapat - - - Reboot and Update - Güncelle ve Yeniden başlat - - - - AdvancedNetworking - - Back - Geri dön - - - Enable Tethering - Kişisel erişim noktasını aç - - - Tethering Password - Kişisel erişim noktasının parolası - - - EDIT - DÜZENLE - - - Enter new tethering password - Erişim noktasına yeni bir sonraki başlatılışında çekilir. parola belirleyin. - - - IP Address - IP Adresi - - - Enable Roaming - Hücresel veri aç - - - APN Setting - APN Ayarları - - - Enter APN - APN Gir - - - leave blank for automatic configuration - otomatik yapılandırma için boş bırakın - - - Cellular Metered - - - - Hidden Network - - - - CONNECT - BAĞLANTI - - - Enter SSID - APN Gir - - - Enter password - Parolayı girin - - - for "%1" - için "%1" - - - Prevent large data uploads when on a metered cellular connection - - - - default - - - - metered - - - - unmetered - - - - Wi-Fi Network Metered - - - - Prevent large data uploads when on a metered Wi-Fi connection - - - - - ConfirmationDialog - - Ok - Tamam - - - Cancel - Vazgeç - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Openpilotu kullanmak için Kullanıcı Koşullarını kabul etmelisiniz. - - - Back - Geri - - - Decline, uninstall %1 - Reddet, Kurulumu kaldır. %1 - - - - DeveloperPanel - - Joystick Debug Mode - - - - Longitudinal Maneuver Mode - - - - openpilot Longitudinal Control (Alpha) - - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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. - - - - Enable ADB - - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - - - - - DevicePanel - - Dongle ID - Adaptör ID - - - N/A - N/A - - - Serial - Seri Numara - - - Driver Camera - Sürücü Kamerası - - - PREVIEW - ÖN İZLEME - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - Sürücü kamerasının görüş açısını test etmek için kamerayı önizleyin (Araç kapalı olmalıdır.) - - - Reset Calibration - Kalibrasyonu sıfırla - - - RESET - SIFIRLA - - - Are you sure you want to reset calibration? - Kalibrasyon ayarını sıfırlamak istediğinizden emin misiniz? - - - Review Training Guide - Eğitim kılavuzunu inceleyin - - - REVIEW - GÖZDEN GEÇİR - - - Review the rules, features, and limitations of openpilot - openpilot sisteminin kurallarını ve sınırlamalarını gözden geçirin. - - - Are you sure you want to review the training guide? - Eğitim kılavuzunu incelemek istediğinizden emin misiniz? - - - Regulatory - Mevzuat - - - VIEW - BAK - - - Change Language - Dili değiştir - - - CHANGE - DEĞİŞTİR - - - Select a language - Dil seçin - - - Reboot - Yeniden başlat - - - Power Off - Sistemi kapat - - - Your device is pointed %1° %2 and %3° %4. - Cihazınız %1° %2 ve %3° %4 yönünde ayarlı - - - down - aşağı - - - up - yukarı - - - left - sol - - - right - sağ - - - Are you sure you want to reboot? - Cihazı Tekrar başlatmak istediğinizden eminmisiniz? - - - Disengage to Reboot - Bağlantıyı kes ve Cihazı Yeniden başlat - - - Are you sure you want to power off? - Cihazı kapatmak istediğizden eminmisiniz? - - - Disengage to Power Off - Bağlantıyı kes ve Cihazı kapat - - - Reset - - - - Review - - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Cihazınızı comma connect (connect.comma.ai) ile eşleştirin ve comma prime aboneliğine göz atın. - - - Pair Device - - - - PAIR - - - - Disengage to Reset Calibration - - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - - - - - -Steering lag calibration is %1% complete. - - - - - -Steering lag calibration is complete. - - - - Steering torque response calibration is %1% complete. - - - - Steering torque response calibration is complete. - - - - - DriverViewWindow - - camera starting - kamera başlatılıyor - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - - - - CHILL MODE ON - - - - - FirehosePanel - - 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 - - - - 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>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. - - - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - - - - Firehose Mode - - - - - HudRenderer - - km/h - km/h - - - mph - mph - - - MAX - MAX - - - - InputDialog - - Cancel - Kapat - - - Need at least %n character(s)! - - En az %n karakter gerekli! - - - - - MultiOptionDialog - - Select - Seç - - - Cancel - İptal et - - - - Networking - - Advanced - Gelişmiş Seçenekler - - - Enter password - Parolayı girin - - - for "%1" - için "%1" - - - Wrong password - Yalnış parola - - - - OffroadAlert - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - - - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - - - - Unable to download updates -%1 - - - - Taking camera snapshots. System won't start until finished. - - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - - - - 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 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. - - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - - - - Acknowledge Excessive Actuation - - - - Snooze Update - Güncellemeyi sessize al - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - - - - - OffroadHome - - UPDATE - GÜNCELLE - - - ALERTS - UYARILAR - - - ALERT - UYARI - - - - OnroadAlerts - - openpilot Unavailable - - - - TAKE CONTROL IMMEDIATELY - - - - Reboot Device - - - - Waiting to start - - - - System Unresponsive - - - - - PairingPopup - - Pair your device to your comma account - comma.ai hesabınız ile cihazı eşleştirin - - - Go to https://connect.comma.ai on your phone - Telefonuzdan https://connect.comma.ai sitesine gidin - - - Click "add new device" and scan the QR code on the right - Yeni cihaz eklemek için sağdaki QR kodunu okutun - - - Bookmark connect.comma.ai to your home screen to use it like an app - Uygulama gibi kullanmak için connect.comma.ai sitesini yer işaretlerine ekleyin. - - - Please connect to Wi-Fi to complete initial pairing - - - - - ParamControl - - Enable - - - - Cancel - - - - - PrimeAdWidget - - Upgrade Now - Hemen yükselt - - - Become a comma prime member at connect.comma.ai - connect.comma.ai üzerinden comma prime üyesi olun - - - PRIME FEATURES: - PRIME ABONELİĞİNİN ÖZELLİKLERİ: - - - Remote access - Uzaktan erişim - - - 24/7 LTE connectivity - - - - 1 year of drive storage - - - - Remote snapshots - - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ ABONE - - - comma prime - comma prime - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - %n dakika önce - - - - %n hour(s) ago - - %n saat önce - - - - %n day(s) ago - - %n gün önce - - - - now - - - - - SettingsWindow - - × - x - - - Device - Cihaz - - - Network - - - - Toggles - Değiştirme - - - Software - Yazılım - - - Developer - - - - Firehose - - - - - SetupWidget - - Finish Setup - Kurulumu bitir - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - Cihazınızı comma connect (connect.comma.ai) ile eşleştirin ve comma prime aboneliğine göz atın. - - - Pair device - Cihazı eşleştirme - - - - Sidebar - - CONNECT - BAĞLANTI - - - OFFLINE - ÇEVRİMDIŞI - - - ONLINE - ÇEVRİMİÇİ - - - ERROR - HATA - - - TEMP - SICAKLIK - - - HIGH - YÜKSEK - - - GOOD - İYİ - - - OK - TAMAM - - - VEHICLE - ARAÇ - - - NO - HAYIR - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-FI - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Uninstall %1 - Kaldır %1 - - - UNINSTALL - KALDIR - - - Are you sure you want to uninstall? - Kaldırmak istediğinden eminmisin? - - - CHECK - KONTROL ET - - - Updates are only downloaded while the car is off. - - - - Current Version - - - - Download - - - - Install Update - - - - INSTALL - - - - Target Branch - - - - SELECT - - - - Select a branch - - - - Uninstall - - - - failed to check for update - - - - DOWNLOAD - - - - update available - - - - never - - - - up to date, last checked %1 - - - - - SshControl - - SSH Keys - SSH Anahtarları - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - UYARI: Bu, GitHub ayarlarınızdaki tüm ortak anahtarlara SSH erişimi sağlar. Asla kendi kullanıcı adınız dışında bir sonraki başlatılışında çekilir. GitHub kullanıcı adı girmeyin. - - - ADD - EKLE - - - Enter your GitHub username - Github kullanıcı adınızı giriniz - - - LOADING - YÜKLENİYOR - - - REMOVE - KALDIR - - - Username '%1' has no keys on GitHub - Kullanısının '%1' Github erişim anahtarı yok - - - Request timed out - İstek zaman aşımına uğradı - - - Username '%1' doesn't exist on GitHub - Github kullanıcısı %1 bulunamadı - - - - SshToggle - - Enable SSH - SSH aç - - - - TermsPage - - Decline - Reddet - - - Agree - Kabul et - - - 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. - - - - - TogglesPanel - - Enable openpilot - openpilot'u aktifleştir - - - Enable Lane Departure Warnings - Şerit ihlali uyarı alın - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - 50 km/s (31 mph) hızın üzerinde sürüş sırasında aracınız dönüş sinyali vermeden algılanan bir sonraki başlatılışında çekilir. şerit çizgisi ihlalinde şeride geri dönmek için uyarılar alın. - - - Use Metric System - Metrik sistemi kullan - - - Display speed in km/h instead of mph. - Hızı mph yerine km/h şeklinde görüntüleyin. - - - Record and Upload Driver Camera - Sürücü kamerasını kayıt et. - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - Sürücüye bakan kamera verisini yükleyin ve Cihazın algoritmasını geliştirmemize yardımcı olun. - - - When enabled, pressing the accelerator pedal will disengage openpilot. - Aktifleştirilirse eğer gaz pedalına basınca openpilot devre dışı kalır. - - - Experimental Mode - - - - Disengage on Accelerator Pedal - - - - Aggressive - - - - Standard - - - - Relaxed - - - - Driving Personality - - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - - - - End-to-End Longitudinal Control - - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - - - - New Driving Visualization - - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - - - - openpilot longitudinal control may come in a future update. - - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - - - - 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 - - - - Enable driver monitoring even when openpilot is not engaged. - - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - - - Changing this setting will restart openpilot if the car is powered on. - - - - Record and Upload Microphone Audio - - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - - - - WiFiPromptWidget - - Open - - - - Maximize your training data uploads to improve openpilot's driving models. - - - - <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> - - - - - WifiUI - - Scanning for networks... - Ağ aranıyor... - - - CONNECTING... - BAĞLANILIYOR... - - - FORGET - UNUT - - - Forget Wi-Fi Network "%1"? - Wi-Fi ağını unut "%1"? - - - Forget - - - - diff --git a/selfdrive/ui/translations/zh-CHS.ts b/selfdrive/ui/translations/zh-CHS.ts deleted file mode 100644 index 9481e62f10..0000000000 --- a/selfdrive/ui/translations/zh-CHS.ts +++ /dev/null @@ -1,1069 +0,0 @@ - - - - - AbstractAlert - - Close - 关闭 - - - Reboot and Update - 重启并更新 - - - - AdvancedNetworking - - Back - 返回 - - - Enable Tethering - 启用WiFi热点 - - - Tethering Password - WiFi热点密码 - - - EDIT - 编辑 - - - Enter new tethering password - 输入新的WiFi热点密码 - - - IP Address - IP 地址 - - - Enable Roaming - 启用数据漫游 - - - APN Setting - APN 设置 - - - Enter APN - 输入 APN - - - leave blank for automatic configuration - 留空以自动配置 - - - Cellular Metered - 按流量计费的手机移动网络 - - - Hidden Network - 隐藏的网络 - - - CONNECT - 连线 - - - Enter SSID - 输入 SSID - - - Enter password - 输入密码 - - - for "%1" - 网络名称:"%1" - - - Prevent large data uploads when on a metered cellular connection - 在按流量计费的移动网络上,防止上传大数据 - - - default - 默认 - - - metered - 按流量计费 - - - unmetered - 不按流量计费 - - - Wi-Fi Network Metered - 按流量计费的 WLAN 网络 - - - Prevent large data uploads when on a metered Wi-Fi connection - 在按流量计费的 WLAN 网络上,防止上传大数据 - - - - ConfirmationDialog - - Ok - 好的 - - - Cancel - 取消 - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - 您必须接受条款和条件以使用openpilot。 - - - Back - 返回 - - - Decline, uninstall %1 - 拒绝并卸载%1 - - - - DeveloperPanel - - Joystick Debug Mode - 摇杆调试模式 - - - Longitudinal Maneuver Mode - 纵向操控测试模式 - - - openpilot Longitudinal Control (Alpha) - openpilot纵向控制(Alpha 版) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - 警告:此车辆的 openpilot 纵向控制功能目前处于Alpha版本,使用此功能将会停用自动紧急制动(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 纵向控制 Alpha 版本时,建议同时启用实验性模式(Experimental mode)。 - - - Enable ADB - 启用 ADB - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB(Android调试桥接)允许通过USB或网络连接到您的设备。更多信息请参见 [https://docs.comma.ai/how-to/connect-to-comma](https://docs.comma.ai/how-to/connect-to-comma)。 - - - - DevicePanel - - Dongle ID - 设备ID(Dongle ID) - - - N/A - N/A - - - Serial - 序列号 - - - Driver Camera - 驾驶员摄像头 - - - PREVIEW - 预览 - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - 打开并预览驾驶员摄像头,以确保驾驶员监控具有良好视野。(仅熄火时可用) - - - Reset Calibration - 重置设备校准 - - - RESET - 重置 - - - Are you sure you want to reset calibration? - 您确定要重置设备校准吗? - - - Review Training Guide - 新手指南 - - - REVIEW - 查看 - - - Review the rules, features, and limitations of openpilot - 查看 openpilot 的使用规则,以及其功能和限制 - - - Are you sure you want to review the training guide? - 您确定要查看新手指南吗? - - - Regulatory - 监管信息 - - - VIEW - 查看 - - - Change Language - 切换语言 - - - CHANGE - 切换 - - - Select a language - 选择语言 - - - Reboot - 重启 - - - Power Off - 关机 - - - Your device is pointed %1° %2 and %3° %4. - 您的设备校准为%1° %2、%3° %4。 - - - down - 朝下 - - - up - 朝上 - - - left - 朝左 - - - right - 朝右 - - - Are you sure you want to reboot? - 您确定要重新启动吗? - - - Disengage to Reboot - 取消openpilot以重新启动 - - - Are you sure you want to power off? - 您确定要关机吗? - - - Disengage to Power Off - 取消openpilot以关机 - - - Reset - 重置 - - - Review - 预览 - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - 将您的设备与comma connect (connect.comma.ai)配对并领取您的comma prime优惠。 - - - Pair Device - 配对设备 - - - PAIR - 配对 - - - Disengage to Reset Calibration - 解除以重置校准 - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - openpilot 要求设备的安装角度:左右偏移需在 4° 以内,上下俯仰角度需在向上 5° 至向下 9° 的范围内。 - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - openpilot 会持续进行校准,因此很少需要重置。如果车辆电源已开启,重置校准会重新启动 openpilot。 - - - - -Steering lag calibration is %1% complete. - - -转向延迟校准已完成 %1%。 - - - - -Steering lag calibration is complete. - - -转向延迟校准已完成。 - - - Steering torque response calibration is %1% complete. - 转向扭矩响应校准已完成 %1%。 - - - Steering torque response calibration is complete. - 转向扭矩响应校准已完成。 - - - - DriverViewWindow - - camera starting - 正在启动相机 - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - 试验模式运行 - - - CHILL MODE ON - 轻松模式运行 - - - - FirehosePanel - - 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 通过观察人类驾驶(包括您)来学习如何驾驶。 - -“Firehose 模式”允许您最大化上传训练数据,以改进 openpilot 的驾驶模型。更多数据意味着更强大的模型,也就意味着更优秀的“实验模式”。 - - - Firehose Mode: ACTIVE - Firehose 模式:激活中 - - - 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>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> 您的驾驶数据被纳入训练数据集。 - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>闲置</span>:请连接到不限流量的网络 - - - Firehose Mode - Firehose 模式 - - - - HudRenderer - - km/h - km/h - - - mph - mph - - - MAX - 最高定速 - - - - InputDialog - - Cancel - 取消 - - - Need at least %n character(s)! - - 至少需要 %n 个字符! - - - - - MultiOptionDialog - - Select - 选择 - - - Cancel - 取消 - - - - Networking - - Advanced - 高级 - - - Enter password - 输入密码 - - - for "%1" - 网络名称:"%1" - - - Wrong password - 密码错误 - - - - OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - 请立即连接网络检查更新。如果不连接网络,openpilot 将在 %1 后便无法使用 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - 请连接至互联网以检查更新。在连接至互联网并完成更新检查之前,openpilot 将不会自动启动。 - - - Unable to download updates -%1 - 无法下载更新 -%1 - - - Taking camera snapshots. System won't start until finished. - 正在使用相机拍摄中。在完成之前,系统将无法启动。 - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - 一个针对您设备的操作系统更新正在后台下载中。当更新准备好安装时,您将收到提示进行更新。 - - - 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) 未被识别。请提交一个 Pull Request 为您的车辆添加正确的固件版本。需要帮助吗?请加入 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. - openpilot 检测到设备的安装位置发生变化。请确保设备完全安装在支架上,并确保支架牢固地固定在挡风玻璃上。 - - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - 设备温度过高。系统正在冷却中,等冷却完毕后才会启动。目前内部组件温度:%1 - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - 设备未能注册到 comma.ai 后端。该设备将无法连接或上传数据到 comma.ai 服务器,也无法获得 comma.ai 的支持。如果该设备是在 comma.ai/shop 购买的,请访问 https://comma.ai/support 提交工单。 - - - Acknowledge Excessive Actuation - 确认过度作动 - - - Snooze Update - 推迟更新 - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - openpilot 在您上一次驾驶中,检测到过度的 %1 作动。请访问 https://comma.ai/support 联系客服,并提供您设备的 Dongle ID 以便进行故障排查。 - - - - OffroadHome - - UPDATE - 更新 - - - ALERTS - 警报 - - - ALERT - 警报 - - - - OnroadAlerts - - openpilot Unavailable - 无法使用 openpilot - - - TAKE CONTROL IMMEDIATELY - 立即接管 - - - Reboot Device - 重启设备 - - - Waiting to start - 等待开始 - - - System Unresponsive - 系统无响应 - - - - PairingPopup - - Pair your device to your comma account - 将您的设备与comma账号配对 - - - Go to https://connect.comma.ai on your phone - 在手机上访问 https://connect.comma.ai - - - Click "add new device" and scan the QR code on the right - 点击“添加新设备”,扫描右侧二维码 - - - Bookmark connect.comma.ai to your home screen to use it like an app - 将 connect.comma.ai 收藏到您的主屏幕,以便像应用程序一样使用它 - - - Please connect to Wi-Fi to complete initial pairing - 请连接 Wi-Fi 以完成初始配对 - - - - ParamControl - - Cancel - 取消 - - - Enable - 启用 - - - - PrimeAdWidget - - Upgrade Now - 现在升级 - - - Become a comma prime member at connect.comma.ai - 打开connect.comma.ai以注册comma prime会员 - - - PRIME FEATURES: - comma prime特权: - - - Remote access - 远程访问 - - - 24/7 LTE connectivity - 全天候 LTE 连接 - - - 1 year of drive storage - 一年的行驶记录储存空间 - - - Remote snapshots - 远程快照 - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ 已订阅 - - - comma prime - comma prime - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - %n 分钟前 - - - - %n hour(s) ago - - %n 小时前 - - - - %n day(s) ago - - %n 天前 - - - - now - 现在 - - - - SettingsWindow - - × - × - - - Device - 设备 - - - Network - 网络 - - - Toggles - 设定 - - - Software - 软件 - - - Developer - 开发人员 - - - Firehose - Firehose - - - - SetupWidget - - Finish Setup - 完成设置 - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - 将您的设备与comma connect (connect.comma.ai)配对并领取您的comma prime优惠。 - - - Pair device - 配对设备 - - - - Sidebar - - CONNECT - CONNECT - - - OFFLINE - 离线 - - - ONLINE - 在线 - - - ERROR - 连接出错 - - - TEMP - 设备温度 - - - HIGH - 过热 - - - GOOD - 良好 - - - OK - 一般 - - - VEHICLE - 车辆连接 - - - NO - - - - PANDA - PANDA - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - 以太网 - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Updates are only downloaded while the car is off. - 车辆熄火时才能下载升级文件。 - - - Current Version - 当前版本 - - - Download - 下载 - - - Install Update - 安装更新 - - - INSTALL - 安装 - - - Target Branch - 目标分支 - - - SELECT - 选择 - - - Select a branch - 选择分支 - - - UNINSTALL - 卸载 - - - Uninstall %1 - 卸载 %1 - - - Are you sure you want to uninstall? - 您确定要卸载吗? - - - CHECK - 查看 - - - Uninstall - 卸载 - - - failed to check for update - 检查更新失败 - - - up to date, last checked %1 - 已经是最新版本,上次检查时间为 %1 - - - DOWNLOAD - 下载 - - - update available - 有可用的更新 - - - never - 从未更新 - - - - SshControl - - SSH Keys - SSH密钥 - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - 警告:这将授予SSH访问权限给您GitHub设置中的所有公钥。切勿输入您自己以外的GitHub用户名。comma员工永远不会要求您添加他们的GitHub用户名。 - - - ADD - 添加 - - - Enter your GitHub username - 输入您的GitHub用户名 - - - LOADING - 正在加载 - - - REMOVE - 删除 - - - Username '%1' has no keys on GitHub - 用户名“%1”在GitHub上没有密钥 - - - Request timed out - 请求超时 - - - Username '%1' doesn't exist on GitHub - GitHub上不存在用户名“%1” - - - - SshToggle - - Enable SSH - 启用SSH - - - - TermsPage - - Decline - 拒绝 - - - Agree - 同意 - - - Welcome to openpilot - 欢迎使用 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. - 您必须接受《条款与条件》才能使用 openpilot。在继续之前,请先阅读最新条款:<span style='color: #465BEA;'>https://comma.ai/terms</span>。 - - - - TogglesPanel - - Enable openpilot - 启用openpilot - - - Enable Lane Departure Warnings - 启用车道偏离警告 - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - 车速超过31mph(50km/h)时,若检测到车辆越过车道线且未打转向灯,系统将发出警告以提醒您返回车道。 - - - Use Metric System - 使用公制单位 - - - Display speed in km/h instead of mph. - 显示车速时,以km/h代替mph。 - - - Record and Upload Driver Camera - 录制并上传驾驶员摄像头 - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - 上传驾驶员摄像头的数据,帮助改进驾驶员监控算法。 - - - Disengage on Accelerator Pedal - 踩油门时取消控制 - - - When enabled, pressing the accelerator pedal will disengage openpilot. - 启用后,踩下油门踏板将取消openpilot。 - - - Experimental Mode - 测试模式 - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot 默认 <b>轻松模式</b>驾驶车辆。试验模式启用一些轻松模式之外的 <b>试验性功能</b>。试验性功能包括: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - 允许驾驶模型控制加速和制动,openpilot将模仿人类驾驶车辆,包括在红灯和停车让行标识前停车。鉴于驾驶模型确定行驶车速,所设定的车速仅作为上限。此功能尚处于早期测试状态,有可能会出现操作错误。 - - - New Driving Visualization - 新驾驶视角 - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - 由于此车辆使用自带的ACC纵向控制,当前无法使用试验模式。 - - - openpilot longitudinal control may come in a future update. - openpilot纵向控制可能会在未来的更新中提供。 - - - Aggressive - 积极 - - - Standard - 标准 - - - Relaxed - 舒适 - - - Driving Personality - 驾驶风格 - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - 在正式(release)版本以外的分支上,可以测试 openpilot 纵向控制的 Alpha 版本以及实验模式。 - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - 启用 openpilot 纵向控制(alpha)开关以允许实验模式。 - - - End-to-End Longitudinal Control - 端到端纵向控制 - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - 推荐使用标准模式。在积极模式下,openpilot 会更靠近前方车辆,并在油门和刹车方面更加激进。在放松模式下,openpilot 会与前方车辆保持更远距离。在支持的车型上,你可以使用方向盘上的距离按钮来循环切换这些驾驶风格。 - - - 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 - 驾驶员监控常开 - - - Enable driver monitoring even when openpilot is not engaged. - 即使在openpilot未激活时也启用驾驶员监控。 - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - openpilot 系统提供“自适应巡航”和“车道保持”驾驶辅助功能。使用此功能时,您需要时刻保持专注。 - - - Changing this setting will restart openpilot if the car is powered on. - 如果车辆已通电,更改此设置将会重新启动 openpilot。 - - - Record and Upload Microphone Audio - 录制并上传麦克风音频 - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - 在驾驶时录制并存储麦克风音频。该音频将会包含在 comma connect 的行车记录仪视频中。 - - - - WiFiPromptWidget - - Open - 开启 - - - 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> Firehose 模式 <span style='font-family: Noto Color Emoji;'>🔥</span> - - - - WifiUI - - Scanning for networks... - 正在扫描网络…… - - - CONNECTING... - 正在连接…… - - - FORGET - 忽略 - - - Forget Wi-Fi Network "%1"? - 忽略WiFi网络 "%1"? - - - Forget - 忽略 - - - diff --git a/selfdrive/ui/translations/zh-CHT.ts b/selfdrive/ui/translations/zh-CHT.ts deleted file mode 100644 index c3ef55d3d9..0000000000 --- a/selfdrive/ui/translations/zh-CHT.ts +++ /dev/null @@ -1,1069 +0,0 @@ - - - - - AbstractAlert - - Close - 關閉 - - - Reboot and Update - 重啟並更新 - - - - AdvancedNetworking - - Back - 回上頁 - - - Enable Tethering - 啟用網路分享 - - - Tethering Password - 網路分享密碼 - - - EDIT - 編輯 - - - Enter new tethering password - 輸入新的網路分享密碼 - - - IP Address - IP 地址 - - - Enable Roaming - 啟用漫遊 - - - APN Setting - APN 設置 - - - Enter APN - 輸入 APN - - - leave blank for automatic configuration - 留空白將自動配置 - - - Cellular Metered - 計費的行動網路 - - - Hidden Network - 隱藏的網路 - - - CONNECT - 連線 - - - Enter SSID - 輸入 SSID - - - Enter password - 輸入密碼 - - - for "%1" - 給 "%1" - - - Prevent large data uploads when on a metered cellular connection - 在使用計費行動網路時,防止上傳大量數據 - - - default - 預設 - - - metered - 計費 - - - unmetered - 非計費 - - - Wi-Fi Network Metered - 計費 Wi-Fi 網路 - - - Prevent large data uploads when on a metered Wi-Fi connection - 在使用計費 Wi-Fi 網路時,防止上傳大量數據 - - - - ConfirmationDialog - - Ok - 確定 - - - Cancel - 取消 - - - - DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - 您必須先接受條款和條件才能使用 openpilot。 - - - Back - 回上頁 - - - Decline, uninstall %1 - 拒絕並解除安裝 %1 - - - - DeveloperPanel - - Joystick Debug Mode - 搖桿調試模式 - - - Longitudinal Maneuver Mode - 縱向操控測試模式 - - - openpilot Longitudinal Control (Alpha) - openpilot 縱向控制 (Alpha 版) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - 警告:此車輛的 openpilot 縱向控制功能目前處於 Alpha 版本,使用此功能將會停用自動緊急煞車(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 縱向控制 Alpha 版本時,建議同時啟用實驗性模式(Experimental mode)。 - - - Enable ADB - 啟用 ADB - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB(Android 調試橋接)允許通過 USB 或網絡連接到您的設備。更多信息請參見 [https://docs.comma.ai/how-to/connect-to-comma](https://docs.comma.ai/how-to/connect-to-comma)。 - - - - DevicePanel - - Dongle ID - Dongle ID - - - N/A - 無法使用 - - - Serial - 序號 - - - Driver Camera - 駕駛員監控鏡頭 - - - PREVIEW - 預覽 - - - Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - 預覽駕駛員監控鏡頭畫面,以確保其具有良好視野。(僅在熄火時可用) - - - Reset Calibration - 重設校準 - - - RESET - 重設 - - - Are you sure you want to reset calibration? - 您確定要重設校準嗎? - - - Review Training Guide - 觀看使用教學 - - - REVIEW - 觀看 - - - Review the rules, features, and limitations of openpilot - 觀看 openpilot 的使用規則、功能和限制 - - - Are you sure you want to review the training guide? - 您確定要觀看使用教學嗎? - - - Regulatory - 法規/監管 - - - VIEW - 觀看 - - - Change Language - 更改語言 - - - CHANGE - 更改 - - - Select a language - 選擇語言 - - - Reboot - 重新啟動 - - - Power Off - 關機 - - - Your device is pointed %1° %2 and %3° %4. - 你的裝置目前朝%2 %1° 以及朝%4 %3° 。 - - - down - - - - up - - - - left - - - - right - - - - Are you sure you want to reboot? - 您確定要重新啟動嗎? - - - Disengage to Reboot - 請先取消控車才能重新啟動 - - - Are you sure you want to power off? - 您確定您要關機嗎? - - - Disengage to Power Off - 請先取消控車才能關機 - - - Reset - 重設 - - - Review - 回顧 - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - 將您的裝置與 comma connect (connect.comma.ai) 配對並領取您的 comma 高級會員優惠。 - - - Pair Device - 配對裝置 - - - PAIR - 配對 - - - Disengage to Reset Calibration - 解除以重設校準 - - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - openpilot 要求裝置的安裝角度,左右偏移須在 4° 以內,上下角度則須介於仰角 5° 至俯角 9° 之間。 - - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. - openpilot 會持續進行校準,因此很少需要重設。若車輛電源開啟,重設校準將會重新啟動 openpilot。 - - - - -Steering lag calibration is %1% complete. - - -轉向延遲校準已完成 %1%。 - - - - -Steering lag calibration is complete. - - -轉向延遲校準已完成。 - - - Steering torque response calibration is %1% complete. - 轉向扭矩反應校準已完成 %1%。 - - - Steering torque response calibration is complete. - 轉向扭矩反應校準已完成。 - - - - DriverViewWindow - - camera starting - 開啟相機中 - - - - ExperimentalModeButton - - EXPERIMENTAL MODE ON - 實驗模式 ON - - - CHILL MODE ON - 輕鬆模式 ON - - - - FirehosePanel - - 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 透過觀察人類駕駛(包括您)來學習如何駕駛。 - -「Firehose 模式」可讓您最大化上傳訓練數據,以改進 openpilot 的駕駛模型。更多數據代表更強大的模型,也就意味著更優秀的「實驗模式」。 - - - Firehose Mode: ACTIVE - Firehose 模式:啟用中 - - - 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>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>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> 您的駕駛數據被納入訓練資料集。 - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>閒置中</span>:請連接到不按流量計費的網絡 - - - Firehose Mode - Firehose 模式 - - - - HudRenderer - - km/h - km/h - - - mph - mph - - - MAX - 最高 - - - - InputDialog - - Cancel - 取消 - - - Need at least %n character(s)! - - 需要至少 %n 個字元! - - - - - MultiOptionDialog - - Select - 選擇 - - - Cancel - 取消 - - - - Networking - - Advanced - 進階 - - - Enter password - 輸入密碼 - - - for "%1" - 給 "%1" - - - Wrong password - 密碼錯誤 - - - - OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - 請立即連接網路檢查更新。如果不連接網路,openpilot 將在 %1 後便無法使用 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - 請連接至網際網路以檢查更新。在連接至網際網路並完成更新檢查之前,openpilot 將不會自動啟動。 - - - Unable to download updates -%1 - 無法下載更新 -%1 - - - Taking camera snapshots. System won't start until finished. - 正在使用相機拍攝中。在完成之前,系統將無法啟動。 - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - 一個有關操作系統的更新正在後台下載中。當更新準備好安裝時,您將收到提示進行更新。 - - - 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) 未被識別。請提交一個 Pull Request 為您的車輛添加正確的韌體版本。需要幫助嗎?請加入 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. - openpilot 偵測到裝置的安裝位置發生變化。請確保裝置完全安裝在支架上,並確保支架牢固地固定在擋風玻璃上。 - - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - 裝置溫度過高。系統正在冷卻中,等冷卻完畢後才會啟動。目前內部組件溫度:%1 - - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. - 裝置註冊 comma.ai 後端失敗。此裝置將無法連線或上傳資料至 comma.ai 伺服器,也無法獲得 comma.ai 的支援。若此裝置購自 comma.ai/shop,請至 https://comma.ai/support 建立支援請求。 - - - Acknowledge Excessive Actuation - 確認過度作動 - - - Snooze Update - 延後更新 - - - openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - openpilot 在您上次的駕駛中,偵測到過度的 %1 作動。請至 https://comma.ai/support 聯絡客服,並提供您裝置的 Dongle ID 以進行故障排除。 - - - - OffroadHome - - UPDATE - 更新 - - - ALERTS - 提醒 - - - ALERT - 提醒 - - - - OnroadAlerts - - openpilot Unavailable - 無法使用 openpilot - - - TAKE CONTROL IMMEDIATELY - 立即接管 - - - Reboot Device - 請重新啟裝置 - - - Waiting to start - 等待開始 - - - System Unresponsive - 系統無回應 - - - - PairingPopup - - Pair your device to your comma account - 將裝置與您的 comma 帳號配對 - - - Go to https://connect.comma.ai on your phone - 用手機連至 https://connect.comma.ai - - - Click "add new device" and scan the QR code on the right - 點選 "add new device" 後掃描右邊的二維碼 - - - Bookmark connect.comma.ai to your home screen to use it like an app - 將 connect.comma.ai 加入您的主螢幕,以便像手機 App 一樣使用它 - - - Please connect to Wi-Fi to complete initial pairing - 請連接 Wi-Fi 以完成初始配對 - - - - ParamControl - - Cancel - 取消 - - - Enable - 啟用 - - - - PrimeAdWidget - - Upgrade Now - 馬上升級 - - - Become a comma prime member at connect.comma.ai - 成為 connect.comma.ai 的高級會員 - - - PRIME FEATURES: - 高級會員特點: - - - Remote access - 遠端存取 - - - 24/7 LTE connectivity - 24/7 LTE 連線 - - - 1 year of drive storage - 一年的行駛記錄儲存空間 - - - Remote snapshots - 遠端快照 - - - - PrimeUserWidget - - ✓ SUBSCRIBED - ✓ 已訂閱 - - - comma prime - comma 高級會員 - - - - QObject - - openpilot - openpilot - - - %n minute(s) ago - - %n 分鐘前 - - - - %n hour(s) ago - - %n 小時前 - - - - %n day(s) ago - - %n 天前 - - - - now - 現在 - - - - SettingsWindow - - × - × - - - Device - 裝置 - - - Network - 網路 - - - Toggles - 設定 - - - Software - 軟體 - - - Developer - 開發人員 - - - Firehose - Firehose - - - - SetupWidget - - Finish Setup - 完成設置 - - - Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - 將您的裝置與 comma connect (connect.comma.ai) 配對並領取您的 comma 高級會員優惠。 - - - Pair device - 配對裝置 - - - - Sidebar - - CONNECT - 雲端服務 - - - OFFLINE - 已離線 - - - ONLINE - 已連線 - - - ERROR - 錯誤 - - - TEMP - 溫度 - - - HIGH - 偏高 - - - GOOD - 正常 - - - OK - 一般 - - - VEHICLE - 車輛通訊 - - - NO - 未連線 - - - PANDA - 車輛通訊 - - - -- - -- - - - Wi-Fi - Wi-Fi - - - ETH - ETH - - - 2G - 2G - - - 3G - 3G - - - LTE - LTE - - - 5G - 5G - - - - SoftwarePanel - - Updates are only downloaded while the car is off. - 系統更新只會在熄火時下載。 - - - Current Version - 當前版本 - - - Download - 下載 - - - Install Update - 安裝更新 - - - INSTALL - 安裝 - - - Target Branch - 目標分支 - - - SELECT - 選取 - - - Select a branch - 選取一個分支 - - - UNINSTALL - 解除安裝 - - - Uninstall %1 - 解除安裝 %1 - - - Are you sure you want to uninstall? - 您確定您要解除安裝嗎? - - - CHECK - 檢查 - - - Uninstall - 解除安裝 - - - failed to check for update - 檢查更新失敗 - - - up to date, last checked %1 - 已經是最新版本,上次檢查時間為 %1 - - - DOWNLOAD - 下載 - - - update available - 有可用的更新 - - - never - 從未更新 - - - - SshControl - - SSH Keys - SSH 金鑰 - - - Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - 警告:這將授權給 GitHub 帳號中所有公鑰 SSH 訪問權限。切勿輸入非您自己的 GitHub 使用者名稱。comma 員工「永遠不會」要求您添加他們的 GitHub 使用者名稱。 - - - ADD - 新增 - - - Enter your GitHub username - 請輸入您 GitHub 的使用者名稱 - - - LOADING - 載入中 - - - REMOVE - 移除 - - - Username '%1' has no keys on GitHub - GitHub 用戶 '%1' 沒有設定任何金鑰 - - - Request timed out - 請求超時 - - - Username '%1' doesn't exist on GitHub - GitHub 用戶 '%1' 不存在 - - - - SshToggle - - Enable SSH - 啟用 SSH 服務 - - - - TermsPage - - Decline - 拒絕 - - - Agree - 接受 - - - Welcome to openpilot - 歡迎使用 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. - 您必須接受《條款與條件》才能使用 openpilot。在繼續之前,請先閱讀最新條款:<span style='color: #465BEA;'>https://comma.ai/terms</span>。 - - - - TogglesPanel - - Enable openpilot - 啟用 openpilot - - - Enable Lane Departure Warnings - 啟用車道偏離警告 - - - Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - 車速在時速 50 公里 (31 英里) 以上且未打方向燈的情況下,如果偵測到車輛駛出目前車道線時,發出車道偏離警告。 - - - Use Metric System - 使用公制單位 - - - Display speed in km/h instead of mph. - 啟用後,速度單位顯示將從 mp/h 改為 km/h。 - - - Record and Upload Driver Camera - 記錄並上傳駕駛監控影像 - - - Upload data from the driver facing camera and help improve the driver monitoring algorithm. - 上傳駕駛監控的錄影來協助我們提升駕駛監控的準確率。 - - - Disengage on Accelerator Pedal - 油門取消控車 - - - When enabled, pressing the accelerator pedal will disengage openpilot. - 啟用後,踩踏油門將會取消 openpilot 控制。 - - - Experimental Mode - 實驗模式 - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot 預設以 <b>輕鬆模式</b> 駕駛。 實驗模式啟用了尚未準備好進入輕鬆模式的 <b>alpha 級功能</b>。實驗功能如下: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - 讓駕駛模型來控制油門及煞車。openpilot將會模擬人類的駕駛行為,包含在看見紅燈及停止標示時停車。由於車速將由駕駛模型決定,因此您設定的時速將成為速度上限。本功能仍在早期實驗階段,請預期模型有犯錯的可能性。 - - - New Driving Visualization - 新的駕駛視覺介面 - - - Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - 因車輛使用內建ACC系統,無法在本車輛上啟動實驗模式。 - - - openpilot longitudinal control may come in a future update. - openpilot 縱向控制可能會在未來的更新中提供。 - - - Aggressive - 積極 - - - Standard - 標準 - - - Relaxed - 舒適 - - - Driving Personality - 駕駛風格 - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - 在正式 (release) 版以外的分支上可以測試 openpilot 縱向控制的 Alpha 版本以及實驗模式。 - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - 啟用 openpilot 縱向控制(alpha)切換以允許實驗模式。 - - - End-to-End Longitudinal Control - 端到端縱向控制 - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - 建議使用標準模式。在積極模式下,openpilot 會更接近前車並更積極地使用油門和剎車。在輕鬆模式下,openpilot 會與前車保持較遠距離。對於支援的汽車,您可以使用方向盤上的距離按鈕來切換這些駕駛風格。 - - - 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 - 駕駛監控常開 - - - Enable driver monitoring even when openpilot is not engaged. - 即使在openpilot未激活時也啟用駕駛監控。 - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - openpilot 系統提供「主動式巡航」與「車道維持」等駕駛輔助功能。使用這些功能時,您必須隨時保持專注。 - - - Changing this setting will restart openpilot if the car is powered on. - 若車輛電源為開啟狀態,變更此設定將會重新啟動 openpilot。 - - - Record and Upload Microphone Audio - 錄製並上傳麥克風音訊 - - - Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - 在駕駛時錄製並儲存麥克風音訊。此音訊將會收錄在 comma connect 的行車記錄器影片中。 - - - - WiFiPromptWidget - - Open - 開啟 - - - 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> Firehose 模式 <span style='font-family: Noto Color Emoji;'>🔥</span> - - - - WifiUI - - Scanning for networks... - 掃描無線網路中... - - - CONNECTING... - 連線中... - - - FORGET - 清除 - - - Forget Wi-Fi Network "%1"? - 清除 Wi-Fi 網路 "%1"? - - - Forget - 清除 - - - diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc deleted file mode 100644 index ed851a41c8..0000000000 --- a/selfdrive/ui/ui.cc +++ /dev/null @@ -1,199 +0,0 @@ -#include "selfdrive/ui/ui.h" - -#include -#include - -#include - -#include "common/transformations/orientation.hpp" -#include "common/swaglog.h" -#include "common/util.h" -#include "system/hardware/hw.h" - -#define BACKLIGHT_DT 0.05 -#define BACKLIGHT_TS 10.00 - -static void update_sockets(UIState *s) { - s->sm->update(0); -} - -static void update_state(UIState *s) { - SubMaster &sm = *(s->sm); - UIScene &scene = s->scene; - - if (sm.updated("liveCalibration")) { - auto list2rot = [](const capnp::List::Reader &rpy_list) ->Eigen::Matrix3f { - return euler2rot({rpy_list[0], rpy_list[1], rpy_list[2]}).cast(); - }; - - auto live_calib = sm["liveCalibration"].getLiveCalibration(); - if (live_calib.getCalStatus() == cereal::LiveCalibrationData::Status::CALIBRATED) { - auto device_from_calib = list2rot(live_calib.getRpyCalib()); - auto wide_from_device = list2rot(live_calib.getWideFromDeviceEuler()); - s->scene.view_from_calib = VIEW_FROM_DEVICE * device_from_calib; - s->scene.view_from_wide_calib = VIEW_FROM_DEVICE * wide_from_device * device_from_calib; - } else { - s->scene.view_from_calib = s->scene.view_from_wide_calib = VIEW_FROM_DEVICE; - } - } - if (sm.updated("pandaStates")) { - auto pandaStates = sm["pandaStates"].getPandaStates(); - if (pandaStates.size() > 0) { - scene.pandaType = pandaStates[0].getPandaType(); - - if (scene.pandaType != cereal::PandaState::PandaType::UNKNOWN) { - scene.ignition = false; - for (const auto& pandaState : pandaStates) { - scene.ignition |= pandaState.getIgnitionLine() || pandaState.getIgnitionCan(); - } - } - } - } else if ((s->sm->frame - s->sm->rcv_frame("pandaStates")) > 5*UI_FREQ) { - scene.pandaType = cereal::PandaState::PandaType::UNKNOWN; - } - if (sm.updated("wideRoadCameraState")) { - auto cam_state = sm["wideRoadCameraState"].getWideRoadCameraState(); - scene.light_sensor = std::max(100.0f - cam_state.getExposureValPercent(), 0.0f); - } else if (!sm.allAliveAndValid({"wideRoadCameraState"})) { - scene.light_sensor = -1; - } - scene.started = sm["deviceState"].getDeviceState().getStarted() && scene.ignition; - - auto params = Params(); - scene.recording_audio = params.getBool("RecordAudio") && scene.started; -} - -void ui_update_params(UIState *s) { - auto params = Params(); - s->scene.is_metric = params.getBool("IsMetric"); -} - -void UIState::updateStatus() { - if (scene.started && sm->updated("selfdriveState")) { - auto ss = (*sm)["selfdriveState"].getSelfdriveState(); - auto state = ss.getState(); - if (state == cereal::SelfdriveState::OpenpilotState::PRE_ENABLED || state == cereal::SelfdriveState::OpenpilotState::OVERRIDING) { - status = STATUS_OVERRIDE; - } else { - status = ss.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED; - } - } - - if (engaged() != engaged_prev) { - engaged_prev = engaged(); - emit engagedChanged(engaged()); - } - - // Handle onroad/offroad transition - if (scene.started != started_prev || sm->frame == 1) { - if (scene.started) { - status = STATUS_DISENGAGED; - scene.started_frame = sm->frame; - } - started_prev = scene.started; - emit offroadTransition(!scene.started); - } -} - -UIState::UIState(QObject *parent) : QObject(parent) { - sm = std::make_unique(std::vector{ - "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", - "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", - "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan", - }); - prime_state = new PrimeState(this); - language = QString::fromStdString(Params().get("LanguageSetting")); - - // update timer - timer = new QTimer(this); - QObject::connect(timer, &QTimer::timeout, this, &UIState::update); - timer->start(1000 / UI_FREQ); -} - -void UIState::update() { - update_sockets(this); - update_state(this); - updateStatus(); - - emit uiUpdate(*this); -} - -Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) { - setAwake(true); - resetInteractiveTimeout(); - - QObject::connect(uiState(), &UIState::uiUpdate, this, &Device::update); -} - -void Device::update(const UIState &s) { - updateBrightness(s); - updateWakefulness(s); -} - -void Device::setAwake(bool on) { - if (on != awake) { - awake = on; - Hardware::set_display_power(awake); - LOGD("setting display power %d", awake); - emit displayPowerChanged(awake); - } -} - -void Device::resetInteractiveTimeout(int timeout) { - if (timeout == -1) { - timeout = (ignition_on ? 10 : 30); - } - interactive_timeout = timeout * UI_FREQ; -} - -void Device::updateBrightness(const UIState &s) { - float clipped_brightness = offroad_brightness; - if (s.scene.started && s.scene.light_sensor >= 0) { - clipped_brightness = s.scene.light_sensor; - - // CIE 1931 - https://www.photonstophotos.net/GeneralTopics/Exposure/Psychometric_Lightness_and_Gamma.htm - if (clipped_brightness <= 8) { - clipped_brightness = (clipped_brightness / 903.3); - } else { - clipped_brightness = std::pow((clipped_brightness + 16.0) / 116.0, 3.0); - } - - // Scale back to 10% to 100% - clipped_brightness = std::clamp(100.0f * clipped_brightness, 10.0f, 100.0f); - } - - int brightness = brightness_filter.update(clipped_brightness); - if (!awake) { - brightness = 0; - } - - if (brightness != last_brightness) { - if (!brightness_future.isRunning()) { - brightness_future = QtConcurrent::run(Hardware::set_brightness, brightness); - last_brightness = brightness; - } - } -} - -void Device::updateWakefulness(const UIState &s) { - bool ignition_just_turned_off = !s.scene.ignition && ignition_on; - ignition_on = s.scene.ignition; - - if (ignition_just_turned_off) { - resetInteractiveTimeout(); - } else if (interactive_timeout > 0 && --interactive_timeout == 0) { - emit interactiveTimeout(); - } - - setAwake(s.scene.ignition || interactive_timeout > 0); -} - -UIState *uiState() { - static UIState ui_state; - return &ui_state; -} - -Device *device() { - static Device _device; - return &_device; -} diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h deleted file mode 100644 index b3c482aafe..0000000000 --- a/selfdrive/ui/ui.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include - -#include "cereal/messaging/messaging.h" -#include "common/mat.h" -#include "common/params.h" -#include "common/util.h" -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/prime_state.h" - -const int UI_BORDER_SIZE = 30; -const int UI_HEADER_HEIGHT = 420; - -const int UI_FREQ = 20; // Hz -const int BACKLIGHT_OFFROAD = 50; - -const Eigen::Matrix3f VIEW_FROM_DEVICE = (Eigen::Matrix3f() << - 0.0, 1.0, 0.0, - 0.0, 0.0, 1.0, - 1.0, 0.0, 0.0).finished(); - -const Eigen::Matrix3f FCAM_INTRINSIC_MATRIX = (Eigen::Matrix3f() << - 2648.0, 0.0, 1928.0 / 2, - 0.0, 2648.0, 1208.0 / 2, - 0.0, 0.0, 1.0).finished(); - -// tici ecam focal probably wrong? magnification is not consistent across frame -// Need to retrain model before this can be changed -const Eigen::Matrix3f ECAM_INTRINSIC_MATRIX = (Eigen::Matrix3f() << - 567.0, 0.0, 1928.0 / 2, - 0.0, 567.0, 1208.0 / 2, - 0.0, 0.0, 1.0).finished(); - -typedef enum UIStatus { - STATUS_DISENGAGED, - STATUS_OVERRIDE, - STATUS_ENGAGED, -} UIStatus; - -const QColor bg_colors [] = { - [STATUS_DISENGAGED] = QColor(0x17, 0x33, 0x49, 0xc8), - [STATUS_OVERRIDE] = QColor(0x91, 0x9b, 0x95, 0xf1), - [STATUS_ENGAGED] = QColor(0x17, 0x86, 0x44, 0xf1), -}; - -typedef struct UIScene { - Eigen::Matrix3f view_from_calib = VIEW_FROM_DEVICE; - Eigen::Matrix3f view_from_wide_calib = VIEW_FROM_DEVICE; - cereal::PandaState::PandaType pandaType; - - cereal::LongitudinalPersonality personality; - - float light_sensor = -1; - bool started, ignition, is_metric, recording_audio; - uint64_t started_frame; -} UIScene; - -class UIState : public QObject { - Q_OBJECT - -public: - UIState(QObject* parent = 0); - void updateStatus(); - inline bool engaged() const { - return scene.started && (*sm)["selfdriveState"].getSelfdriveState().getEnabled(); - } - - std::unique_ptr sm; - UIStatus status; - UIScene scene = {}; - QString language; - PrimeState *prime_state; - -signals: - void uiUpdate(const UIState &s); - void offroadTransition(bool offroad); - void engagedChanged(bool engaged); - -private slots: - void update(); - -private: - QTimer *timer; - bool started_prev = false; - bool engaged_prev = false; -}; - -UIState *uiState(); - -// device management class -class Device : public QObject { - Q_OBJECT - -public: - Device(QObject *parent = 0); - bool isAwake() { return awake; } - void setOffroadBrightness(int brightness) { - offroad_brightness = std::clamp(brightness, 0, 100); - } - -private: - bool awake = false; - int interactive_timeout = 0; - bool ignition_on = false; - - int offroad_brightness = BACKLIGHT_OFFROAD; - int last_brightness = 0; - FirstOrderFilter brightness_filter; - QFuture brightness_future; - - void updateBrightness(const UIState &s); - void updateWakefulness(const UIState &s); - void setAwake(bool on); - -signals: - void displayPowerChanged(bool on); - void interactiveTimeout(); - -public slots: - void resetInteractiveTimeout(int timeout = -1); - void update(const UIState &s); -}; - -Device *device(); -void ui_update_params(UIState *s); diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py index 643d246012..e91820f242 100755 --- a/selfdrive/ui/update_translations.py +++ b/selfdrive/ui/update_translations.py @@ -1,48 +1,38 @@ #!/usr/bin/env python3 -import argparse -import json +from itertools import chain import os - -from openpilot.common.basedir import BASEDIR -from openpilot.system.ui.lib.multilang import UI_DIR, TRANSLATIONS_DIR, LANGUAGES_FILE - -TRANSLATIONS_INCLUDE_FILE = os.path.join(TRANSLATIONS_DIR, "alerts_generated.h") -PLURAL_ONLY = ["en"] # base language, only create entries for strings with plural forms +from openpilot.system.ui.lib.multilang import SYSTEM_UI_DIR, UI_DIR, TRANSLATIONS_DIR, multilang -def generate_translations_include(): - # offroad alerts - # TODO translate events from openpilot.selfdrive/controls/lib/events.py - content = "// THIS IS AN AUTOGENERATED FILE, PLEASE EDIT alerts_offroad.json\n" - with open(os.path.join(BASEDIR, "selfdrive/selfdrived/alerts_offroad.json")) as f: - for alert in json.load(f).values(): - content += f'QT_TRANSLATE_NOOP("OffroadAlert", R"({alert["text"]})");\n' +def update_translations(): + files = [] + for root, _, filenames in chain(os.walk(SYSTEM_UI_DIR), + os.walk(os.path.join(UI_DIR, "widgets")), + os.walk(os.path.join(UI_DIR, "layouts")), + os.walk(os.path.join(UI_DIR, "onroad"))): + for filename in filenames: + if filename.endswith(".py"): + files.append(os.path.join(root, filename)) - with open(TRANSLATIONS_INCLUDE_FILE, "w") as f: - f.write(content) + # Create main translation file + cmd = ("xgettext -L Python --keyword=tr --keyword=trn:1,2 --keyword=tr_noop --from-code=UTF-8 " + + "--flag=tr:1:python-brace-format --flag=trn:1:python-brace-format --flag=trn:2:python-brace-format " + + "-o translations/app.pot {}").format(" ".join(files)) + ret = os.system(cmd) + assert ret == 0 -def update_translations(vanish: bool = False, translation_files: None | list[str] = None, translations_dir: str = TRANSLATIONS_DIR): - if translation_files is None: - with open(LANGUAGES_FILE) as f: - translation_files = json.load(f).values() - - for file in translation_files: - tr_file = os.path.join(translations_dir, f"{file}.ts") - args = f"lupdate -locations none -recursive {UI_DIR} -ts {tr_file} -I {BASEDIR}" - if vanish: - args += " -no-obsolete" - if file in PLURAL_ONLY: - args += " -pluralonly" - ret = os.system(args) - assert ret == 0 + # Generate/update translation files for each language + for name in multilang.languages.values(): + if os.path.exists(os.path.join(TRANSLATIONS_DIR, f"app_{name}.po")): + cmd = f"msgmerge --update --no-fuzzy-matching --backup=none --sort-output translations/app_{name}.po translations/app.pot" + ret = os.system(cmd) + assert ret == 0 + else: + cmd = f"msginit -l {name} --no-translator --input translations/app.pot --output-file translations/app_{name}.po" + ret = os.system(cmd) + assert ret == 0 if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Update translation files for UI", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("--vanish", action="store_true", help="Remove translations with source text no longer found") - args = parser.parse_args() - - generate_translations_include() - update_translations(args.vanish) + update_translations() diff --git a/selfdrive/ui/update_translations_raylib.py b/selfdrive/ui/update_translations_raylib.py deleted file mode 100755 index e91820f242..0000000000 --- a/selfdrive/ui/update_translations_raylib.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -from itertools import chain -import os -from openpilot.system.ui.lib.multilang import SYSTEM_UI_DIR, UI_DIR, TRANSLATIONS_DIR, multilang - - -def update_translations(): - files = [] - for root, _, filenames in chain(os.walk(SYSTEM_UI_DIR), - os.walk(os.path.join(UI_DIR, "widgets")), - os.walk(os.path.join(UI_DIR, "layouts")), - os.walk(os.path.join(UI_DIR, "onroad"))): - for filename in filenames: - if filename.endswith(".py"): - files.append(os.path.join(root, filename)) - - # Create main translation file - cmd = ("xgettext -L Python --keyword=tr --keyword=trn:1,2 --keyword=tr_noop --from-code=UTF-8 " + - "--flag=tr:1:python-brace-format --flag=trn:1:python-brace-format --flag=trn:2:python-brace-format " + - "-o translations/app.pot {}").format(" ".join(files)) - - ret = os.system(cmd) - assert ret == 0 - - # Generate/update translation files for each language - for name in multilang.languages.values(): - if os.path.exists(os.path.join(TRANSLATIONS_DIR, f"app_{name}.po")): - cmd = f"msgmerge --update --no-fuzzy-matching --backup=none --sort-output translations/app_{name}.po translations/app.pot" - ret = os.system(cmd) - assert ret == 0 - else: - cmd = f"msginit -l {name} --no-translator --input translations/app.pot --output-file translations/app_{name}.po" - ret = os.system(cmd) - assert ret == 0 - - -if __name__ == "__main__": - update_translations() diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 5c02227ca5..940e7f9125 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -80,7 +80,6 @@ procs = [ PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), PythonProcess("sensord", "system.sensord.sensord", only_onroad, enabled=not PC), - # NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, enabled=False), PythonProcess("ui", "selfdrive.ui.ui", always_run), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), diff --git a/third_party/qt5/larch64/bin/lrelease b/third_party/qt5/larch64/bin/lrelease deleted file mode 100755 index 5891f85851..0000000000 --- a/third_party/qt5/larch64/bin/lrelease +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:56ddfc4ead01eaf43cba41e2095ec4f52309c53a60ba9e351d8dbd900151228f -size 546824 diff --git a/third_party/qt5/larch64/bin/lupdate b/third_party/qt5/larch64/bin/lupdate deleted file mode 100755 index 0055aae229..0000000000 --- a/third_party/qt5/larch64/bin/lupdate +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7af679aa708031f6d19baabbd48e92d23462c6ff287caccdcb5c6324168d985d -size 1095744 diff --git a/tools/cabana/utils/api.cc b/tools/cabana/utils/api.cc index 99e904c490..2ed461fdf6 100644 --- a/tools/cabana/utils/api.cc +++ b/tools/cabana/utils/api.cc @@ -1,4 +1,4 @@ -#include "selfdrive/ui/qt/api.h" +#include "tools/cabana/utils/api.h" #include #include @@ -13,9 +13,10 @@ #include #include +#include "common/params.h" #include "common/util.h" #include "system/hardware/hw.h" -#include "selfdrive/ui/qt/util.h" +#include "tools/cabana/utils/util.h" QString getVersion() { static QString version = QString::fromStdString(Params().get("Version")); From 4861d150566bf58d1ce205ba20ce575596226178 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 23 Oct 2025 00:45:51 -0700 Subject: [PATCH 241/341] reduce ui scons imports --- selfdrive/ui/SConscript | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index a9b40f0d24..37bf4974df 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,9 +1,6 @@ import os import json -Import('env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') - -raylib_env = env.Clone() - +Import('env', 'arch', 'common') # compile gettext .po -> .mo translations with open(File("translations/languages.json").abspath) as f: @@ -14,13 +11,14 @@ po_sources = [src for src in po_sources if os.path.exists(File(src).abspath)] mo_targets = [src.replace(".po", ".mo") for src in po_sources] mo_build = [] for src, tgt in zip(po_sources, mo_targets): - mo_build.append(raylib_env.Command(tgt, src, "msgfmt -o $TARGET $SOURCE")) -mo_alias = raylib_env.Alias('mo', mo_build) -raylib_env.AlwaysBuild(mo_alias) + mo_build.append(env.Command(tgt, src, "msgfmt -o $TARGET $SOURCE")) +mo_alias = env.Alias('mo', mo_build) +env.AlwaysBuild(mo_alias) if GetOption('extras'): # build installers if arch != "Darwin": + raylib_env = env.Clone() raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') From 485c7b2725081f9375b789c6b617458fd86d681d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 23 Oct 2025 00:54:31 -0700 Subject: [PATCH 242/341] multilib: relative paths (#36439) * relative * clean up --- selfdrive/ui/translations/app.pot | 473 ++++++++++++------------ selfdrive/ui/translations/app_ar.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_de.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_en.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_es.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_fr.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_ja.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_ko.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_pt-BR.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_th.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_tr.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_zh-CHS.po | 473 ++++++++++++------------ selfdrive/ui/translations/app_zh-CHT.po | 473 ++++++++++++------------ selfdrive/ui/update_translations.py | 11 +- 14 files changed, 2958 insertions(+), 3202 deletions(-) diff --git a/selfdrive/ui/translations/app.pot b/selfdrive/ui/translations/app.pot index e6a0e880db..abb6940a54 100644 --- a/selfdrive/ui/translations/app.pot +++ b/selfdrive/ui/translations/app.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 19:09-0700\n" +"POT-Creation-Date: 2025-10-23 00:51-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,488 +18,469 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " "prime offer." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " "terms at https://comma.ai/terms before continuing." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -508,7 +489,7 @@ msgid "" "better Experimental Mode." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -532,37 +513,37 @@ msgid "" "particular forks) are able to be used for training." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " "NEVER ask you to add their GitHub username." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -573,394 +554,394 @@ msgid "" "powered on." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " "Resetting calibration will restart openpilot if the car is powered on." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -968,99 +949,99 @@ msgid "" "cycle through these personalities with your steering wheel distance button." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " "(50 km/h)." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1076,74 +1057,74 @@ msgid "" "corner." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " "ACC is used for longitudinal control." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " "Experimental mode, on non-release branches." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " "Experimental mode." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "" diff --git a/selfdrive/ui/translations/app_ar.po b/selfdrive/ui/translations/app_ar.po index 5860c94ddd..608389fc07 100644 --- a/selfdrive/ui/translations/app_ar.po +++ b/selfdrive/ui/translations/app_ar.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-22 16:32-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -18,48 +18,48 @@ msgstr "" "Plural-Forms: nplurals=6; plural=n==0?0:n==1?1:n==2?2:(n%100>=3 && " "n%100<=10)?3:(n%100>=11 && n%100<=99)?4:5;\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " اكتملت معايرة استجابة عزم التوجيه." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " اكتملت معايرة استجابة عزم التوجيه بنسبة {}٪." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " جهازك موجه بمقدار {:.1f}° {} و {:.1f}° {}." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "سنة واحدة من تخزين القيادة" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "اتصال LTE على مدار الساعة" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -76,22 +76,22 @@ msgstr "" "التجربة عند تفعيل نسخة ألفا من التحكم الطولي. تغيير هذا الإعداد سيعيد تشغيل " "openpilot إذا كانت السيارة قيد التشغيل." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

اكتملت معايرة تأخر التوجيه." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

اكتملت معايرة تأخر التوجيه بنسبة {}٪." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "نشط" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -99,42 +99,41 @@ msgstr "" "يتيح ADB (Android Debug Bridge) الاتصال بجهازك عبر USB أو عبر الشبكة. راجع " "https://docs.comma.ai/how-to/connect-to-comma لمزيد من المعلومات." -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "إضافة" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "إعداد APN" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "تأكيد التشغيل المفرط" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "متقدم" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "عدواني" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "موافقة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "مراقبة السائق دائماً" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -143,285 +142,277 @@ msgstr "" "يمكن اختبار نسخة ألفا من التحكم الطولي لـ openpilot، مع وضع التجربة، على " "الفروع غير الإصدارية." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "هل أنت متأكد أنك تريد إيقاف التشغيل؟" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "هل أنت متأكد أنك تريد إعادة التشغيل؟" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "هل أنت متأكد أنك تريد إعادة ضبط المعايرة؟" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "هل أنت متأكد أنك تريد إلغاء التثبيت؟" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "رجوع" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "انضم إلى comma prime عبر connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "ثبّت connect.comma.ai على شاشتك الرئيسية لاستخدامه كتطبيق" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "تغيير" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "تحقق" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "وضع الهدوء مُفعل" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "CONNECT" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "CONNECTING..." -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "إلغاء" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "خلوي بتعرفة محدودة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "تغيير اللغة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "" "سيؤدي تغيير هذا الإعداد إلى إعادة تشغيل openpilot إذا كانت السيارة قيد " "التشغيل." -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "اضغط \"إضافة جهاز جديد\" ثم امسح رمز QR على اليمين" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "إغلاق" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "الإصدار الحالي" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "تنزيل" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "رفض" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "رفض، وإلغاء تثبيت openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "المطور" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "الجهاز" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "فصل عند الضغط على دواسة الوقود" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "افصل لإيقاف التشغيل" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "افصل لإعادة التشغيل" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "افصل لإعادة ضبط المعايرة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "عرض السرعة بالكيلومتر/ساعة بدلاً من الميل/ساعة." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "معرّف الدونجل" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "تنزيل" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "كاميرا السائق" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "شخصية القيادة" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "تعديل" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "خطأ" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "وضع التجربة مُفعل" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "تمكين" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "تمكين ADB" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "تمكين تحذيرات مغادرة المسار" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "تمكين التجوال" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "تمكين SSH" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "تمكين الربط" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "تمكين مراقبة السائق حتى عندما لا يكون openpilot مُشغلاً." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "تمكين openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " "Experimental mode." msgstr "فعّل تبديل التحكم الطولي (ألفا) لـ openpilot للسماح بوضع التجربة." -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "أدخل APN" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "أدخل SSID" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "أدخل كلمة مرور الربط الجديدة" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "أدخل كلمة المرور" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "أدخل اسم مستخدم GitHub الخاص بك" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "خطأ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "وضع التجربة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -430,25 +421,25 @@ msgstr "" "وضع التجربة غير متاح حالياً في هذه السيارة لأن نظام ACC الأصلي يُستخدم للتحكم " "الطولي." -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "جارٍ النسيان..." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "إنهاء الإعداد" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "وضع Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -490,174 +481,168 @@ msgstr "" "هل يهم أي برنامج أشغّل؟ نعم، فقط openpilot الأصلي (وبعض التفرعات المحددة) " "يمكن استخدامه للتدريب." -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "نسيان" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "هل تريد نسيان شبكة Wi‑Fi \"{}\"؟" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "جيد" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "اذهب إلى https://connect.comma.ai على هاتفك" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "مرتفع" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "شبكة مخفية" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "غير نشط: اتصل بشبكة غير محدودة التعرفة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "تثبيت" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "عنوان IP" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "تثبيت التحديث" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "وضع تصحيح عصا التحكم" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "جارٍ التحميل" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "وضع المناورة الطولية" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "أقصى" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." msgstr "زد من تحميل بيانات التدريب لتحسين نماذج قيادة openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "غير متوفر" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "لا" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "الشبكة" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "لم يتم العثور على مفاتيح SSH" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "لم يتم العثور على مفاتيح SSH للمستخدم '{}'" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "لا توجد ملاحظات إصدار متاحة." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "غير متصل" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "موافق" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "متصل" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "فتح" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "إقران" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "معاينة" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "ميزات PRIME:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "إقران الجهاز" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "إقران الجهاز" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "قم بإقران جهازك بحساب comma" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -665,28 +650,28 @@ msgid "" msgstr "" "أقرِن جهازك مع comma connect (connect.comma.ai) واحصل على عرض comma prime." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "يرجى الاتصال بشبكة Wi‑Fi لإكمال الاقتران الأولي" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "إيقاف التشغيل" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "منع رفع البيانات الكبيرة عند الاتصال بشبكة Wi‑Fi محدودة التعرفة" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "منع رفع البيانات الكبيرة عند الاتصال الخلوي محدود التعرفة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -694,42 +679,42 @@ msgstr "" "عاين كاميرا مواجهة السائق للتأكد من أن مراقبة السائق تتم برؤية جيدة. (يجب أن " "تكون المركبة متوقفة)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "خطأ في رمز QR" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "إزالة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "إعادة ضبط" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "مراجعة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "إعادة التشغيل" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "إعادة تشغيل الجهاز" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "إعادة التشغيل والتحديث" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -738,17 +723,17 @@ msgstr "" "استقبال تنبيهات للتوجيه للعودة إلى المسار عند انحراف المركبة فوق خط المسار " "المُكتشف بدون إشارة انعطاف مفعّلة أثناء القيادة فوق 31 ميل/س (50 كم/س)." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "تسجيل ورفع فيديو كاميرا السائق" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "تسجيل ورفع صوت الميكروفون" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -756,100 +741,100 @@ msgstr "" "تسجيل وتخزين صوت الميكروفون أثناء القيادة. سيُدرج الصوت في فيديو الكاميرا " "الأمامية في comma connect." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "لوائح" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "مسترخٍ" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "وصول عن بُعد" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "لقطات عن بُعد" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "انتهت مهلة الطلب" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "إعادة ضبط" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "إعادة ضبط المعايرة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "مراجعة دليل التدريب" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "مراجعة قواعد وميزات وحدود openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "اختيار" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "مفاتيح SSH" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "جارٍ مسح شبكات Wi‑Fi..." -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "اختيار" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "اختر فرعاً" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "اختر لغة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "الرقم التسلسلي" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "تأجيل التحديث" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "البرمجيات" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "قياسي" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -861,81 +846,79 @@ msgstr "" "عن السيارات الأمامية. في السيارات المدعومة، يمكنك التنقل بين هذه الشخصيات " "بزر مسافة المقود." -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "النظام لا يستجيب" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "تولَّ السيطرة فوراً" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "الحرارة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "الفرع المستهدف" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "كلمة مرور الربط" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "مفاتيح التبديل" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "إلغاء التثبيت" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "تحديث" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "إلغاء التثبيت" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "غير معروف" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "يتم تنزيل التحديثات فقط عندما تكون السيارة متوقفة." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "الترقية الآن" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." msgstr "" "ارفع بيانات من كاميرا مواجهة السائق وساعد في تحسين خوارزمية مراقبة السائق." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "استخدام النظام المتري" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -943,22 +926,21 @@ msgstr "" "استخدم نظام openpilot للتحكم الذكي بالسرعة والمساعدة على البقاء داخل المسار. " "يتطلب استخدام هذه الميزة انتباهك الكامل في جميع الأوقات." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "المركبة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "عرض" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "بانتظار البدء" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -968,35 +950,35 @@ msgstr "" "بك. لا تُدخل مطلقاً اسم مستخدم GitHub غير اسمك. لن يطلب منك موظف في comma أبداً " "إضافة اسم مستخدمهم." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "مرحباً بك في openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "عند التمكين، سيؤدي الضغط على دواسة الوقود إلى فصل openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "شبكة Wi‑Fi محدودة التعرفة" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "كلمة مرور خاطئة" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "يجب عليك قبول الشروط والأحكام لاستخدام openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1005,83 +987,82 @@ msgstr "" "يجب عليك قبول الشروط والأحكام لاستخدام openpilot. اقرأ أحدث الشروط على " "https://comma.ai/terms قبل المتابعة." -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "بدء تشغيل الكاميرا" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "افتراضي" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "أسفل" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "فشل التحقق من وجود تحديث" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "لـ \"{}\"" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "كم/س" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "اتركه فارغاً للإعداد التلقائي" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "يسار" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "محدود التعرفة" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "ميل/س" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "أبداً" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "الآن" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "التحكم الطولي لـ openpilot (ألفا)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot غير متاح" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1106,7 +1087,7 @@ msgstr "" "السرعات المنخفضة لإظهار بعض المنعطفات بشكل أفضل. كما سيظهر شعار وضع التجربة " "في الزاوية العلوية اليمنى." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1115,7 +1096,7 @@ msgstr "" "يقوم openpilot بالمعايرة بشكل مستمر، ونادراً ما تتطلب إعادة الضبط. ستؤدي " "إعادة ضبط المعايرة إلى إعادة تشغيل openpilot إذا كانت السيارة قيد التشغيل." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1128,12 +1109,12 @@ msgstr "" "يتيح وضع Firehose زيادة تحميل بيانات التدريب لتحسين نماذج قيادة openpilot. " "المزيد من البيانات يعني نماذج أكبر، مما يعني وضع تجربة أفضل." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "قد يأتي التحكم الطولي لـ openpilot في تحديث مستقبلي." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." @@ -1141,37 +1122,37 @@ msgstr "" "يتطلب openpilot تركيب الجهاز ضمن 4° يساراً أو يميناً وضمن 5° للأعلى أو 9° " "للأسفل." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "يمين" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "غير محدود" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "أعلى" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "محدّث، آخر تحقق: أبداً" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "محدّث، آخر تحقق {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "تحديث متاح" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" @@ -1182,7 +1163,7 @@ msgstr[3] "{} تنبيهات" msgstr[4] "{} تنبيهات" msgstr[5] "{} تنبيه" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" @@ -1193,7 +1174,7 @@ msgstr[3] "قبل {} أيام" msgstr[4] "قبل {} أيام" msgstr[5] "قبل {} يوم" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" @@ -1204,7 +1185,7 @@ msgstr[3] "قبل {} ساعات" msgstr[4] "قبل {} ساعات" msgstr[5] "قبل {} ساعة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" @@ -1215,7 +1196,7 @@ msgstr[3] "قبل {} دقائق" msgstr[4] "قبل {} دقائق" msgstr[5] "قبل {} دقيقة" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." @@ -1226,12 +1207,12 @@ msgstr[3] "{} مقاطع من قيادتك ضمن مجموعة بيانات ال msgstr[4] "{} مقاطع من قيادتك ضمن مجموعة بيانات التدريب حتى الآن." msgstr[5] "{} مقطع من قيادتك ضمن مجموعة بيانات التدريب حتى الآن." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ مشترك" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 وضع Firehose 🔥" diff --git a/selfdrive/ui/translations/app_de.po b/selfdrive/ui/translations/app_de.po index cac0faf887..f32c27a9ef 100644 --- a/selfdrive/ui/translations/app_de.po +++ b/selfdrive/ui/translations/app_de.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-20 16:35-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " Die Lenkmoment-Reaktionskalibrierung ist abgeschlossen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " Die Lenkmoment-Reaktionskalibrierung ist zu {}% abgeschlossen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Ihr Gerät ist um {:.1f}° {} und {:.1f}° {} ausgerichtet." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "1 Jahr Fahrtdatenspeicherung" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "24/7 LTE‑Verbindung" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -76,22 +76,22 @@ msgstr "" "Experimentalmodus wird empfohlen, wenn Sie die openpilot-Längsregelung " "(Alpha) aktivieren." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

Kalibrierung der Lenkverzögerung abgeschlossen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

Kalibrierung der Lenkverzögerung zu {}% abgeschlossen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "AKTIV" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -100,42 +100,41 @@ msgstr "" "USB oder über das Netzwerk. Siehe https://docs.comma.ai/how-to/connect-to-" "comma für weitere Informationen." -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "HINZUFÜGEN" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "APN‑Einstellung" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "Übermäßige Betätigung bestätigen" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "Erweitert" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "Aggressiv" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "Zustimmen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "Immer aktive Fahrerüberwachung" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -144,243 +143,237 @@ msgstr "" "Eine Alpha-Version der openpilot-Längsregelung kann zusammen mit dem " "Experimentalmodus auf Nicht-Release-Zweigen getestet werden." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Sind Sie sicher, dass Sie ausschalten möchten?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Sind Sie sicher, dass Sie neu starten möchten?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Sind Sie sicher, dass Sie die Kalibrierung zurücksetzen möchten?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Sind Sie sicher, dass Sie deinstallieren möchten?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "Zurück" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "Werden Sie comma prime Mitglied auf connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "" "Fügen Sie connect.comma.ai Ihrem Startbildschirm hinzu, um es wie eine App " "zu verwenden" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "ÄNDERN" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "PRÜFEN" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "CHILL‑MODUS AKTIV" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "VERBINDUNG" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "VERBINDUNG" -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "Abbrechen" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "Getaktete Mobilfunkverbindung" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "Sprache ändern" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "" " Durch Ändern dieser Einstellung wird openpilot neu gestartet, wenn das Auto " "eingeschaltet ist." -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "Klicken Sie auf \"add new device\" und scannen Sie den QR‑Code rechts" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "Schließen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Aktuelle Version" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "HERUNTERLADEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "Ablehnen" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "Ablehnen, openpilot deinstallieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "Entwickler" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "Gerät" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "Beim Gaspedal deaktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Zum Ausschalten deaktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Zum Neustart deaktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Zum Zurücksetzen der Kalibrierung deaktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "Geschwindigkeit in km/h statt mph anzeigen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "Dongle-ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Herunterladen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "Fahrerkamera" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "Fahrstil" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "BEARBEITEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "FEHLER" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "EXPERIMENTALMODUS AKTIV" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "Aktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "ADB aktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "Spurverlassenswarnungen aktivieren" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "openpilot aktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "SSH aktivieren" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "Spurverlassenswarnungen aktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "Fahrerüberwachung auch aktivieren, wenn openpilot nicht aktiv ist." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "openpilot aktivieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " @@ -389,44 +382,42 @@ msgstr "" "Den Schalter für die openpilot-Längsregelung (Alpha) aktivieren, um den " "Experimentalmodus zu erlauben." -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "APN eingeben" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "SSID eingeben" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "Neues Tethering‑Passwort eingeben" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "Passwort eingeben" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "Geben Sie Ihren GitHub‑Benutzernamen ein" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "Fehler" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "Experimentalmodus" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -435,25 +426,25 @@ msgstr "" "Der Experimentalmodus ist derzeit auf diesem Fahrzeug nicht verfügbar, da " "der serienmäßige ACC für die Längsregelung verwendet wird." -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "WIRD VERGESSEN..." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "Einrichtung abschließen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Firehose‑Modus" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -498,81 +489,79 @@ msgstr "" "Upstream‑openpilot (und bestimmte Forks) können für das Training verwendet " "werden." -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "Vergessen" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "WLAN‑Netz „{}“ vergessen?" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "GUT" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "Gehen Sie auf Ihrem Telefon zu https://connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "HOCH" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "Netzwerk" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "INAKTIV: Mit einem unlimitierten Netzwerk verbinden" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALLIEREN" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "IP‑Adresse" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Update installieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "Joystick‑Debugmodus" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "LADEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "Längsmanövermodus" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "MAX" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." @@ -580,94 +569,90 @@ msgstr "" "Maximieren Sie Ihre Trainingsdaten‑Uploads, um die Fahrmodelle von openpilot " "zu verbessern." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "k. A." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "KEIN" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "Netzwerk" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "Keine SSH‑Schlüssel gefunden" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "Keine SSH‑Schlüssel für Benutzer '{username}' gefunden" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "Keine Versionshinweise verfügbar." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "OFFLINE" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "OK" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "ONLINE" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "Öffnen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "KOPPELN" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "VORSCHAU" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "PRIME‑FUNKTIONEN:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "Gerät koppeln" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "Gerät koppeln" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "Koppeln Sie Ihr Gerät mit Ihrem comma‑Konto" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -676,28 +661,28 @@ msgstr "" "Koppeln Sie Ihr Gerät mit comma connect (connect.comma.ai) und lösen Sie Ihr " "comma‑prime‑Angebot ein." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Bitte mit WLAN verbinden, um das erste Koppeln abzuschließen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Ausschalten" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -705,42 +690,42 @@ msgstr "" "Vorschau der Fahrer‑Kamera, um sicherzustellen, dass die Fahrerüberwachung " "gute Sicht hat. (Fahrzeug muss ausgeschaltet sein)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "QR‑Code‑Fehler" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "ENTFERNEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "ZURÜCKSETZEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "ANSEHEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Neustart" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "Gerät neu starten" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "Neustarten und aktualisieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -750,17 +735,17 @@ msgstr "" "ohne Blinker über eine erkannte Spurlinie driftet und über 31 mph (50 km/h) " "fährt." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "Fahrerkamera aufzeichnen und hochladen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "Mikrofonton aufzeichnen und hochladen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -768,101 +753,101 @@ msgstr "" "Mikrofonton während der Fahrt aufzeichnen und speichern. Die Audiospur wird " "im Dashcam‑Video in comma connect enthalten sein." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "Vorschriften" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "Entspannt" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "Fernzugriff" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "Remote‑Schnappschüsse" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "Zeitüberschreitung bei der Anfrage" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Zurücksetzen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "Kalibrierung zurücksetzen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "Trainingsanleitung ansehen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "" "Überprüfen Sie die Regeln, Funktionen und Einschränkungen von openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "SSH‑Schlüssel" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "WLAN‑Netzwerke werden gesucht..." -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "Auswählen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "Sprache auswählen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "Seriennummer" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "Update verschieben" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "Software" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "Standard" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -875,69 +860,67 @@ msgstr "" "unterstützten Fahrzeugen können Sie mit der Abstandstaste am Lenkrad " "zwischen diesen Profilen wechseln." -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "System reagiert nicht" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "SOFORT DIE KONTROLLE ÜBERNEHMEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "TEMP" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "Tethering‑Passwort" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "Schalter" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "DEINSTALLIEREN" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "UPDATE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Deinstallieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "Unbekannt" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Updates werden nur heruntergeladen, wenn das Auto aus ist." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "Jetzt abonnieren" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." @@ -945,12 +928,12 @@ msgstr "" "Daten von der Fahrer‑Kamera hochladen und den Fahrerüberwachungs‑Algorithmus " "verbessern." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "Metersystem verwenden" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -959,22 +942,21 @@ msgstr "" "Spurhalteassistenz. Ihre Aufmerksamkeit ist jederzeit erforderlich, um diese " "Funktion zu nutzen." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "FAHRZEUG" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "ANSEHEN" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "Warten auf Start" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -985,36 +967,36 @@ msgstr "" "als Ihren eigenen ein. Ein comma‑Mitarbeiter wird Sie NIEMALS bitten, seinen " "GitHub‑Benutzernamen hinzuzufügen." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "Willkommen bei openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "Wenn aktiviert, deaktiviert das Drücken des Gaspedals openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "WLAN" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "Getaktetes WLAN‑Netzwerk" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "Falsches Passwort" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "" "Sie müssen die Nutzungsbedingungen akzeptieren, um openpilot zu verwenden." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1024,83 +1006,82 @@ msgstr "" "Lesen Sie die aktuellen Bedingungen unter https://comma.ai/terms, bevor Sie " "fortfahren." -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "Kamera startet" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "Standard" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "unten" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "Überprüfung auf Updates fehlgeschlagen" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "für „{}“" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "km/h" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "für automatische Konfiguration leer lassen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "links" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "getaktet" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "nie" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "jetzt" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "openpilot Längsregelung (Alpha)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot nicht verfügbar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1128,7 +1109,7 @@ msgstr "" "manche Kurven besser zu zeigen. Das Experimentalmodus‑Logo wird außerdem " "oben rechts angezeigt." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1137,7 +1118,7 @@ msgstr "" " Durch Ändern dieser Einstellung wird openpilot neu gestartet, wenn das Auto " "eingeschaltet ist." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1151,12 +1132,12 @@ msgstr "" "maximieren, um die Fahrmodelle von openpilot zu verbessern. Mehr Daten " "bedeuten größere Modelle – und damit einen besseren Experimentalmodus." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "Die openpilot‑Längsregelung könnte in einem zukünftigen Update kommen." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." @@ -1164,77 +1145,77 @@ msgstr "" "openpilot erfordert, dass das Gerät innerhalb von 4° nach links oder rechts " "und innerhalb von 5° nach oben oder 9° nach unten montiert ist." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "rechts" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "unbegrenzt" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "oben" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "Aktuell, zuletzt geprüft: nie" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "Aktuell, zuletzt geprüft: {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "Update verfügbar" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{} WARNUNG" msgstr[1] "{} WARNUNGEN" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "vor {} Tag" msgstr[1] "vor {} Tagen" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "vor {} Stunde" msgstr[1] "vor {} Stunden" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "vor {} Minute" msgstr[1] "vor {} Minuten" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "{} Segment Ihrer Fahrten ist bisher im Trainingsdatensatz." msgstr[1] "{} Segmente Ihrer Fahrten sind bisher im Trainingsdatensatz." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ ABONNIERT" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Firehose‑Modus 🔥" diff --git a/selfdrive/ui/translations/app_en.po b/selfdrive/ui/translations/app_en.po index 66fecf358d..6fbb537aff 100644 --- a/selfdrive/ui/translations/app_en.po +++ b/selfdrive/ui/translations/app_en.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-21 18:18-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " Steering torque response calibration is complete." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " Steering torque response calibration is {}% complete." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Your device is pointed {:.1f}° {} and {:.1f}° {}." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "1 year of drive storage" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "24/7 LTE connectivity" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -76,22 +76,22 @@ msgstr "" "control alpha. Changing this setting will restart openpilot if the car is " "powered on." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

Steering lag calibration is complete." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

Steering lag calibration is {}% complete." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "ACTIVE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -99,42 +99,41 @@ msgstr "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "ADD" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "APN Setting" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "Acknowledge Excessive Actuation" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "Advanced" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "Aggressive" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "Agree" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "Always-On Driver Monitoring" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -143,239 +142,233 @@ msgstr "" "An alpha version of openpilot longitudinal control can be tested, along with " "Experimental mode, on non-release branches." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Are you sure you want to power off?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Are you sure you want to reboot?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Are you sure you want to reset calibration?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Are you sure you want to uninstall?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "Back" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "Become a comma prime member at connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "Bookmark connect.comma.ai to your home screen to use it like an app" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "CHANGE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "CHECK" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "CHILL MODE ON" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "CONNECT" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "CONNECTING..." -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "Cancel" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "Cellular Metered" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "Change Language" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "Changing this setting will restart openpilot if the car is powered on." -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "Click \"add new device\" and scan the QR code on the right" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "Close" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Current Version" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "DOWNLOAD" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "Decline" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "Decline, uninstall openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "Developer" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "Device" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "Disengage on Accelerator Pedal" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Disengage to Power Off" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Disengage to Reboot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Disengage to Reset Calibration" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "Display speed in km/h instead of mph." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "Dongle ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Download" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "Driver Camera" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "Driving Personality" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "EDIT" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "ERROR" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "EXPERIMENTAL MODE ON" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "Enable" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "Enable ADB" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "Enable Lane Departure Warnings" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "Enable Roaming" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "Enable SSH" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "Enable Tethering" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "Enable driver monitoring even when openpilot is not engaged." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "Enable openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " @@ -384,44 +377,42 @@ msgstr "" "Enable the openpilot longitudinal control (alpha) toggle to allow " "Experimental mode." -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "Enter APN" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "Enter SSID" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "Enter new tethering password" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "Enter password" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "Enter your GitHub username" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "Error" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "Experimental Mode" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -430,25 +421,25 @@ msgstr "" "Experimental mode is currently unavailable on this car since the car's stock " "ACC is used for longitudinal control." -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "FORGETTING..." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "Finish Setup" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Firehose Mode" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -492,175 +483,169 @@ msgstr "" "Does it matter which software I run? Yes, only upstream openpilot (and " "particular forks) are able to be used for training." -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "Forget" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "Forget Wi-Fi Network \"{}\"?" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "GOOD" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "Go to https://connect.comma.ai on your phone" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "HIGH" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "Hidden Network" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "INACTIVE: connect to an unmetered network" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALL" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "IP Address" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Install Update" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "Joystick Debug Mode" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "LOADING" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "Longitudinal Maneuver Mode" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "MAX" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." msgstr "" "Maximize your training data uploads to improve openpilot's driving models." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "N/A" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "NO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "Network" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "No SSH keys found" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "No SSH keys found for user '{}'" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "No release notes available." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "OFFLINE" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "OK" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "ONLINE" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "Open" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "PAIR" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "PREVIEW" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "PRIME FEATURES:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "Pair Device" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "Pair device" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "Pair your device to your comma account" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -669,28 +654,28 @@ msgstr "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " "prime offer." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Please connect to Wi-Fi to complete initial pairing" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Power Off" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "Prevent large data uploads when on a metered Wi-Fi connection" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "Prevent large data uploads when on a metered cellular connection" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -698,42 +683,42 @@ msgstr "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "QR Code Error" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "REMOVE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "RESET" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "REVIEW" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Reboot" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "Reboot Device" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "Reboot and Update" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -743,17 +728,17 @@ msgstr "" "detected lane line without a turn signal activated while driving over 31 mph " "(50 km/h)." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "Record and Upload Driver Camera" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "Record and Upload Microphone Audio" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -761,100 +746,100 @@ msgstr "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "Regulatory" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "Relaxed" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "Remote access" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "Remote snapshots" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "Request timed out" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Reset" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "Reset Calibration" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "Review Training Guide" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "Review the rules, features, and limitations of openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "SSH Keys" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "Scanning Wi-Fi networks..." -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "Select" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "Select a language" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "Serial" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "Snooze Update" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "Software" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "Standard" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -866,69 +851,67 @@ msgstr "" "openpilot will stay further away from lead cars. On supported cars, you can " "cycle through these personalities with your steering wheel distance button." -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "System Unresponsive" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "TAKE CONTROL IMMEDIATELY" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "TEMP" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "Tethering Password" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "Toggles" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "UNINSTALL" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "UPDATE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Uninstall" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "Unknown" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Updates are only downloaded while the car is off." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "Upgrade Now" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." @@ -936,12 +919,12 @@ msgstr "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "Use Metric System" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -949,22 +932,21 @@ msgstr "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "VEHICLE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "VIEW" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "Waiting to start" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -974,35 +956,35 @@ msgstr "" "Never enter a GitHub username other than your own. A comma employee will " "NEVER ask you to add their GitHub username." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "Welcome to openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "When enabled, pressing the accelerator pedal will disengage openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi-Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "Wi-Fi Network Metered" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "Wrong password" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "You must accept the Terms and Conditions in order to use openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1011,83 +993,82 @@ msgstr "" "You must accept the Terms and Conditions to use openpilot. Read the latest " "terms at https://comma.ai/terms before continuing." -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "camera starting" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "default" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "down" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "failed to check for update" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "for \"{}\"" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "km/h" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "leave blank for automatic configuration" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "left" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "metered" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "never" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "now" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "openpilot Longitudinal Control (Alpha)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot Unavailable" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1114,7 +1095,7 @@ msgstr "" "some turns. The Experimental mode logo will also be shown in the top right " "corner." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1123,7 +1104,7 @@ msgstr "" "openpilot is continuously calibrating, resetting is rarely required. " "Resetting calibration will restart openpilot if the car is powered on." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1137,12 +1118,12 @@ msgstr "" "openpilot's driving models. More data means bigger models, which means " "better Experimental Mode." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "openpilot longitudinal control may come in a future update." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." @@ -1150,77 +1131,77 @@ msgstr "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "right" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "unmetered" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "up" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "up to date, last checked never" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "up to date, last checked {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "update available" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{} ALERT" msgstr[1] "{} ALERTS" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{} day ago" msgstr[1] "{} days ago" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{} hour ago" msgstr[1] "{} hours ago" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "{} minute ago" msgstr[1] "{} minutes ago" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "{} segment of your driving is in the training dataset so far." msgstr[1] "{} segments of your driving is in the training dataset so far." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ SUBSCRIBED" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Firehose Mode 🔥" diff --git a/selfdrive/ui/translations/app_es.po b/selfdrive/ui/translations/app_es.po index 04dffbf3e6..59b9e6dfdb 100644 --- a/selfdrive/ui/translations/app_es.po +++ b/selfdrive/ui/translations/app_es.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-20 16:35-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " La calibración de respuesta de par de dirección está completa." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " La calibración de respuesta de par de dirección está {}% completa." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Tu dispositivo está orientado {:.1f}° {} y {:.1f}° {}." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "1 año de almacenamiento de conducción" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "Conectividad LTE 24/7" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -75,22 +75,22 @@ msgstr "" "cambiar al control longitudinal de openpilot. Se recomienda activar el modo " "Experimental al habilitar el control longitudinal de openpilot (alpha)." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "ACTIVO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -99,42 +99,41 @@ msgstr "" "red. Consulta https://docs.comma.ai/how-to/connect-to-comma para más " "información." -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "AÑADIR" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "Reconocer actuación excesiva" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "Agresivo" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "Aceptar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "Supervisión del conductor siempre activa" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -143,244 +142,238 @@ msgstr "" "Se puede probar una versión alpha del control longitudinal de openpilot, " "junto con el modo Experimental, en ramas que no son de lanzamiento." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "¿Seguro que quieres apagar?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "¿Seguro que quieres reiniciar?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "¿Seguro que quieres restablecer la calibración?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "¿Seguro que quieres desinstalar?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "Atrás" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "Hazte miembro de comma prime en connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "" "Añade connect.comma.ai a tu pantalla de inicio para usarlo como una app" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "CAMBIAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "COMPROBAR" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "MODO CHILL ACTIVADO" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "CONECTAR" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "CONECTAR" -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "Cambiar idioma" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "" " Cambiar esta configuración reiniciará openpilot si el coche está encendido." -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "" "Haz clic en \"añadir nuevo dispositivo\" y escanea el código QR de la derecha" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "Cerrar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Versión actual" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "DESCARGAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "Rechazar" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "Rechazar, desinstalar openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "Desarrollador" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "Dispositivo" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "Desactivar con el pedal del acelerador" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Desactivar para apagar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Desactivar para reiniciar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Desactivar para restablecer la calibración" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "Mostrar la velocidad en km/h en lugar de mph." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "ID del dongle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Descargar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "Cámara del conductor" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "Estilo de conducción" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "ERROR" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "MODO EXPERIMENTAL ACTIVADO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "Activar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "Activar ADB" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "Activar advertencias de salida de carril" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "Activar openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "Activar SSH" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "Activar advertencias de salida de carril" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "" "Activar la supervisión del conductor incluso cuando openpilot no esté " "activado." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "Activar openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " @@ -389,44 +382,42 @@ msgstr "" "Activa el interruptor de control longitudinal de openpilot (alpha) para " "permitir el modo Experimental." -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "Introduce tu nombre de usuario de GitHub" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "Modo experimental" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -435,25 +426,25 @@ msgstr "" "El modo experimental no está disponible actualmente en este coche, ya que se " "usa el ACC de fábrica para el control longitudinal." -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "Finalizar configuración" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Modo Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -496,81 +487,79 @@ msgstr "" "¿Importa qué software ejecuto? Sí, solo openpilot upstream (y forks " "particulares) pueden usarse para entrenamiento." -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "BUENO" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "Ve a https://connect.comma.ai en tu teléfono" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "ALTO" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "Red" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "INACTIVO: conéctate a una red sin límites" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALAR" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Instalar actualización" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "Modo de depuración de joystick" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "CARGANDO" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "Modo de maniobra longitudinal" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "MÁX" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." @@ -578,94 +567,90 @@ msgstr "" "Maximiza tus cargas de datos de entrenamiento para mejorar los modelos de " "conducción de openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "NO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "Red" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "No se encontraron claves SSH" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "No se encontraron claves SSH para el usuario '{username}'" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "No hay notas de versión disponibles." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "SIN CONEXIÓN" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "OK" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "EN LÍNEA" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "Abrir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "EMPAREJAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "VISTA PREVIA" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "FUNCIONES PRIME:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "Emparejar dispositivo" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "Emparejar dispositivo" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "Empareja tu dispositivo con tu cuenta de comma" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -674,28 +659,28 @@ msgstr "" "Empareja tu dispositivo con comma connect (connect.comma.ai) y reclama tu " "oferta de comma prime." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Conéctate a Wi‑Fi para completar el emparejamiento inicial" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Apagar" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -704,42 +689,42 @@ msgstr "" "supervisión del conductor tenga buena visibilidad. (el vehículo debe estar " "apagado)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "Error de código QR" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "ELIMINAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "RESTABLECER" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "REVISAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Reiniciar" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "Reiniciar dispositivo" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "Reiniciar y actualizar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -749,17 +734,17 @@ msgstr "" "línea de carril detectada sin la direccional activada mientras conduces a " "más de 31 mph (50 km/h)." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "Grabar y subir cámara del conductor" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "Grabar y subir audio del micrófono" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -767,100 +752,100 @@ msgstr "" "Grabar y almacenar audio del micrófono mientras conduces. El audio se " "incluirá en el video de la dashcam en comma connect." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "Reglamentario" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "Relajado" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "Acceso remoto" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "Capturas remotas" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "Se agotó el tiempo de espera de la solicitud" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Restablecer" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "Restablecer calibración" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "Revisar guía de entrenamiento" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "Revisa las reglas, funciones y limitaciones de openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "Selecciona un idioma" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "Número de serie" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "Posponer actualización" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "Software" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "Estándar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -873,69 +858,67 @@ msgstr "" "coches compatibles, puedes cambiar entre estas personalidades con el botón " "de distancia del volante." -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "Sistema sin respuesta" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "TOME EL CONTROL INMEDIATAMENTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "TEMP" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "Interruptores" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "DESINSTALAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "ACTUALIZAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Desinstalar" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "Desconocido" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Las actualizaciones solo se descargan cuando el coche está apagado." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "Mejorar ahora" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." @@ -943,12 +926,12 @@ msgstr "" "Sube datos de la cámara orientada al conductor y ayuda a mejorar el " "algoritmo de supervisión del conductor." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "Usar sistema métrico" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -957,22 +940,21 @@ msgstr "" "mantenimiento de carril. Tu atención se requiere en todo momento para usar " "esta función." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "VEHÍCULO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "VER" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "Esperando para iniciar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -983,37 +965,37 @@ msgstr "" "que no sea el tuyo. Un empleado de comma NUNCA te pedirá que agregues su " "nombre de usuario de GitHub." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "Bienvenido a openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "" "Cuando está activado, al presionar el pedal del acelerador se desactivará " "openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "Debes aceptar los Términos y Condiciones para poder usar openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1022,83 +1004,82 @@ msgstr "" "Debes aceptar los Términos y Condiciones para usar openpilot. Lee los " "términos más recientes en https://comma.ai/terms antes de continuar." -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "iniciando cámara" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "abajo" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "Error al buscar actualizaciones" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "km/h" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "izquierda" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "nunca" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "ahora" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "Control longitudinal de openpilot (Alpha)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot no disponible" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1127,7 +1108,7 @@ msgstr "" "mostrar mejor algunos giros. El logotipo del modo Experimental también se " "mostrará en la esquina superior derecha." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1135,7 +1116,7 @@ msgid "" msgstr "" " Cambiar esta configuración reiniciará openpilot si el coche está encendido." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1149,14 +1130,14 @@ msgstr "" "para mejorar los modelos de conducción de openpilot. Más datos significan " "modelos más grandes, lo que significa un mejor Modo Experimental." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "" "El control longitudinal de openpilot podría llegar en una actualización " "futura." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." @@ -1164,65 +1145,65 @@ msgstr "" "openpilot requiere que el dispositivo esté montado dentro de 4° a izquierda " "o derecha y dentro de 5° hacia arriba o 9° hacia abajo." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "derecha" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "arriba" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "actualizado, última comprobación: nunca" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "actualizado, última comprobación: {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "actualización disponible" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{} ALERTA" msgstr[1] "{} ALERTAS" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "hace {} día" msgstr[1] "hace {} días" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "hace {} hora" msgstr[1] "hace {} horas" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "hace {} minuto" msgstr[1] "hace {} minutos" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." @@ -1233,12 +1214,12 @@ msgstr[1] "" "{} segmentos de tu conducción están en el conjunto de entrenamiento hasta " "ahora." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ SUSCRITO" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Modo Firehose 🔥" diff --git a/selfdrive/ui/translations/app_fr.po b/selfdrive/ui/translations/app_fr.po index 4b0370403e..f883d4d485 100644 --- a/selfdrive/ui/translations/app_fr.po +++ b/selfdrive/ui/translations/app_fr.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-20 18:19-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "1 an de stockage de trajets" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "Connexion LTE 24/7" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -76,22 +76,22 @@ msgstr "" "est recommandé d'activer le mode expérimental lors de l'activation du " "contrôle longitudinal openpilot alpha." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "ACTIF" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -100,42 +100,41 @@ msgstr "" "le réseau. Voir https://docs.comma.ai/how-to/connect-to-comma pour plus " "d'informations." -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "AJOUTER" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "Accuser réception d'actionnement excessif" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "Agressif" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "Accepter" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "Surveillance continue du conducteur" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -144,245 +143,239 @@ msgstr "" "Une version alpha du contrôle longitudinal openpilot peut être testée, avec " "le mode expérimental, sur des branches non publiées." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Êtes-vous sûr de vouloir éteindre ?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Êtes-vous sûr de vouloir redémarrer ?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Êtes-vous sûr de vouloir réinitialiser la calibration ?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Êtes-vous sûr de vouloir désinstaller ?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "Retour" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "Devenez membre comma prime sur connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "" "Ajoutez connect.comma.ai à votre écran d'accueil pour l'utiliser comme une " "application" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "CHANGER" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "VÉRIFIER" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "MODE CHILL ACTIVÉ" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "CONNECTER" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "CONNECTER" -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "Changer la langue" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "" " La modification de ce réglage redémarrera openpilot si la voiture est sous " "tension." -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "Cliquez sur \"add new device\" et scannez le code QR à droite" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "Fermer" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Version actuelle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "TÉLÉCHARGER" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "Refuser" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "Refuser, désinstaller openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "Développeur" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "Appareil" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "Désengager à l'appui sur l'accélérateur" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Désengager pour éteindre" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Désengager pour redémarrer" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Désengager pour réinitialiser la calibration" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "Afficher la vitesse en km/h au lieu de mph." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "ID du dongle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Télécharger" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "Caméra conducteur" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "Personnalité de conduite" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "ERREUR" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "MODE EXPÉRIMENTAL ACTIVÉ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "Activer" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "Activer ADB" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "Activer les alertes de sortie de voie" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "Activer openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "Activer SSH" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "Activer les alertes de sortie de voie" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "" "Activer la surveillance du conducteur même lorsque openpilot n'est pas " "engagé." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "Activer openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " @@ -391,44 +384,42 @@ msgstr "" "Activez l'option de contrôle longitudinal openpilot (alpha) pour autoriser " "le mode expérimental." -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "Entrez votre nom d'utilisateur GitHub" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "Mode expérimental" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -437,25 +428,25 @@ msgstr "" "Le mode expérimental est actuellement indisponible sur cette voiture car " "l'ACC d'origine est utilisé pour le contrôle longitudinal." -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "Terminer la configuration" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Mode Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -499,81 +490,79 @@ msgstr "" "Le logiciel utilisé importe-t-il ? Oui, seul openpilot amont (et certains " "forks) peut être utilisé pour l'entraînement." -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "BON" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "Allez sur https://connect.comma.ai sur votre téléphone" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "ÉLEVÉ" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "Réseau" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "INACTIF : connectez-vous à un réseau non limité" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALLER" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Installer la mise à jour" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "Mode débogage joystick" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "CHARGEMENT" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "Mode de manœuvre longitudinale" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "MAX" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." @@ -581,94 +570,90 @@ msgstr "" "Maximisez vos envois de données d'entraînement pour améliorer les modèles de " "conduite d'openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "NON" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "Réseau" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "Aucune clé SSH trouvée" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "Aucune clé SSH trouvée pour l'utilisateur '{username}'" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "Aucune note de version disponible." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "HORS LIGNE" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "OK" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "EN LIGNE" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "Ouvrir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "ASSOCIER" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "APERÇU" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "FONCTIONNALITÉS PRIME :" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "Associer l'appareil" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "Associer l'appareil" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "Associez votre appareil à votre compte comma" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -677,28 +662,28 @@ msgstr "" "Associez votre appareil à comma connect (connect.comma.ai) et réclamez votre " "offre comma prime." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Veuillez vous connecter au Wi‑Fi pour terminer l'association initiale" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Éteindre" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -707,42 +692,42 @@ msgstr "" "surveillance du conducteur a une bonne visibilité. (le véhicule doit être " "éteint)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "Erreur de code QR" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "SUPPRIMER" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "RÉINITIALISER" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "CONSULTER" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Redémarrer" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "Redémarrer l'appareil" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "Redémarrer et mettre à jour" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -752,17 +737,17 @@ msgstr "" "une ligne de voie détectée sans clignotant activé en roulant au-delà de 31 " "mph (50 km/h)." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "Enregistrer et téléverser la caméra conducteur" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "Enregistrer et téléverser l'audio du microphone" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -770,100 +755,100 @@ msgstr "" "Enregistrer et stocker l'audio du microphone pendant la conduite. L'audio " "sera inclus dans la vidéo dashcam dans comma connect." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "Réglementaire" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "Détendu" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "Accès à distance" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "Captures à distance" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "Délai de la requête dépassé" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Réinitialiser" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "Réinitialiser la calibration" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "Consulter le guide d'entraînement" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "Consultez les règles, fonctionnalités et limitations d'openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "Numéro de série" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "Reporter la mise à jour" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "Logiciel" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "Standard" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -876,70 +861,68 @@ msgstr "" "tête. Sur les voitures compatibles, vous pouvez parcourir ces personnalités " "avec le bouton de distance du volant." -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "Système non réactif" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "REPRENEZ IMMÉDIATEMENT LE CONTRÔLE" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "TEMPÉRATURE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "Options" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "DÉSINSTALLER" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "METTRE À JOUR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Désinstaller" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "Inconnu" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "" "Les mises à jour ne sont téléchargées que lorsque la voiture est éteinte." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "Mettre à niveau maintenant" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." @@ -947,12 +930,12 @@ msgstr "" "Téléverser les données de la caméra orientée conducteur et aider à améliorer " "l'algorithme de surveillance du conducteur." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "Utiliser le système métrique" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -961,22 +944,21 @@ msgstr "" "voie. Votre attention est requise en permanence pour utiliser cette " "fonctionnalité." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "VÉHICULE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "VOIR" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "En attente de démarrage" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -987,36 +969,36 @@ msgstr "" "le vôtre. Un employé comma ne vous demandera JAMAIS d'ajouter son nom " "d'utilisateur GitHub." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "Bienvenue sur openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "" "Lorsque activé, appuyer sur la pédale d'accélérateur désengagera openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "Vous devez accepter les conditions générales pour utiliser openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1025,83 +1007,82 @@ msgstr "" "Vous devez accepter les conditions générales pour utiliser openpilot. Lisez " "les dernières conditions sur https://comma.ai/terms avant de continuer." -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "démarrage de la caméra" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "échec de la vérification de mise à jour" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "km/h" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "jamais" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "maintenant" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "Contrôle longitudinal openpilot (Alpha)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot indisponible" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1129,7 +1110,7 @@ msgstr "" "pour mieux montrer certains virages. Le logo du mode expérimental sera " "également affiché en haut à droite." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1138,7 +1119,7 @@ msgstr "" " La modification de ce réglage redémarrera openpilot si la voiture est sous " "tension." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1154,14 +1135,14 @@ msgstr "" "données signifie des modèles plus grands, ce qui signifie un meilleur Mode " "expérimental." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "" "Le contrôle longitudinal openpilot pourra arriver dans une future mise à " "jour." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." @@ -1169,65 +1150,65 @@ msgstr "" "openpilot exige que l'appareil soit monté à moins de 4° à gauche ou à droite " "et à moins de 5° vers le haut ou 9° vers le bas." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "à jour, dernière vérification jamais" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "à jour, dernière vérification {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "mise à jour disponible" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{} ALERTE" msgstr[1] "{} ALERTES" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "il y a {} jour" msgstr[1] "il y a {} jours" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "il y a {} heure" msgstr[1] "il y a {} heures" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "il y a {} minute" msgstr[1] "il y a {} minutes" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." @@ -1238,12 +1219,12 @@ msgstr[1] "" "{} segments de votre conduite sont dans l'ensemble d'entraînement jusqu'à " "présent." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ ABONNÉ" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Mode Firehose 🔥" diff --git a/selfdrive/ui/translations/app_ja.po b/selfdrive/ui/translations/app_ja.po index 0ae8869634..ca8aac1515 100644 --- a/selfdrive/ui/translations/app_ja.po +++ b/selfdrive/ui/translations/app_ja.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-22 16:32-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " ステアリングトルク応答のキャリブレーションが完了しました。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " ステアリングトルク応答のキャリブレーションは{}%完了しました。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " デバイスは{:.1f}°{}、{:.1f}°{}の向きです。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "走行データを1年間保存" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "24時間365日のLTE接続" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -75,22 +75,22 @@ msgstr "" "実験モードの有効化を推奨します。この設定を変更すると、車が起動中の場合は" "openpilotが再起動します。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

ステアリング遅延のキャリブレーションが完了しました。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

ステアリング遅延のキャリブレーションは{}%完了しました。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "アクティブ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -99,42 +99,41 @@ msgstr "" "に接続できます。詳しくは https://docs.comma.ai/how-to/connect-to-comma を参照" "してください。" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "追加" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "APN設定" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "過度な作動を承認" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "詳細設定" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "アグレッシブ" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "同意する" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "常時ドライバーモニタリング" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -143,239 +142,233 @@ msgstr "" "openpilotの縦制御アルファ版は、実験モードと併せて非リリースブランチでテストで" "きます。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "本当に電源をオフにしますか?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "本当に再起動しますか?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "本当にキャリブレーションをリセットしますか?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "本当にアンインストールしますか?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "戻る" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "connect.comma.aiで comma prime に加入" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "connect.comma.aiをホーム画面に追加してアプリのように使いましょう" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "変更" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "確認" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "チルモードON" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "接続" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "接続中..." -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "キャンセル" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "従量課金の携帯回線" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "言語を変更" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "車が起動中の場合、この設定を変更するとopenpilotが再起動します。" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "\"add new device\"を押して右側のQRコードをスキャン" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "閉じる" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "現在のバージョン" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "ダウンロード" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "拒否する" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "拒否してopenpilotをアンインストール" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "開発者" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "デバイス" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "アクセルで解除" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "解除して電源オフ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "解除して再起動" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "解除してキャリブレーションをリセット" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "速度をmphではなくkm/hで表示します。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "ドングルID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "ダウンロード" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "ドライバーカメラ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "走行性格" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "編集" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "エラー" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "実験モードON" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "有効化" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "ADBを有効化" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "車線逸脱警報を有効化" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "ローミングを有効化" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "SSHを有効化" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "テザリングを有効化" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "openpilotが未作動でもドライバーモニタリングを有効にします。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "openpilotを有効化" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " @@ -383,44 +376,42 @@ msgid "" msgstr "" "openpilot縦制御(アルファ)のトグルを有効にすると実験モードが使用できます。" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "APNを入力" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "SSIDを入力" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "新しいテザリングのパスワードを入力" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "パスワードを入力" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "GitHubユーザー名を入力" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "エラー" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "実験モード" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -428,25 +419,25 @@ msgid "" msgstr "" "この車では縦制御に純正ACCを使用するため、現在実験モードは利用できません。" -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "削除中..." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "セットアップを完了" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Firehoseモード" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -489,175 +480,169 @@ msgstr "" "どのソフトウェアを使うかは重要ですか? はい。学習に使えるのは上流のopenpilot" "(および特定のフォーク)のみです。" -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "削除" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "Wi‑Fiネットワーク「{}」を削除しますか?" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "良好" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "スマートフォンで https://connect.comma.ai にアクセス" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "高温" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "非公開ネットワーク" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "非アクティブ:非従量のネットワークに接続してください" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "インストール" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "IPアドレス" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "アップデートをインストール" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "ジョイスティックデバッグモード" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "読み込み中" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "縦制御マヌーバーモード" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "最大" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." msgstr "" "学習データのアップロードを最大化してopenpilotの運転モデルを改善しましょう。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "該当なし" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "いいえ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "ネットワーク" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "SSH鍵が見つかりません" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "ユーザー'{}'のSSH鍵が見つかりません" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "リリースノートはありません。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "オフライン" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "OK" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "オンライン" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "開く" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "ペアリング" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "プレビュー" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "prime の特典:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "デバイスをペアリング" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "デバイスをペアリング" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "デバイスをあなたの comma アカウントにペアリング" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -666,28 +651,28 @@ msgstr "" "デバイスを comma connect(connect.comma.ai)とペアリングして、comma prime 特" "典を受け取りましょう。" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "初回ペアリングを完了するにはWi‑Fiに接続してください" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "電源オフ" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "従量課金のWi‑Fi接続時は大きなデータのアップロードを抑制" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "従量課金の携帯回線接続時は大きなデータのアップロードを抑制" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -695,42 +680,42 @@ msgstr "" "ドライバー向きカメラのプレビューでモニタリングの視界を確認します。(車両は停" "止状態である必要があります)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "QRコードエラー" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "削除" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "リセット" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "確認" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "再起動" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "デバイスを再起動" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "再起動して更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -739,17 +724,17 @@ msgstr "" "時速31mph(50km/h)を超えて走行中にウインカーを出さず検出された車線を外れた場" "合、車線内に戻るよう警告を受け取ります。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "ドライバーカメラを記録してアップロード" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "マイク音声を記録してアップロード" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -757,100 +742,100 @@ msgstr "" "走行中にマイク音声を記録・保存します。音声は comma connect のドライブレコー" "ダー動画に含まれます。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "規制情報" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "リラックス" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "リモートアクセス" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "リモートスナップショット" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "リクエストがタイムアウトしました" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "リセット" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "キャリブレーションをリセット" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "トレーニングガイドを確認" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "openpilotのルール、機能、制限を確認" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "選択" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "SSH鍵" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "Wi‑Fiネットワークを検索中..." -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "選択" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "ブランチを選択" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "言語を選択" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "シリアル" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "更新を後で通知" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "ソフトウェア" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "スタンダード" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -861,69 +846,67 @@ msgstr "" "リラックスでは前走車との距離を保ちます。対応車種ではステアリングの車間ボタン" "でこれらの性格を切り替えられます。" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "システムが応答しません" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "すぐに手動介入してください" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "温度" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "対象ブランチ" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "テザリングのパスワード" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "トグル" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "アンインストール" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "アンインストール" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "不明" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "アップデートは車両の電源が切れている間のみダウンロードされます。" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "今すぐアップグレード" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." @@ -931,12 +914,12 @@ msgstr "" "ドライバー向きカメラのデータをアップロードしてモニタリングアルゴリズムの改善" "に協力してください。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "メートル法を使用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -944,22 +927,21 @@ msgstr "" "ACCと車線維持支援にopenpilotを使用します。本機能の使用中は常に注意が必要で" "す。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "車両" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "表示" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "開始待機中" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -969,35 +951,35 @@ msgstr "" "GitHubユーザー名を絶対に入力しないでください。comma の従業員が自分のGitHub" "ユーザー名を追加するよう求めることは決してありません。" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "openpilotへようこそ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "有効にすると、アクセルを踏むとopenpilotが解除されます。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "Wi‑Fiネットワーク(従量課金)" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "パスワードが違います" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "openpilotを使用するには、利用規約に同意する必要があります。" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1006,83 +988,82 @@ msgstr "" "openpilotを使用するには利用規約に同意する必要があります。続行する前に " "https://comma.ai/terms の最新の規約をお読みください。" -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "カメラを起動中" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "既定" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "下" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "アップデートの確認に失敗しました" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "「{}」向け" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "km/h" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "自動設定の場合は空欄のままにしてください" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "左" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "従量" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "なし" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "今" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "openpilot 縦制御(アルファ)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilotは利用できません" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1107,7 +1088,7 @@ msgstr "" "り、一部の曲がりをより良く表示します。画面右上には実験モードのロゴも表示され" "ます。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1117,7 +1098,7 @@ msgstr "" "稀です。車が起動中にキャリブレーションをリセットするとopenpilotが再起動しま" "す。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1131,12 +1112,12 @@ msgstr "" "デルを改善できます。データが増えるほどモデルが大きくなり、実験モードがより良" "くなります。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "openpilotの縦制御は将来のアップデートで提供される可能性があります。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." @@ -1144,73 +1125,73 @@ msgstr "" "openpilotでは、デバイスの取り付け角度が左右±4°、上方向5°以内、下方向9°以内で" "ある必要があります。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "右" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "非従量" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "上" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "最新です。最終確認: なし" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "最新です。最終確認: {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "更新があります" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{}件のアラート" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{}日前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{}時間前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "{}分前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "" "これまでにあなたの走行の{}セグメントが学習データセットに含まれています。" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ 登録済み" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Firehoseモード 🔥" diff --git a/selfdrive/ui/translations/app_ko.po b/selfdrive/ui/translations/app_ko.po index c07d449013..5a3e891b87 100644 --- a/selfdrive/ui/translations/app_ko.po +++ b/selfdrive/ui/translations/app_ko.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-22 16:32-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " 스티어링 토크 응답 보정이 완료되었습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " 스티어링 토크 응답 보정이 {}% 완료되었습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " 장치는 {:.1f}° {} 및 {:.1f}° {} 방향을 가리키고 있습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "주행 데이터 1년 보관" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "연중무휴 LTE 연결" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -74,22 +74,22 @@ msgstr "" "정을 켜세요. 종방향 제어 알파를 켤 때는 실험 모드 사용을 권장합니다. 차량 전" "원이 켜져 있는 경우 이 설정을 변경하면 openpilot이 재시작됩니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

스티어링 지연 보정이 완료되었습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

스티어링 지연 보정이 {}% 완료되었습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "활성" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -98,42 +98,41 @@ msgstr "" "습니다. 자세한 내용은 https://docs.comma.ai/how-to/connect-to-comma 를 참고하" "세요." -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "추가" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "APN 설정" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "과도한 작동을 확인" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "고급" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "공격적" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "동의" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "항상 켜짐 운전자 모니터링" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -142,283 +141,275 @@ msgstr "" "openpilot 종방향 제어 알파 버전은 실험 모드와 함께 비릴리스 브랜치에서 테스트" "할 수 있습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "정말 전원을 끄시겠습니까?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "정말 재시작하시겠습니까?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "정말 보정을 재설정하시겠습니까?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "정말 제거하시겠습니까?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "뒤로" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "connect.comma.ai에서 comma prime 회원이 되세요" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "connect.comma.ai를 홈 화면에 추가하여 앱처럼 사용하세요" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "변경" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "확인" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "칠 모드 켜짐" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "연결" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "연결 중..." -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "취소" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "종량제 셀룰러" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "언어 변경" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "차량 전원이 켜져 있으면 이 설정을 변경할 때 openpilot이 재시작됩니다." -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "\"add new device\"를 눌러 오른쪽의 QR 코드를 스캔하세요" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "닫기" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "현재 버전" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "다운로드" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "거부" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "거부하고 openpilot 제거" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "개발자" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "장치" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "가속 페달로 해제" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "해제 후 전원 끄기" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "해제 후 재시작" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "해제 후 보정 재설정" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "속도를 mph 대신 km/h로 표시합니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "동글 ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "다운로드" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "운전자 카메라" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "주행 성향" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "편집" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "오류" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "실험 모드 켜짐" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "사용" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "ADB 사용" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "차선 이탈 경고 사용" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "로밍 사용" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "SSH 사용" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "테더링 사용" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "openpilot이 작동 중이 아닐 때도 운전자 모니터링을 사용합니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "openpilot 사용" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " "Experimental mode." msgstr "실험 모드를 사용하려면 openpilot 종방향 제어(알파) 토글을 켜세요." -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "APN 입력" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "SSID 입력" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "새 테더링 비밀번호 입력" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "비밀번호 입력" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "GitHub 사용자 이름 입력" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "오류" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "실험 모드" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -427,25 +418,25 @@ msgstr "" "이 차량은 종방향 제어에 순정 ACC를 사용하므로 현재 실험 모드를 사용할 수 없습" "니다." -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "삭제 중..." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "설정 완료" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Firehose 모드" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -488,174 +479,168 @@ msgstr "" "어떤 소프트웨어를 실행하는지가 중요한가요? 예. 학습에는 업스트림 " "openpilot(및 일부 포크)만 사용할 수 있습니다." -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "삭제" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "Wi‑Fi 네트워크 \"{}\"를 삭제하시겠습니까?" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "양호" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "휴대폰에서 https://connect.comma.ai 에 접속하세요" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "높음" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "숨겨진 네트워크" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "비활성: 비종량제 네트워크에 연결하세요" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "설치" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "IP 주소" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "업데이트 설치" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "조이스틱 디버그 모드" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "로딩 중" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "종방향 매뉴버 모드" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "최대" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." msgstr "학습 데이터 업로드를 최대화하여 openpilot의 주행 모델을 개선하세요." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "해당 없음" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "아니오" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "네트워크" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "SSH 키를 찾을 수 없습니다" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "사용자 '{}'의 SSH 키를 찾을 수 없습니다" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "릴리스 노트가 없습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "오프라인" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "확인" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "온라인" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "열기" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "페어링" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "미리보기" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "prime 기능:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "장치 페어링" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "장치 페어링" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "장치를 귀하의 comma 계정에 페어링하세요" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -664,28 +649,28 @@ msgstr "" "장치를 comma connect(connect.comma.ai)와 페어링하고 comma prime 혜택을 받으세" "요." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "초기 페어링을 완료하려면 Wi‑Fi에 연결하세요" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "전원 끄기" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "종량제 Wi‑Fi 연결 시 대용량 업로드 방지" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "종량제 셀룰러 연결 시 대용량 업로드 방지" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -693,42 +678,42 @@ msgstr "" "운전자 모니터링의 가시성을 확인하기 위해 운전자 카메라를 미리 봅니다. (차량" "은 꺼져 있어야 합니다)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "QR 코드 오류" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "제거" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "재설정" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "검토" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "재시작" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "장치 재시작" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "재시작 및 업데이트" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -737,17 +722,17 @@ msgstr "" "시속 31mph(50km/h) 이상에서 방향지시등 없이 감지된 차선 밖으로 벗어나면 차선" "으로 복귀하라는 경고를 받습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "운전자 카메라 기록 및 업로드" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "마이크 오디오 기록 및 업로드" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -755,100 +740,100 @@ msgstr "" "주행 중 마이크 오디오를 기록하고 저장합니다. 오디오는 comma connect의 대시캠 " "영상에 포함됩니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "규제 정보" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "편안함" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "원격 액세스" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "원격 스냅샷" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "요청 시간이 초과되었습니다" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "재설정" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "보정 재설정" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "학습 가이드 검토" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "openpilot의 규칙, 기능 및 제한을 검토" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "선택" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "SSH 키" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "Wi‑Fi 네트워크 검색 중..." -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "선택" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "브랜치 선택" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "언어 선택" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "시리얼" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "업데이트 나중에 알림" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "소프트웨어" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "표준" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -859,69 +844,67 @@ msgstr "" "극적입니다. 편안함 모드에서는 앞차와 거리를 더 둡니다. 지원 차량에서는 스티어" "링의 차간 버튼으로 이 성향들을 전환할 수 있습니다." -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "시스템 응답 없음" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "즉시 수동 조작하세요" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "온도" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "대상 브랜치" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "테더링 비밀번호" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "토글" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "제거" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "업데이트" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "제거" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "알 수 없음" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "업데이트는 차량 전원이 꺼져 있을 때만 다운로드됩니다." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "지금 업그레이드" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." @@ -929,12 +912,12 @@ msgstr "" "운전자 방향 카메라 데이터를 업로드하여 운전자 모니터링 알고리즘 개선에 도움" "을 주세요." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "미터법 사용" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -942,22 +925,21 @@ msgstr "" "ACC 및 차선 유지 보조에 openpilot을 사용합니다. 이 기능을 사용할 때는 항상 주" "의가 필요합니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "차량" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "보기" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "시작 대기 중" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -967,35 +949,35 @@ msgstr "" "아닌 GitHub 사용자 이름을 절대 입력하지 마세요. comma 직원이 본인의 GitHub 사" "용자 이름 추가를 요구하는 일은 결코 없습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "openpilot에 오신 것을 환영합니다" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "이 옵션을 켜면 가속 페달을 밟을 때 openpilot이 해제됩니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "Wi‑Fi 네트워크 종량제" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "비밀번호가 올바르지 않습니다" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "openpilot을 사용하려면 약관에 동의해야 합니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1004,83 +986,82 @@ msgstr "" "openpilot을 사용하려면 약관에 동의해야 합니다. 계속하기 전에 https://comma." "ai/terms 에서 최신 약관을 읽어주세요." -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "카메라 시작 중" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "기본값" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "아래" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "업데이트 확인 실패" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "\"{}\"용" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "km/h" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "자동 구성을 사용하려면 비워 두세요" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "왼쪽" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "종량제" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "없음" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "지금" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "openpilot 종방향 제어(알파)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot 사용 불가" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1104,7 +1085,7 @@ msgstr "" "새로운 주행 시각화
저속에서는 도로 방향의 광각 카메라로 전환되어 일" "부 회전을 더 잘 보여줍니다. 화면 오른쪽 위에는 실험 모드 로고도 표시됩니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1113,7 +1094,7 @@ msgstr "" "openpilot은 지속적으로 보정을 진행하므로 재설정이 필요한 경우는 드뭅니다. 차" "량 전원이 켜져 있을 때 보정을 재설정하면 openpilot이 재시작됩니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1127,83 +1108,83 @@ msgstr "" "할 수 있게 해줍니다. 데이터가 많을수록 모델은 커지고, 실험 모드는 더 좋아집니" "다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "openpilot 종방향 제어는 향후 업데이트에서 제공될 수 있습니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." msgstr "openpilot은 장치를 좌우 4°, 위쪽 5°, 아래쪽 9° 이내로 장착해야 합니다." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "오른쪽" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "비종량제" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "위" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "최신입니다. 마지막 확인: 없음" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "최신입니다. 마지막 확인: {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "업데이트 가능" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{}건의 알림" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{}일 전" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{}시간 전" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "{}분 전" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "현재까지 귀하의 주행 {}세그먼트가 학습 데이터셋에 포함되었습니다." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ 구독됨" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Firehose 모드 🔥" diff --git a/selfdrive/ui/translations/app_pt-BR.po b/selfdrive/ui/translations/app_pt-BR.po index a14304bb8b..8a388d0da0 100644 --- a/selfdrive/ui/translations/app_pt-BR.po +++ b/selfdrive/ui/translations/app_pt-BR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-21 00:00-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " A calibração da resposta de torque da direção foi concluída." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " A calibração da resposta de torque da direção está {}% concluída." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Seu dispositivo está apontado {:.1f}° {} e {:.1f}° {}." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "1 ano de armazenamento de condução" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "Conectividade LTE 24/7" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -75,22 +75,22 @@ msgstr "" "longitudinal do openpilot. Recomenda-se ativar o Modo Experimental ao ativar " "o controle longitudinal do openpilot em alpha." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "ATIVO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -99,42 +99,41 @@ msgstr "" "pela rede. Veja https://docs.comma.ai/how-to/connect-to-comma para mais " "informações." -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "ADICIONAR" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "Reconhecer Atuação Excessiva" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "Agressivo" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "Concordo" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "Monitoramento de Motorista Sempre Ativo" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -143,241 +142,235 @@ msgstr "" "Uma versão alpha do controle longitudinal do openpilot pode ser testada, " "junto com o Modo Experimental, em ramificações fora de release." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Tem certeza de que deseja desligar?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Tem certeza de que deseja reiniciar?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Tem certeza de que deseja redefinir a calibração?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Tem certeza de que deseja desinstalar?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "Voltar" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "Torne-se membro comma prime em connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "Adicione connect.comma.ai à tela inicial para usá-lo como um app" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "ALTERAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "VERIFICAR" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "MODO CHILL ATIVO" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "CONECTAR" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "CONECTAR" -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "Alterar Idioma" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "" " Alterar esta configuração reiniciará o openpilot se o carro estiver ligado." -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "Toque em \"adicionar novo dispositivo\" e escaneie o QR code à direita" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "Fechar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Versão Atual" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "BAIXAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "Recusar" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "Recusar, desinstalar o openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "Desenvolvedor" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "Dispositivo" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "Desativar ao pressionar o acelerador" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Desativar para Desligar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Desativar para Reiniciar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Desativar para Redefinir Calibração" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "Exibir velocidade em km/h em vez de mph." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "ID do Dongle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "Baixar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "Câmera do Motorista" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "Personalidade de Condução" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "ERRO" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "MODO EXPERIMENTAL ATIVO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "Ativar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "Ativar ADB" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "Ativar alertas de saída de faixa" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "Ativar openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "Ativar SSH" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "Ativar alertas de saída de faixa" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "" "Ativar monitoramento do motorista mesmo quando o openpilot não está engajado." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "Ativar openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " @@ -386,44 +379,42 @@ msgstr "" "Ative a opção de controle longitudinal do openpilot (alpha) para permitir o " "Modo Experimental." -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "Digite seu nome de usuário do GitHub" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "Modo Experimental" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -432,25 +423,25 @@ msgstr "" "O Modo Experimental está indisponível neste carro pois o ACC original do " "carro é usado para controle longitudinal." -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "Concluir Configuração" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Modo Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -493,81 +484,79 @@ msgstr "" "Importa qual software eu executo? Sim, apenas o openpilot upstream (e forks " "específicos) podem ser usados para treinamento." -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "BOM" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "Acesse https://connect.comma.ai no seu telefone" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "ALTO" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "Rede" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "INATIVO: conecte a uma rede sem franquia" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "INSTALAR" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Instalar Atualização" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "Modo de Depuração do Joystick" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "CARREGANDO" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "Modo de Manobra Longitudinal" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "MÁX" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." @@ -575,94 +564,90 @@ msgstr "" "Maximize seus envios de dados de treinamento para melhorar os modelos de " "condução do openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "NÃO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "Rede" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "Nenhuma chave SSH encontrada" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "Nenhuma chave SSH encontrada para o usuário '{username}'" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "Sem notas de versão disponíveis." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "OFFLINE" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "OK" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "ONLINE" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "Abrir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "EMPARELHAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "PRÉVIA" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "RECURSOS PRIME:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "Emparelhar Dispositivo" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "Emparelhar dispositivo" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "Emparelhe seu dispositivo à sua conta comma" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -671,28 +656,28 @@ msgstr "" "Emparelhe seu dispositivo com o comma connect (connect.comma.ai) e resgate " "sua oferta comma prime." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "Conecte-se ao Wi‑Fi para concluir o emparelhamento inicial" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Desligar" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -701,42 +686,42 @@ msgstr "" "monitoramento do motorista tenha boa visibilidade. (veículo deve estar " "desligado)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "Erro no QR Code" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "REMOVER" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "REDEFINIR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "REVISAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Reiniciar" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "Reiniciar Dispositivo" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "Reiniciar e Atualizar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -745,17 +730,17 @@ msgstr "" "Receba alertas para voltar à faixa quando seu veículo cruzar uma linha de " "faixa detectada sem seta ativada ao dirigir acima de 31 mph (50 km/h)." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "Gravar e Enviar Câmera do Motorista" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "Gravar e Enviar Áudio do Microfone" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -763,100 +748,100 @@ msgstr "" "Grave e armazene o áudio do microfone enquanto dirige. O áudio será incluído " "no vídeo da dashcam no comma connect." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "Regulatório" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "Relaxado" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "Acesso remoto" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "Capturas remotas" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "Tempo da solicitação esgotado" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Redefinir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "Redefinir Calibração" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "Revisar Guia de Treinamento" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "Revise as regras, recursos e limitações do openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "Selecione um idioma" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "Serial" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "Adiar Atualização" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "Software" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "Padrão" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -869,69 +854,67 @@ msgstr "" "compatíveis, você pode alternar essas personalidades com o botão de " "distância do volante." -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "Sistema sem resposta" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "ASSUMA O CONTROLE IMEDIATAMENTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "TEMP" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "Alternâncias" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "DESINSTALAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "ATUALIZAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Desinstalar" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "Desconhecido" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Atualizações são baixadas apenas com o carro desligado." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "Atualizar Agora" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." @@ -939,12 +922,12 @@ msgstr "" "Envie dados da câmera voltada para o motorista e ajude a melhorar o " "algoritmo de monitoramento do motorista." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "Usar Sistema Métrico" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -953,22 +936,21 @@ msgstr "" "de permanência em faixa. Sua atenção é necessária o tempo todo para usar " "este recurso." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "VEÍCULO" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "VER" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "Aguardando para iniciar" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -979,36 +961,36 @@ msgstr "" "seja o seu. Um funcionário da comma NUNCA pedirá para você adicionar o nome " "de usuário do GitHub dele." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "Bem-vindo ao openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "" "Quando ativado, pressionar o pedal do acelerador desengajará o openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "Você deve aceitar os Termos e Condições para usar o openpilot." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1017,83 +999,82 @@ msgstr "" "Você deve aceitar os Termos e Condições para usar o openpilot. Leia os " "termos mais recentes em https://comma.ai/terms antes de continuar." -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "câmera iniciando" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "para baixo" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "falha ao verificar atualização" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "km/h" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "à esquerda" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "nunca" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "agora" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "Controle Longitudinal do openpilot (Alpha)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot Indisponível" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1121,7 +1102,7 @@ msgstr "" "curvas. O logotipo do Modo Experimental também será exibido no canto " "superior direito." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1129,7 +1110,7 @@ msgid "" msgstr "" " Alterar esta configuração reiniciará o openpilot se o carro estiver ligado." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1143,13 +1124,13 @@ msgstr "" "melhorar os modelos de condução do openpilot. Mais dados significam modelos " "maiores, o que significa um Modo Experimental melhor." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "" "o controle longitudinal do openpilot pode vir em uma atualização futura." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." @@ -1157,65 +1138,65 @@ msgstr "" "o openpilot requer que o dispositivo seja montado dentro de 4° para a " "esquerda ou direita e dentro de 5° para cima ou 9° para baixo." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "à direita" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "para cima" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "atualizado, última verificação: nunca" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "atualizado, última verificação: {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "atualização disponível" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{} ALERTA" msgstr[1] "{} ALERTAS" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{} dia atrás" msgstr[1] "{} dias atrás" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{} hora atrás" msgstr[1] "{} horas atrás" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "{} minuto atrás" msgstr[1] "{} minutos atrás" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." @@ -1224,12 +1205,12 @@ msgstr[0] "" msgstr[1] "" "{} segmentos da sua condução estão no conjunto de treinamento até agora." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ ASSINADO" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Modo Firehose 🔥" diff --git a/selfdrive/ui/translations/app_th.po b/selfdrive/ui/translations/app_th.po index 87420ed07a..f2e56f2882 100644 --- a/selfdrive/ui/translations/app_th.po +++ b/selfdrive/ui/translations/app_th.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-22 16:32-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -69,371 +69,362 @@ msgid "" "powered on." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " "Experimental mode, on non-release branches." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " "Experimental mode." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " "ACC is used for longitudinal control." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -457,359 +448,353 @@ msgid "" "particular forks) are able to be used for training." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " "prime offer." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " "(50 km/h)." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -817,219 +802,215 @@ msgid "" "cycle through these personalities with your steering wheel distance button." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " "NEVER ask you to add their GitHub username." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " "terms at https://comma.ai/terms before continuing." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1045,14 +1026,14 @@ msgid "" "corner." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " "Resetting calibration will restart openpilot if the car is powered on." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1061,88 +1042,88 @@ msgid "" "better Experimental Mode." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "" msgstr[1] "" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "" diff --git a/selfdrive/ui/translations/app_tr.po b/selfdrive/ui/translations/app_tr.po index 022621f579..10191234a1 100644 --- a/selfdrive/ui/translations/app_tr.po +++ b/selfdrive/ui/translations/app_tr.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 18:57-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-20 18:19-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " Direksiyon tork tepkisi kalibrasyonu tamamlandı." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " Direksiyon tork tepkisi kalibrasyonu {}% tamamlandı." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " Cihazınız {:.1f}° {} ve {:.1f}° {} yönünde konumlandırılmış." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "1 yıl sürüş depolaması" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "7/24 LTE bağlantısı" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -75,22 +75,22 @@ msgstr "" "etkinleştirin. openpilot boylamsal kontrol alfayı etkinleştirirken Deneysel " "modu etkinleştirmeniz önerilir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "AKTİF" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -99,42 +99,41 @@ msgstr "" "sağlar. Daha fazla bilgi için https://docs.comma.ai/how-to/connect-to-comma " "adresine bakın." -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "EKLE" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "Aşırı Müdahaleyi Onayla" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "Agresif" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "Kabul et" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "Sürekli Sürücü İzleme" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " @@ -143,241 +142,235 @@ msgstr "" "openpilot boylamsal kontrolünün alfa sürümü, Deneysel mod ile birlikte, " "yayın dışı dallarda test edilebilir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "Kapatmak istediğinizden emin misiniz?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "Yeniden başlatmak istediğinizden emin misiniz?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "Kalibrasyonu sıfırlamak istediğinizden emin misiniz?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "Kaldırmak istediğinizden emin misiniz?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "Geri" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "connect.comma.ai adresinde comma prime üyesi olun" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "" "connect.comma.ai'yi ana ekranınıza ekleyerek bir uygulama gibi kullanın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "DEĞİŞTİR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "KONTROL ET" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "CHILL MODU AÇIK" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "BAĞLAN" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "BAĞLAN" -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "Dili Değiştir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "" " Bu ayarı değiştirmek, araç çalışıyorsa openpilot'u yeniden başlatacaktır." -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "\"yeni cihaz ekle\"ye tıklayın ve sağdaki QR kodunu tarayın" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "Kapat" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "Geçerli Sürüm" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "İNDİR" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "Reddet" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "Reddet, openpilot'u kaldır" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "Geliştirici" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "Cihaz" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "Gaz Pedalında Devreden Çık" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "Kapatmak için Devreden Çıkın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "Yeniden Başlatmak için Devreden Çıkın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "Kalibrasyonu Sıfırlamak için Devreden Çıkın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "Hızı mph yerine km/h olarak göster." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "Dongle ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "İndir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "Sürücü Kamerası" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "Sürüş Kişiliği" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "HATA" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "DENEYSEL MOD AÇIK" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "Etkinleştir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "ADB'yi Etkinleştir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "Şerit Terk Uyarılarını Etkinleştir" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "openpilot'u etkinleştir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "SSH'yi Etkinleştir" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "Şerit Terk Uyarılarını Etkinleştir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "openpilot devrede değilken bile sürücü izlemesini etkinleştir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "openpilot'u etkinleştir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " @@ -385,44 +378,42 @@ msgid "" msgstr "" "Deneysel modu etkinleştirmek için openpilot boylamsal kontrolünü (alfa) açın." -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "GitHub kullanıcı adınızı girin" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "Deneysel Mod" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " @@ -431,25 +422,25 @@ msgstr "" "Bu araçta boylamsal kontrol için stok ACC kullanıldığından şu anda Deneysel " "mod kullanılamıyor." -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "Kurulumu Bitir" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Firehose Modu" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -493,81 +484,79 @@ msgstr "" "Hangi yazılımı çalıştırdığım önemli mi? Evet, yalnızca upstream openpilot " "(ve bazı fork'lar) eğitim için kullanılabilir." -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "İYİ" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "Telefonunuzda https://connect.comma.ai adresine gidin" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "YÜKSEK" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "Ağ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "PASİF: sınırsız bir ağa bağlanın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "YÜKLE" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "Güncellemeyi Yükle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "Joystick Hata Ayıklama Modu" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "YÜKLENİYOR" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "Boylamsal Manevra Modu" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "MAKS" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." @@ -575,94 +564,90 @@ msgstr "" "openpilot'un sürüş modellerini iyileştirmek için eğitim veri yüklemelerinizi " "en üst düzeye çıkarın." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "HAYIR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "Ağ" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "SSH anahtarı bulunamadı" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "'{username}' için SSH anahtarı bulunamadı" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "Sürüm notu mevcut değil." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "ÇEVRİMDIŞI" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "OK" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "ÇEVRİMİÇİ" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "Aç" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "EŞLE" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "ÖNİZLEME" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "PRIME ÖZELLİKLERİ:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "Cihazı Eşle" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "Cihazı eşle" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "Cihazınızı comma hesabınızla eşleştirin" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -671,28 +656,28 @@ msgstr "" "Cihazınızı comma connect (connect.comma.ai) ile eşleştirin ve comma prime " "teklifinizi alın." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "İlk eşleştirmeyi tamamlamak için lütfen Wi‑Fi'a bağlanın" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "Kapat" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" @@ -700,42 +685,42 @@ msgstr "" "Sürücü izleme görünürlüğünün iyi olduğundan emin olmak için sürücüye bakan " "kamerayı önizleyin. (araç kapalı olmalıdır)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "QR Kod Hatası" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "KALDIR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "SIFIRLA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "GÖZDEN GEÇİR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "Yeniden Başlat" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "Cihazı Yeniden Başlat" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "Yeniden Başlat ve Güncelle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -744,17 +729,17 @@ msgstr "" "Araç 31 mph (50 km/h) üzerindeyken sinyal verilmeden algılanan şerit " "çizgisini aştığınızda şeride geri dönmeniz için uyarılar alın." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "Sürücü Kamerasını Kaydet ve Yükle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "Mikrofon Sesini Kaydet ve Yükle" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." @@ -762,101 +747,101 @@ msgstr "" "Sürüş sırasında mikrofon sesini kaydedip saklayın. Ses, comma connect'teki " "ön kamera videosuna dahil edilecektir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "Mevzuat" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "Rahat" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "Uzaktan erişim" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "Uzaktan anlık görüntüler" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "İstek zaman aşımına uğradı" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "Sıfırla" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "Kalibrasyonu Sıfırla" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "Eğitim Kılavuzunu İncele" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "" "openpilot'un kurallarını, özelliklerini ve sınırlamalarını gözden geçirin" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "Bir dil seçin" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "Seri" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "Güncellemeyi Ertele" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "Yazılım" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "Standart" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -868,69 +853,67 @@ msgstr "" "araçlardan daha uzak durur. Desteklenen araçlarda bu kişilikler arasında " "direksiyon mesafe düğmesiyle geçiş yapabilirsiniz." -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "Sistem Yanıt Vermiyor" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "HEMEN KONTROLÜ DEVRALIN" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "TEMP" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "Seçenekler" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "KALDIR" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "GÜNCELLE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "Kaldır" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "Bilinmiyor" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "Güncellemeler yalnızca araç kapalıyken indirilir." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "Şimdi Yükselt" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." @@ -938,12 +921,12 @@ msgstr "" "Sürücüye bakan kameradan veri yükleyin ve sürücü izleme algoritmasını " "geliştirmeye yardımcı olun." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "Metrik Sistemi Kullan" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -952,22 +935,21 @@ msgstr "" "sistemini kullanın. Bu özelliği kullanırken her zaman dikkatli olmanız " "gerekir." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "ARAÇ" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "GÖRÜNTÜLE" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "Başlatma bekleniyor" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -977,36 +959,36 @@ msgstr "" "Kendi adınız dışında asla bir GitHub kullanıcı adı girmeyin. Bir comma " "çalışanı sizden asla GitHub kullanıcı adlarını eklemenizi İSTEMEZ." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "openpilot'a hoş geldiniz" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "" "Etkinleştirildiğinde, gaz pedalına basmak openpilot'u devreden çıkarır." -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "openpilot'u kullanmak için Şartlar ve Koşulları kabul etmelisiniz." -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -1015,83 +997,82 @@ msgstr "" "openpilot'u kullanmak için Şartlar ve Koşulları kabul etmelisiniz. Devam " "etmeden önce en güncel şartları https://comma.ai/terms adresinde okuyun." -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "kamera başlatılıyor" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "aşağı" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "güncelleme kontrolü başarısız" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "km/h" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "sol" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "mph" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "asla" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "şimdi" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "openpilot Boylamsal Kontrol (Alfa)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot Kullanılamıyor" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1118,7 +1099,7 @@ msgstr "" "göstermek için yola bakan geniş açılı kameraya geçer. Deneysel mod logosu " "sağ üst köşede de gösterilecektir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1126,7 +1107,7 @@ msgid "" msgstr "" " Bu ayarı değiştirmek, araç çalışıyorsa openpilot'u yeniden başlatacaktır." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1140,12 +1121,12 @@ msgstr "" "yüklemelerinizi en üst düzeye çıkarmanıza olanak tanır. Daha fazla veri, " "daha büyük modeller demektir; bu da daha iyi Deneysel Mod anlamına gelir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "openpilot boylamsal kontrolü gelecekteki bir güncellemede gelebilir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." @@ -1153,77 +1134,77 @@ msgstr "" "openpilot, cihazın sağa/sola 4° ve yukarı 5° veya aşağı 9° içinde monte " "edilmesini gerektirir." -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "sağ" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "yukarı" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "güncel, son kontrol asla" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "güncel, son kontrol {}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "güncelleme mevcut" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{} UYARI" msgstr[1] "{} UYARILAR" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{} gün önce" msgstr[1] "{} gün önce" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{} saat önce" msgstr[1] "{} saat önce" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "{} dakika önce" msgstr[1] "{} dakika önce" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "{} segment sürüşünüz eğitim veri setinde." msgstr[1] "{} segment sürüşünüz eğitim veri setinde." -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ ABONE" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Firehose Modu 🔥" diff --git a/selfdrive/ui/translations/app_zh-CHS.po b/selfdrive/ui/translations/app_zh-CHS.po index d09add97d8..16e4369476 100644 --- a/selfdrive/ui/translations/app_zh-CHS.po +++ b/selfdrive/ui/translations/app_zh-CHS.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 19:09-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-22 16:32-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " 转向扭矩响应校准完成。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " 转向扭矩响应校准已完成 {}%。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " 您的设备朝向 {:.1f}° {} 与 {:.1f}° {}。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "1 年行驶数据存储" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "全天候 LTE 连接" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -73,22 +73,22 @@ msgstr "" "制。启用此选项可切换为 openpilot 纵向控制。建议同时启用实验模式。若车辆通电," "更改此设置将会重启 openpilot。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

转向延迟校准完成。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

转向延迟校准已完成 {}%。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "已启用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -96,350 +96,341 @@ msgstr "" "ADB(Android 调试桥)可通过 USB 或网络连接到您的设备。详见 https://docs." "comma.ai/how-to/connect-to-comma。" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "添加" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "APN 设置" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "确认过度作动" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "高级" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "激进" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "同意" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "始终启用驾驶员监控" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " "Experimental mode, on non-release branches." msgstr "openpilot 纵向控制的 alpha 版本可在非发布分支搭配实验模式进行测试。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "确定要关机吗?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "确定要重启吗?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "确定要重置校准吗?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "确定要卸载吗?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "返回" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "前往 connect.comma.ai 成为 comma prime 会员" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "将 connect.comma.ai 添加到主屏幕,像应用一样使用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "更改" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "检查" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "安稳模式已开启" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "CONNECT" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "CONNECTING..." -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "取消" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "蜂窝计量" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "更改语言" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "若车辆通电,更改此设置将重启 openpilot。" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "点击“添加新设备”,扫描右侧二维码" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "关闭" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "当前版本" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "下载" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "拒绝" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "拒绝并卸载 openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "开发者" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "设备" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "踩下加速踏板时脱离" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "脱离以关机" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "脱离以重启" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "脱离以重置校准" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "以 km/h 显示速度(非 mph)。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "Dongle ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "下载" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "车内摄像头" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "驾驶风格" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "编辑" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "错误" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "实验模式已开启" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "启用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "启用 ADB" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "启用车道偏离警示" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "启用漫游" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "启用 SSH" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "启用网络共享" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "即使未启用 openpilot 也启用驾驶员监控。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "启用 openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " "Experimental mode." msgstr "启用 openpilot 纵向控制(alpha)开关,以使用实验模式。" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "输入 APN" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "输入 SSID" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "输入新的网络共享密码" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "输入密码" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "输入您的 GitHub 用户名" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "错误" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "实验模式" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " "ACC is used for longitudinal control." msgstr "此车型当前无法使用实验模式,因为纵向控制使用的是原厂 ACC。" -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "正在遗忘..." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "完成设置" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Firehose 模式" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -477,174 +468,168 @@ msgstr "" "\n" "我跑什么软件有区别吗?有,只有上游 openpilot(及特定分支)可用于训练。" -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "忘记" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "要忘记 Wi‑Fi 网络“{}”吗?" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "良好" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "在手机上前往 https://connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "高" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "隐藏网络" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "未启用:请连接不限流量网络" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "安装" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "IP 地址" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "安装更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "摇杆调试模式" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "加载中" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "纵向操作模式" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "最大" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." msgstr "最大化上传训练数据,以改进 openpilot 的驾驶模型。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "无" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "否" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "网络" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "未找到 SSH 密钥" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "未找到用户“{}”的 SSH 密钥" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "暂无发行说明。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "离线" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "确定" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "在线" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "打开" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "配对" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "预览" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "PRIME 功能:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "配对设备" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "配对设备" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "将设备配对到您的 comma 账号" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -652,69 +637,69 @@ msgid "" msgstr "" "将设备与 comma connect(connect.comma.ai)配对,领取您的 comma prime 优惠。" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "请连接 Wi‑Fi 以完成初始配对" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "关机" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "在计量制 Wi‑Fi 连接时避免大量上传" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "在计量制蜂窝网络时避免大量上传" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" msgstr "预览车内摄像头以确保驾驶员监控视野良好。(车辆必须熄火)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "二维码错误" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "移除" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "重置" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "查看" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "重启" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "重启设备" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "重启并更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -723,117 +708,117 @@ msgstr "" "当车辆以超过 31 mph(50 km/h)行驶且未打转向灯越过检测到的车道线时,接收引导" "回车道的警报。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "录制并上传车内摄像头" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "录制并上传麦克风音频" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." msgstr "" "行驶时录制并保存麦克风音频。音频将包含在 comma connect 的行车记录视频中。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "法规" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "从容" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "远程访问" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "远程快照" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "请求超时" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "重置" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "重置校准" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "查看训练指南" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "查看 openpilot 的规则、功能与限制" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "选择" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "SSH 密钥" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "正在扫描 Wi‑Fi 网络…" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "选择" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "选择分支" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "选择语言" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "序列号" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "延后更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "软件" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "标准" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -844,80 +829,78 @@ msgstr "" "容模式下,会与前车保持更远距离。在支持的车型上,可用方向盘距离按钮切换这些风" "格。" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "系统无响应" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "请立即接管控制" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "温度" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "目标分支" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "网络共享密码" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "切换" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "卸载" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "卸载" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "未知" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "仅在车辆熄火时下载更新。" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "立即升级" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." msgstr "上传车内摄像头数据,帮助改进驾驶员监控算法。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "使用公制" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." @@ -925,22 +908,21 @@ msgstr "" "使用 openpilot 进行自适应巡航与车道保持辅助。使用此功能时,您必须始终保持专" "注。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "车辆" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "查看" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "等待开始" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -949,35 +931,35 @@ msgstr "" "警告:这将授予对您 GitHub 设置中所有公钥的 SSH 访问权限。请勿输入非您本人的 " "GitHub 用户名。comma 员工绝不会要求您添加他们的用户名。" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "欢迎使用 openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "启用后,踩下加速踏板将会脱离 openpilot。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "Wi‑Fi 计量网络" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "密码错误" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "您必须接受条款与条件才能使用 openpilot。" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -986,83 +968,82 @@ msgstr "" "您必须接受条款与条件才能使用 openpilot。继续前请阅读 https://comma.ai/terms " "上的最新条款。" -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "相机启动中" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "默认" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "下" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "检查更新失败" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "用于“{}”" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "公里/时" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "留空以自动配置" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "左" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "计量" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "英里/时" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "从不" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "现在" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "openpilot 纵向控制(Alpha)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot 无法使用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1084,7 +1065,7 @@ msgstr "" "视化
在低速时,驾驶可视化将切换至面向道路的广角摄像头以更好显示部分转" "弯。右上角也会显示实验模式图标。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1093,7 +1074,7 @@ msgstr "" "openpilot 持续进行校准,通常无需重置。若车辆通电,重置校准将会重启 " "openpilot。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1106,88 +1087,88 @@ msgstr "" "Firehose 模式可让您最大化上传训练数据,以改进 openpilot 的驾驶模型。更多数据" "意味着更大的模型,也意味着更好的实验模式。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "openpilot 纵向控制可能会在未来更新中提供。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." msgstr "openpilot 要求设备安装在左右 4°、上 5° 或下 9° 以内。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "右" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "不限流量" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "上" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "已是最新,最后检查:从未" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "已是最新,最后检查:{}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "有可用更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{} 条警报" msgstr[1] "{} 条警报" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{} 天前" msgstr[1] "{} 天前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{} 小时前" msgstr[1] "{} 小时前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "{} 分钟前" msgstr[1] "{} 分钟前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "目前已有 {} 个您的驾驶片段被纳入训练数据集。" msgstr[1] "目前已有 {} 个您的驾驶片段被纳入训练数据集。" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ 已订阅" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Firehose 模式 🔥" diff --git a/selfdrive/ui/translations/app_zh-CHT.po b/selfdrive/ui/translations/app_zh-CHT.po index 1e9e2c0948..85cfb77401 100644 --- a/selfdrive/ui/translations/app_zh-CHT.po +++ b/selfdrive/ui/translations/app_zh-CHT.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-22 19:09-0700\n" +"POT-Creation-Date: 2025-10-23 00:50-0700\n" "PO-Revision-Date: 2025-10-22 16:32-0700\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -17,48 +17,48 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:160 +#: selfdrive/ui/layouts/settings/device.py:160 #, python-format msgid " Steering torque response calibration is complete." msgstr " 轉向扭矩回應校正完成。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:158 +#: selfdrive/ui/layouts/settings/device.py:158 #, python-format msgid " Steering torque response calibration is {}% complete." msgstr " 轉向扭矩回應校正已完成 {}%。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid " Your device is pointed {:.1f}° {} and {:.1f}° {}." msgstr " 您的裝置朝向 {:.1f}° {} 與 {:.1f}° {}。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:43 +#: selfdrive/ui/layouts/sidebar.py:43 msgid "--" msgstr "--" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "1 year of drive storage" msgstr "1 年行駛資料儲存" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "24/7 LTE connectivity" msgstr "全年無休 LTE 連線" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:46 +#: selfdrive/ui/layouts/sidebar.py:46 msgid "2G" msgstr "2G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:47 +#: selfdrive/ui/layouts/sidebar.py:47 msgid "3G" msgstr "3G" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:49 +#: selfdrive/ui/layouts/sidebar.py:49 msgid "5G" msgstr "5G" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:23 +#: selfdrive/ui/layouts/settings/developer.py:23 msgid "" "WARNING: openpilot longitudinal control is in alpha for this car and will " "disable Automatic Emergency Braking (AEB).

On this car, openpilot " @@ -73,22 +73,22 @@ msgstr "" "制。啟用此選項可切換為 openpilot 縱向控制。建議同時啟用實驗模式。若車輛通電," "變更此設定將會重新啟動 openpilot。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:148 +#: selfdrive/ui/layouts/settings/device.py:148 #, python-format msgid "

Steering lag calibration is complete." msgstr "

轉向延遲校正完成。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:146 +#: selfdrive/ui/layouts/settings/device.py:146 #, python-format msgid "

Steering lag calibration is {}% complete." msgstr "

轉向延遲校正已完成 {}%。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:138 +#: selfdrive/ui/layouts/settings/firehose.py:138 #, python-format msgid "ACTIVE" msgstr "啟用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:15 +#: selfdrive/ui/layouts/settings/developer.py:15 msgid "" "ADB (Android Debug Bridge) allows connecting to your device over USB or over " "the network. See https://docs.comma.ai/how-to/connect-to-comma for more info." @@ -96,350 +96,341 @@ msgstr "" "ADB (Android Debug Bridge) 可透過 USB 或網路連線至您的裝置。詳見 https://" "docs.comma.ai/how-to/connect-to-comma。" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:30 +#: selfdrive/ui/widgets/ssh_key.py:30 msgid "ADD" msgstr "新增" -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:139 #, python-format msgid "APN Setting" msgstr "APN 設定" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:109 +#: selfdrive/ui/widgets/offroad_alerts.py:109 #, python-format msgid "Acknowledge Excessive Actuation" msgstr "確認過度作動" -#: /home/batman/openpilot/system/ui/widgets/network.py:74 -#: /home/batman/openpilot/system/ui/widgets/network.py:95 +#: system/ui/widgets/network.py:74 system/ui/widgets/network.py:95 #, python-format msgid "Advanced" msgstr "進階" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Aggressive" msgstr "積極" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:116 +#: selfdrive/ui/layouts/onboarding.py:116 #, python-format msgid "Agree" msgstr "同意" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:70 +#: selfdrive/ui/layouts/settings/toggles.py:70 #, python-format msgid "Always-On Driver Monitoring" msgstr "持續啟用駕駛監控" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:186 +#: selfdrive/ui/layouts/settings/toggles.py:186 #, python-format msgid "" "An alpha version of openpilot longitudinal control can be tested, along with " "Experimental mode, on non-release branches." msgstr "openpilot 縱向控制的 alpha 版本可於非發行分支搭配實驗模式進行測試。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Are you sure you want to power off?" msgstr "確定要關機嗎?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Are you sure you want to reboot?" msgstr "確定要重新啟動嗎?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Are you sure you want to reset calibration?" msgstr "確定要重設校正嗎?" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Are you sure you want to uninstall?" msgstr "確定要解除安裝嗎?" -#: /home/batman/openpilot/system/ui/widgets/network.py:99 -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:147 +#: system/ui/widgets/network.py:99 selfdrive/ui/layouts/onboarding.py:147 #, python-format msgid "Back" msgstr "返回" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:38 +#: selfdrive/ui/widgets/prime.py:38 #, python-format msgid "Become a comma prime member at connect.comma.ai" msgstr "前往 connect.comma.ai 成為 comma prime 會員" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:130 +#: selfdrive/ui/widgets/pairing_dialog.py:130 #, python-format msgid "Bookmark connect.comma.ai to your home screen to use it like an app" msgstr "將 connect.comma.ai 加到主畫面,像 App 一樣使用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "CHANGE" msgstr "變更" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:107 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:118 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:147 +#: selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:107 +#: selfdrive/ui/layouts/settings/software.py:118 +#: selfdrive/ui/layouts/settings/software.py:147 #, python-format msgid "CHECK" msgstr "檢查" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "CHILL MODE ON" msgstr "安穩模式已開啟" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: system/ui/widgets/network.py:155 selfdrive/ui/layouts/sidebar.py:73 +#: selfdrive/ui/layouts/sidebar.py:134 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:138 #, python-format msgid "CONNECT" msgstr "CONNECT" -#: /home/batman/openpilot/system/ui/widgets/network.py:369 +#: system/ui/widgets/network.py:369 #, python-format msgid "CONNECTING..." msgstr "CONNECTING..." -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:23 -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:35 -#: /home/batman/openpilot/system/ui/widgets/keyboard.py:81 -#: /home/batman/openpilot/system/ui/widgets/network.py:318 +#: system/ui/widgets/confirm_dialog.py:23 system/ui/widgets/option_dialog.py:35 +#: system/ui/widgets/keyboard.py:81 system/ui/widgets/network.py:318 #, python-format msgid "Cancel" msgstr "取消" -#: /home/batman/openpilot/system/ui/widgets/network.py:134 +#: system/ui/widgets/network.py:134 #, python-format msgid "Cellular Metered" msgstr "行動網路計量" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:68 +#: selfdrive/ui/layouts/settings/device.py:68 #, python-format msgid "Change Language" msgstr "變更語言" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:125 +#: selfdrive/ui/layouts/settings/toggles.py:125 #, python-format msgid "Changing this setting will restart openpilot if the car is powered on." msgstr "若車輛通電,變更此設定將重新啟動 openpilot。" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:129 +#: selfdrive/ui/widgets/pairing_dialog.py:129 #, python-format msgid "Click \"add new device\" and scan the QR code on the right" msgstr "點選「新增裝置」,掃描右側 QR 碼" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:104 +#: selfdrive/ui/widgets/offroad_alerts.py:104 #, python-format msgid "Close" msgstr "關閉" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:49 +#: selfdrive/ui/layouts/settings/software.py:49 #, python-format msgid "Current Version" msgstr "目前版本" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:110 +#: selfdrive/ui/layouts/settings/software.py:110 #, python-format msgid "DOWNLOAD" msgstr "下載" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:115 +#: selfdrive/ui/layouts/onboarding.py:115 #, python-format msgid "Decline" msgstr "拒絕" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:148 +#: selfdrive/ui/layouts/onboarding.py:148 #, python-format msgid "Decline, uninstall openpilot" msgstr "拒絕並解除安裝 openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:67 +#: selfdrive/ui/layouts/settings/settings.py:67 msgid "Developer" msgstr "開發人員" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:62 +#: selfdrive/ui/layouts/settings/settings.py:62 msgid "Device" msgstr "裝置" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:58 +#: selfdrive/ui/layouts/settings/toggles.py:58 #, python-format msgid "Disengage on Accelerator Pedal" msgstr "踩下加速踏板時脫離" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:184 +#: selfdrive/ui/layouts/settings/device.py:184 #, python-format msgid "Disengage to Power Off" msgstr "脫離以關機" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:172 +#: selfdrive/ui/layouts/settings/device.py:172 #, python-format msgid "Disengage to Reboot" msgstr "脫離以重新啟動" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:103 +#: selfdrive/ui/layouts/settings/device.py:103 #, python-format msgid "Disengage to Reset Calibration" msgstr "脫離以重設校正" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:32 +#: selfdrive/ui/layouts/settings/toggles.py:32 msgid "Display speed in km/h instead of mph." msgstr "以 km/h 顯示速度(非 mph)。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:59 #, python-format msgid "Dongle ID" msgstr "Dongle ID" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:50 +#: selfdrive/ui/layouts/settings/software.py:50 #, python-format msgid "Download" msgstr "下載" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "Driver Camera" msgstr "車內鏡頭" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:96 +#: selfdrive/ui/layouts/settings/toggles.py:96 #, python-format msgid "Driving Personality" msgstr "駕駛風格" -#: /home/batman/openpilot/system/ui/widgets/network.py:123 -#: /home/batman/openpilot/system/ui/widgets/network.py:139 +#: system/ui/widgets/network.py:123 system/ui/widgets/network.py:139 #, python-format msgid "EDIT" msgstr "編輯" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:138 +#: selfdrive/ui/layouts/sidebar.py:138 msgid "ERROR" msgstr "錯誤" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:45 +#: selfdrive/ui/layouts/sidebar.py:45 msgid "ETH" msgstr "ETH" -#: /home/batman/openpilot/selfdrive/ui/widgets/exp_mode_button.py:50 +#: selfdrive/ui/widgets/exp_mode_button.py:50 #, python-format msgid "EXPERIMENTAL MODE ON" msgstr "實驗模式已開啟" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:166 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:228 +#: selfdrive/ui/layouts/settings/developer.py:166 +#: selfdrive/ui/layouts/settings/toggles.py:228 #, python-format msgid "Enable" msgstr "啟用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:39 +#: selfdrive/ui/layouts/settings/developer.py:39 #, python-format msgid "Enable ADB" msgstr "啟用 ADB" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:64 +#: selfdrive/ui/layouts/settings/toggles.py:64 #, python-format msgid "Enable Lane Departure Warnings" msgstr "啟用偏離車道警示" -#: /home/batman/openpilot/system/ui/widgets/network.py:129 +#: system/ui/widgets/network.py:129 #, python-format msgid "Enable Roaming" msgstr "啟用漫遊" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:48 +#: selfdrive/ui/layouts/settings/developer.py:48 #, python-format msgid "Enable SSH" msgstr "啟用 SSH" -#: /home/batman/openpilot/system/ui/widgets/network.py:120 +#: system/ui/widgets/network.py:120 #, python-format msgid "Enable Tethering" msgstr "啟用網路共享" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:30 +#: selfdrive/ui/layouts/settings/toggles.py:30 msgid "Enable driver monitoring even when openpilot is not engaged." msgstr "即使未啟動 openpilot 亦啟用駕駛監控。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:46 +#: selfdrive/ui/layouts/settings/toggles.py:46 #, python-format msgid "Enable openpilot" msgstr "啟用 openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:189 +#: selfdrive/ui/layouts/settings/toggles.py:189 #, python-format msgid "" "Enable the openpilot longitudinal control (alpha) toggle to allow " "Experimental mode." msgstr "啟用 openpilot 縱向控制(alpha)切換,以使用實驗模式。" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "Enter APN" msgstr "輸入 APN" -#: /home/batman/openpilot/system/ui/widgets/network.py:241 +#: system/ui/widgets/network.py:241 #, python-format msgid "Enter SSID" msgstr "輸入 SSID" -#: /home/batman/openpilot/system/ui/widgets/network.py:254 +#: system/ui/widgets/network.py:254 #, python-format msgid "Enter new tethering password" msgstr "輸入新的網路共享密碼" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "Enter password" msgstr "輸入密碼" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:89 +#: selfdrive/ui/widgets/ssh_key.py:89 #, python-format msgid "Enter your GitHub username" msgstr "輸入您的 GitHub 使用者名稱" -#: /home/batman/openpilot/system/ui/widgets/list_view.py:123 -#: /home/batman/openpilot/system/ui/widgets/list_view.py:160 +#: system/ui/widgets/list_view.py:123 system/ui/widgets/list_view.py:160 #, python-format msgid "Error" msgstr "錯誤" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:52 +#: selfdrive/ui/layouts/settings/toggles.py:52 #, python-format msgid "Experimental Mode" msgstr "實驗模式" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:181 +#: selfdrive/ui/layouts/settings/toggles.py:181 #, python-format msgid "" "Experimental mode is currently unavailable on this car since the car's stock " "ACC is used for longitudinal control." msgstr "此車款目前無法使用實驗模式,因為縱向控制使用的是原廠 ACC。" -#: /home/batman/openpilot/system/ui/widgets/network.py:373 +#: system/ui/widgets/network.py:373 #, python-format msgid "FORGETTING..." msgstr "正在遺忘..." -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:44 +#: selfdrive/ui/widgets/setup.py:44 #, python-format msgid "Finish Setup" msgstr "完成設定" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:66 +#: selfdrive/ui/layouts/settings/settings.py:66 msgid "Firehose" msgstr "Firehose" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:18 +#: selfdrive/ui/layouts/settings/firehose.py:18 msgid "Firehose Mode" msgstr "Firehose 模式" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:25 +#: selfdrive/ui/layouts/settings/firehose.py:25 msgid "" "For maximum effectiveness, bring your device inside and connect to a good " "USB-C adapter and Wi-Fi weekly.\n" @@ -477,174 +468,168 @@ msgstr "" "\n" "我跑什麼軟體有差嗎?有,只有上游 openpilot(及特定分支)可用於訓練。" -#: /home/batman/openpilot/system/ui/widgets/network.py:318 -#: /home/batman/openpilot/system/ui/widgets/network.py:451 +#: system/ui/widgets/network.py:318 system/ui/widgets/network.py:451 #, python-format msgid "Forget" msgstr "忘記" -#: /home/batman/openpilot/system/ui/widgets/network.py:319 +#: system/ui/widgets/network.py:319 #, python-format msgid "Forget Wi-Fi Network \"{}\"?" msgstr "要忘記 Wi‑Fi 網路「{}」嗎?" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 msgid "GOOD" msgstr "良好" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:128 +#: selfdrive/ui/widgets/pairing_dialog.py:128 #, python-format msgid "Go to https://connect.comma.ai on your phone" msgstr "在手機上前往 https://connect.comma.ai" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:129 msgid "HIGH" msgstr "高" -#: /home/batman/openpilot/system/ui/widgets/network.py:155 +#: system/ui/widgets/network.py:155 #, python-format msgid "Hidden Network" msgstr "隱藏網路" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:140 +#: selfdrive/ui/layouts/settings/firehose.py:140 #, python-format msgid "INACTIVE: connect to an unmetered network" msgstr "未啟用:請連接不限流量網路" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:136 +#: selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:136 #, python-format msgid "INSTALL" msgstr "安裝" -#: /home/batman/openpilot/system/ui/widgets/network.py:150 +#: system/ui/widgets/network.py:150 #, python-format msgid "IP Address" msgstr "IP 位址" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:53 +#: selfdrive/ui/layouts/settings/software.py:53 #, python-format msgid "Install Update" msgstr "安裝更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:56 +#: selfdrive/ui/layouts/settings/developer.py:56 #, python-format msgid "Joystick Debug Mode" msgstr "搖桿除錯模式" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:29 +#: selfdrive/ui/widgets/ssh_key.py:29 msgid "LOADING" msgstr "載入中" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:48 +#: selfdrive/ui/layouts/sidebar.py:48 msgid "LTE" msgstr "LTE" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:64 +#: selfdrive/ui/layouts/settings/developer.py:64 #, python-format msgid "Longitudinal Maneuver Mode" msgstr "縱向操作模式" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:148 +#: selfdrive/ui/onroad/hud_renderer.py:148 #, python-format msgid "MAX" msgstr "最大" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:75 +#: selfdrive/ui/widgets/setup.py:75 #, python-format msgid "" "Maximize your training data uploads to improve openpilot's driving models." msgstr "最大化上傳訓練資料,以改進 openpilot 的駕駛模型。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:59 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:59 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "N/A" msgstr "無" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "NO" msgstr "否" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:63 +#: selfdrive/ui/layouts/settings/settings.py:63 msgid "Network" msgstr "網路" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:114 +#: selfdrive/ui/widgets/ssh_key.py:114 #, python-format msgid "No SSH keys found" msgstr "找不到 SSH 金鑰" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:126 +#: selfdrive/ui/widgets/ssh_key.py:126 #, python-format msgid "No SSH keys found for user '{}'" msgstr "找不到使用者 '{}' 的 SSH 金鑰" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:320 +#: selfdrive/ui/widgets/offroad_alerts.py:320 #, python-format msgid "No release notes available." msgstr "無可用發行說明。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:73 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:134 +#: selfdrive/ui/layouts/sidebar.py:73 selfdrive/ui/layouts/sidebar.py:134 msgid "OFFLINE" msgstr "離線" -#: /home/batman/openpilot/system/ui/widgets/html_render.py:263 -#: /home/batman/openpilot/system/ui/widgets/confirm_dialog.py:93 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 +#: system/ui/widgets/html_render.py:263 system/ui/widgets/confirm_dialog.py:93 +#: selfdrive/ui/layouts/sidebar.py:127 #, python-format msgid "OK" msgstr "確定" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:136 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:136 +#: selfdrive/ui/layouts/sidebar.py:144 msgid "ONLINE" msgstr "線上" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:20 +#: selfdrive/ui/widgets/setup.py:20 #, python-format msgid "Open" msgstr "開啟" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "PAIR" msgstr "配對" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:142 +#: selfdrive/ui/layouts/sidebar.py:142 msgid "PANDA" msgstr "PANDA" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:62 +#: selfdrive/ui/layouts/settings/device.py:62 #, python-format msgid "PREVIEW" msgstr "預覽" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:44 +#: selfdrive/ui/widgets/prime.py:44 #, python-format msgid "PRIME FEATURES:" msgstr "PRIME 功能:" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:48 +#: selfdrive/ui/layouts/settings/device.py:48 #, python-format msgid "Pair Device" msgstr "配對裝置" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:19 +#: selfdrive/ui/widgets/setup.py:19 #, python-format msgid "Pair device" msgstr "配對裝置" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:103 +#: selfdrive/ui/widgets/pairing_dialog.py:103 #, python-format msgid "Pair your device to your comma account" msgstr "將裝置配對至您的 comma 帳號" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:48 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:24 +#: selfdrive/ui/widgets/setup.py:48 selfdrive/ui/layouts/settings/device.py:24 #, python-format msgid "" "Pair your device with comma connect (connect.comma.ai) and claim your comma " @@ -652,69 +637,69 @@ msgid "" msgstr "" "將裝置與 comma connect(connect.comma.ai)配對,領取您的 comma prime 優惠。" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:91 +#: selfdrive/ui/widgets/setup.py:91 #, python-format msgid "Please connect to Wi-Fi to complete initial pairing" msgstr "請連線至 Wi‑Fi 以完成初始化配對" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:187 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:187 #, python-format msgid "Power Off" msgstr "關機" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Prevent large data uploads when on a metered Wi-Fi connection" msgstr "在計量制 Wi‑Fi 連線時避免大量上傳" -#: /home/batman/openpilot/system/ui/widgets/network.py:135 +#: system/ui/widgets/network.py:135 #, python-format msgid "Prevent large data uploads when on a metered cellular connection" msgstr "在計量制行動網路時避免大量上傳" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:25 +#: selfdrive/ui/layouts/settings/device.py:25 msgid "" "Preview the driver facing camera to ensure that driver monitoring has good " "visibility. (vehicle must be off)" msgstr "預覽車內鏡頭以確保駕駛監控視野良好。(車輛須熄火)" -#: /home/batman/openpilot/selfdrive/ui/widgets/pairing_dialog.py:161 +#: selfdrive/ui/widgets/pairing_dialog.py:161 #, python-format msgid "QR Code Error" msgstr "QR 碼錯誤" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:31 +#: selfdrive/ui/widgets/ssh_key.py:31 msgid "REMOVE" msgstr "移除" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "RESET" msgstr "重設" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "REVIEW" msgstr "檢視" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:55 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:175 +#: selfdrive/ui/layouts/settings/device.py:55 +#: selfdrive/ui/layouts/settings/device.py:175 #, python-format msgid "Reboot" msgstr "重新啟動" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:66 +#: selfdrive/ui/onroad/alert_renderer.py:66 #, python-format msgid "Reboot Device" msgstr "重新啟動裝置" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:112 +#: selfdrive/ui/widgets/offroad_alerts.py:112 #, python-format msgid "Reboot and Update" msgstr "重新啟動並更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:27 +#: selfdrive/ui/layouts/settings/toggles.py:27 msgid "" "Receive alerts to steer back into the lane when your vehicle drifts over a " "detected lane line without a turn signal activated while driving over 31 mph " @@ -723,117 +708,117 @@ msgstr "" "當車輛以超過 31 mph(50 km/h)行駛且未打方向燈越過偵測到的車道線時,接收轉向" "回車道的警示。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:76 +#: selfdrive/ui/layouts/settings/toggles.py:76 #, python-format msgid "Record and Upload Driver Camera" msgstr "錄製並上傳車內鏡頭" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:82 +#: selfdrive/ui/layouts/settings/toggles.py:82 #, python-format msgid "Record and Upload Microphone Audio" msgstr "錄製並上傳麥克風音訊" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:33 +#: selfdrive/ui/layouts/settings/toggles.py:33 msgid "" "Record and store microphone audio while driving. The audio will be included " "in the dashcam video in comma connect." msgstr "" "行車時錄製並儲存麥克風音訊。音訊將包含在 comma connect 的行車紀錄影片中。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "Regulatory" msgstr "法規" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Relaxed" msgstr "從容" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote access" msgstr "遠端存取" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:47 +#: selfdrive/ui/widgets/prime.py:47 #, python-format msgid "Remote snapshots" msgstr "遠端擷圖" -#: /home/batman/openpilot/selfdrive/ui/widgets/ssh_key.py:123 +#: selfdrive/ui/widgets/ssh_key.py:123 #, python-format msgid "Request timed out" msgstr "要求逾時" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:119 +#: selfdrive/ui/layouts/settings/device.py:119 #, python-format msgid "Reset" msgstr "重設" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:51 +#: selfdrive/ui/layouts/settings/device.py:51 #, python-format msgid "Reset Calibration" msgstr "重設校正" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:65 +#: selfdrive/ui/layouts/settings/device.py:65 #, python-format msgid "Review Training Guide" msgstr "檢視訓練指南" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:27 +#: selfdrive/ui/layouts/settings/device.py:27 msgid "Review the rules, features, and limitations of openpilot" msgstr "檢視 openpilot 的規則、功能與限制" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "SELECT" msgstr "選取" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:53 +#: selfdrive/ui/layouts/settings/developer.py:53 #, python-format msgid "SSH Keys" msgstr "SSH 金鑰" -#: /home/batman/openpilot/system/ui/widgets/network.py:310 +#: system/ui/widgets/network.py:310 #, python-format msgid "Scanning Wi-Fi networks..." msgstr "正在掃描 Wi‑Fi 網路…" -#: /home/batman/openpilot/system/ui/widgets/option_dialog.py:36 +#: system/ui/widgets/option_dialog.py:36 #, python-format msgid "Select" msgstr "選取" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:183 +#: selfdrive/ui/layouts/settings/software.py:183 #, python-format msgid "Select a branch" msgstr "選取分支" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:91 +#: selfdrive/ui/layouts/settings/device.py:91 #, python-format msgid "Select a language" msgstr "選取語言" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:60 +#: selfdrive/ui/layouts/settings/device.py:60 #, python-format msgid "Serial" msgstr "序號" -#: /home/batman/openpilot/selfdrive/ui/widgets/offroad_alerts.py:106 +#: selfdrive/ui/widgets/offroad_alerts.py:106 #, python-format msgid "Snooze Update" msgstr "延後更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:65 +#: selfdrive/ui/layouts/settings/settings.py:65 msgid "Software" msgstr "軟體" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:98 +#: selfdrive/ui/layouts/settings/toggles.py:98 #, python-format msgid "Standard" msgstr "標準" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:22 +#: selfdrive/ui/layouts/settings/toggles.py:22 msgid "" "Standard is recommended. In aggressive mode, openpilot will follow lead cars " "closer and be more aggressive with the gas and brake. In relaxed mode " @@ -844,102 +829,99 @@ msgstr "" "從容模式下,會與前車保持更遠距離。於支援車款,可用方向盤距離按鈕切換這些風" "格。" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:59 -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:65 +#: selfdrive/ui/onroad/alert_renderer.py:59 +#: selfdrive/ui/onroad/alert_renderer.py:65 #, python-format msgid "System Unresponsive" msgstr "系統無回應" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:58 +#: selfdrive/ui/onroad/alert_renderer.py:58 #, python-format msgid "TAKE CONTROL IMMEDIATELY" msgstr "請立刻接手控制" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:71 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:125 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:127 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:129 +#: selfdrive/ui/layouts/sidebar.py:71 selfdrive/ui/layouts/sidebar.py:125 +#: selfdrive/ui/layouts/sidebar.py:127 selfdrive/ui/layouts/sidebar.py:129 msgid "TEMP" msgstr "溫度" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:61 +#: selfdrive/ui/layouts/settings/software.py:61 #, python-format msgid "Target Branch" msgstr "目標分支" -#: /home/batman/openpilot/system/ui/widgets/network.py:124 +#: system/ui/widgets/network.py:124 #, python-format msgid "Tethering Password" msgstr "網路共享密碼" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/settings.py:64 +#: selfdrive/ui/layouts/settings/settings.py:64 msgid "Toggles" msgstr "切換" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:72 #, python-format msgid "UNINSTALL" msgstr "解除安裝" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:155 +#: selfdrive/ui/layouts/home.py:155 #, python-format msgid "UPDATE" msgstr "更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:163 +#: selfdrive/ui/layouts/settings/software.py:72 +#: selfdrive/ui/layouts/settings/software.py:163 #, python-format msgid "Uninstall" msgstr "解除安裝" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:117 +#: selfdrive/ui/layouts/sidebar.py:117 msgid "Unknown" msgstr "未知" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:48 +#: selfdrive/ui/layouts/settings/software.py:48 #, python-format msgid "Updates are only downloaded while the car is off." msgstr "僅在車輛熄火時下載更新。" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:33 +#: selfdrive/ui/widgets/prime.py:33 #, python-format msgid "Upgrade Now" msgstr "立即升級" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:31 +#: selfdrive/ui/layouts/settings/toggles.py:31 msgid "" "Upload data from the driver facing camera and help improve the driver " "monitoring algorithm." msgstr "上傳車內鏡頭資料,協助改善駕駛監控演算法。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:88 +#: selfdrive/ui/layouts/settings/toggles.py:88 #, python-format msgid "Use Metric System" msgstr "使用公制" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:17 +#: selfdrive/ui/layouts/settings/toggles.py:17 msgid "" "Use the openpilot system for adaptive cruise control and lane keep driver " "assistance. Your attention is required at all times to use this feature." msgstr "" "使用 openpilot 進行 ACC 與車道維持輔助。使用此功能時,您必須始終保持專注。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:72 -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:144 +#: selfdrive/ui/layouts/sidebar.py:72 selfdrive/ui/layouts/sidebar.py:144 msgid "VEHICLE" msgstr "車輛" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:67 +#: selfdrive/ui/layouts/settings/device.py:67 #, python-format msgid "VIEW" msgstr "檢視" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:52 +#: selfdrive/ui/onroad/alert_renderer.py:52 #, python-format msgid "Waiting to start" msgstr "等待開始" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:19 +#: selfdrive/ui/layouts/settings/developer.py:19 msgid "" "Warning: This grants SSH access to all public keys in your GitHub settings. " "Never enter a GitHub username other than your own. A comma employee will " @@ -948,35 +930,35 @@ msgstr "" "警告:這將授予對您 GitHub 設定中所有公開金鑰的 SSH 存取權。請勿輸入非您本人" "的 GitHub 帳號。comma 員工絕不會要求您新增他們的帳號。" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:111 +#: selfdrive/ui/layouts/onboarding.py:111 #, python-format msgid "Welcome to openpilot" msgstr "歡迎使用 openpilot" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:20 +#: selfdrive/ui/layouts/settings/toggles.py:20 msgid "When enabled, pressing the accelerator pedal will disengage openpilot." msgstr "啟用後,踩下加速踏板將會脫離 openpilot。" -#: /home/batman/openpilot/selfdrive/ui/layouts/sidebar.py:44 +#: selfdrive/ui/layouts/sidebar.py:44 msgid "Wi-Fi" msgstr "Wi‑Fi" -#: /home/batman/openpilot/system/ui/widgets/network.py:144 +#: system/ui/widgets/network.py:144 #, python-format msgid "Wi-Fi Network Metered" msgstr "Wi‑Fi 計量網路" -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:314 #, python-format msgid "Wrong password" msgstr "密碼錯誤" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:145 +#: selfdrive/ui/layouts/onboarding.py:145 #, python-format msgid "You must accept the Terms and Conditions in order to use openpilot." msgstr "您必須接受條款與細則才能使用 openpilot。" -#: /home/batman/openpilot/selfdrive/ui/layouts/onboarding.py:112 +#: selfdrive/ui/layouts/onboarding.py:112 #, python-format msgid "" "You must accept the Terms and Conditions to use openpilot. Read the latest " @@ -985,83 +967,82 @@ msgstr "" "您必須接受條款與細則才能使用 openpilot。繼續前請閱讀 https://comma.ai/terms " "上的最新條款。" -#: /home/batman/openpilot/selfdrive/ui/onroad/driver_camera_dialog.py:34 +#: selfdrive/ui/onroad/driver_camera_dialog.py:34 #, python-format msgid "camera starting" msgstr "相機啟動中" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:63 +#: selfdrive/ui/widgets/prime.py:63 #, python-format msgid "comma prime" msgstr "comma prime" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "default" msgstr "預設" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "down" msgstr "下" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:106 +#: selfdrive/ui/layouts/settings/software.py:106 #, python-format msgid "failed to check for update" msgstr "檢查更新失敗" -#: /home/batman/openpilot/system/ui/widgets/network.py:237 -#: /home/batman/openpilot/system/ui/widgets/network.py:314 +#: system/ui/widgets/network.py:237 system/ui/widgets/network.py:314 #, python-format msgid "for \"{}\"" msgstr "適用於「{}」" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "km/h" msgstr "公里/時" -#: /home/batman/openpilot/system/ui/widgets/network.py:204 +#: system/ui/widgets/network.py:204 #, python-format msgid "leave blank for automatic configuration" msgstr "留空以自動設定" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "left" msgstr "左" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "metered" msgstr "計量" -#: /home/batman/openpilot/selfdrive/ui/onroad/hud_renderer.py:177 +#: selfdrive/ui/onroad/hud_renderer.py:177 #, python-format msgid "mph" msgstr "英里/時" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:20 +#: selfdrive/ui/layouts/settings/software.py:20 #, python-format msgid "never" msgstr "從不" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:31 +#: selfdrive/ui/layouts/settings/software.py:31 #, python-format msgid "now" msgstr "現在" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/developer.py:71 +#: selfdrive/ui/layouts/settings/developer.py:71 #, python-format msgid "openpilot Longitudinal Control (Alpha)" msgstr "openpilot 縱向控制(Alpha)" -#: /home/batman/openpilot/selfdrive/ui/onroad/alert_renderer.py:51 +#: selfdrive/ui/onroad/alert_renderer.py:51 #, python-format msgid "openpilot Unavailable" msgstr "openpilot 無法使用" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:158 +#: selfdrive/ui/layouts/settings/toggles.py:158 #, python-format msgid "" "openpilot defaults to driving in chill mode. Experimental mode enables alpha-" @@ -1083,7 +1064,7 @@ msgstr "" "駕駛視覺化
在低速時,駕駛視覺化將切換至面向道路的廣角鏡頭以更好呈現部" "分轉彎。右上角亦會顯示實驗模式圖示。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:165 +#: selfdrive/ui/layouts/settings/device.py:165 #, python-format msgid "" "openpilot is continuously calibrating, resetting is rarely required. " @@ -1092,7 +1073,7 @@ msgstr "" "openpilot 會持續校正,通常不需重設。若車輛通電,重設校正將重新啟動 " "openpilot。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:20 +#: selfdrive/ui/layouts/settings/firehose.py:20 msgid "" "openpilot learns to drive by watching humans, like you, drive.\n" "\n" @@ -1105,88 +1086,88 @@ msgstr "" "Firehose 模式可讓您最大化上傳訓練資料,以改進 openpilot 的駕駛模型。更多資料" "代表更大的模型,也就代表更好的實驗模式。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/toggles.py:183 +#: selfdrive/ui/layouts/settings/toggles.py:183 #, python-format msgid "openpilot longitudinal control may come in a future update." msgstr "openpilot 縱向控制可能於未來更新提供。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:26 +#: selfdrive/ui/layouts/settings/device.py:26 msgid "" "openpilot requires the device to be mounted within 4° left or right and " "within 5° up or 9° down." msgstr "openpilot 要求裝置安裝在左右 4°、上 5° 或下 9° 以內。" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:134 +#: selfdrive/ui/layouts/settings/device.py:134 #, python-format msgid "right" msgstr "右" -#: /home/batman/openpilot/system/ui/widgets/network.py:142 +#: system/ui/widgets/network.py:142 #, python-format msgid "unmetered" msgstr "不限流量" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/device.py:133 +#: selfdrive/ui/layouts/settings/device.py:133 #, python-format msgid "up" msgstr "上" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:117 +#: selfdrive/ui/layouts/settings/software.py:117 #, python-format msgid "up to date, last checked never" msgstr "已為最新,最後檢查:從未" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:115 +#: selfdrive/ui/layouts/settings/software.py:115 #, python-format msgid "up to date, last checked {}" msgstr "已為最新,最後檢查:{}" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:109 +#: selfdrive/ui/layouts/settings/software.py:109 #, python-format msgid "update available" msgstr "有可用更新" -#: /home/batman/openpilot/selfdrive/ui/layouts/home.py:169 +#: selfdrive/ui/layouts/home.py:169 #, python-format msgid "{} ALERT" msgid_plural "{} ALERTS" msgstr[0] "{} 則警示" msgstr[1] "{} 則警示" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:40 +#: selfdrive/ui/layouts/settings/software.py:40 #, python-format msgid "{} day ago" msgid_plural "{} days ago" msgstr[0] "{} 天前" msgstr[1] "{} 天前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:37 +#: selfdrive/ui/layouts/settings/software.py:37 #, python-format msgid "{} hour ago" msgid_plural "{} hours ago" msgstr[0] "{} 小時前" msgstr[1] "{} 小時前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/software.py:34 +#: selfdrive/ui/layouts/settings/software.py:34 #, python-format msgid "{} minute ago" msgid_plural "{} minutes ago" msgstr[0] "{} 分鐘前" msgstr[1] "{} 分鐘前" -#: /home/batman/openpilot/selfdrive/ui/layouts/settings/firehose.py:111 +#: selfdrive/ui/layouts/settings/firehose.py:111 #, python-format msgid "{} segment of your driving is in the training dataset so far." msgid_plural "{} segments of your driving is in the training dataset so far." msgstr[0] "目前已有 {} 個您的駕駛片段納入訓練資料集。" msgstr[1] "目前已有 {} 個您的駕駛片段納入訓練資料集。" -#: /home/batman/openpilot/selfdrive/ui/widgets/prime.py:62 +#: selfdrive/ui/widgets/prime.py:62 #, python-format msgid "✓ SUBSCRIBED" msgstr "✓ 已訂閱" -#: /home/batman/openpilot/selfdrive/ui/widgets/setup.py:22 +#: selfdrive/ui/widgets/setup.py:22 #, python-format msgid "🔥 Firehose Mode 🔥" msgstr "🔥 Firehose 模式 🔥" diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py index e91820f242..779beb5ba2 100755 --- a/selfdrive/ui/update_translations.py +++ b/selfdrive/ui/update_translations.py @@ -1,8 +1,11 @@ #!/usr/bin/env python3 from itertools import chain import os +from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.multilang import SYSTEM_UI_DIR, UI_DIR, TRANSLATIONS_DIR, multilang +POT_FILE = os.path.join(TRANSLATIONS_DIR, "app.pot") + def update_translations(): files = [] @@ -12,12 +15,12 @@ def update_translations(): os.walk(os.path.join(UI_DIR, "onroad"))): for filename in filenames: if filename.endswith(".py"): - files.append(os.path.join(root, filename)) + files.append(os.path.relpath(os.path.join(root, filename), BASEDIR)) # Create main translation file cmd = ("xgettext -L Python --keyword=tr --keyword=trn:1,2 --keyword=tr_noop --from-code=UTF-8 " + "--flag=tr:1:python-brace-format --flag=trn:1:python-brace-format --flag=trn:2:python-brace-format " + - "-o translations/app.pot {}").format(" ".join(files)) + f"-D {BASEDIR} -o {POT_FILE} {' '.join(files)}") ret = os.system(cmd) assert ret == 0 @@ -25,11 +28,11 @@ def update_translations(): # Generate/update translation files for each language for name in multilang.languages.values(): if os.path.exists(os.path.join(TRANSLATIONS_DIR, f"app_{name}.po")): - cmd = f"msgmerge --update --no-fuzzy-matching --backup=none --sort-output translations/app_{name}.po translations/app.pot" + cmd = f"msgmerge --update --no-fuzzy-matching --backup=none --sort-output {TRANSLATIONS_DIR}/app_{name}.po {POT_FILE}" ret = os.system(cmd) assert ret == 0 else: - cmd = f"msginit -l {name} --no-translator --input translations/app.pot --output-file translations/app_{name}.po" + cmd = f"msginit -l {name} --no-translator --input {POT_FILE} --output-file {TRANSLATIONS_DIR}/app_{name}.po" ret = os.system(cmd) assert ret == 0 From ab234c72a31af11eb63f9f1d4304db1eaa44a996 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 23 Oct 2025 00:55:31 -0700 Subject: [PATCH 243/341] wait slightly longer to take screenshot --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 29fae5b61c..3080b55b95 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -25,7 +25,7 @@ AlertStatus = log.SelfdriveState.AlertStatus TEST_DIR = pathlib.Path(__file__).parent TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" -UI_DELAY = 0.2 +UI_DELAY = 0.5 BRANCH_NAME = "this-is-a-really-super-mega-ultra-max-extreme-ultimate-long-branch-name" VERSION = f"0.10.1 / {BRANCH_NAME} / 7864838 / Oct 03" From 6486ab6cab719be265fb022082eec5014b645792 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:09:14 -0500 Subject: [PATCH 244/341] raylib: fix crash when toggling advanced network toggles (#36443) use get_state() --- system/ui/widgets/network.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index e705c55f7c..e9d7a1b09e 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -175,14 +175,14 @@ class AdvancedNetworkSettings(Widget): self._wifi_metered_action.selected_button = int(metered) if metered in (MeteredType.UNKNOWN, MeteredType.YES, MeteredType.NO) else 0 def _toggle_tethering(self): - checked = self._tethering_action.state + checked = self._tethering_action.get_state() self._tethering_action.set_enabled(False) if checked: self._wifi_metered_action.set_enabled(False) self._wifi_manager.set_tethering_active(checked) def _toggle_roaming(self): - roaming_state = self._roaming_action.state + roaming_state = self._roaming_action.get_state() self._params.put_bool("GsmRoaming", roaming_state) self._wifi_manager.update_gsm_settings(roaming_state, self._params.get("GsmApn") or "", self._params.get_bool("GsmMetered")) @@ -206,7 +206,7 @@ class AdvancedNetworkSettings(Widget): gui_app.set_modal_overlay(self._keyboard, update_apn) def _toggle_cellular_metered(self): - metered = self._cellular_metered_action.state + metered = self._cellular_metered_action.get_state() self._params.put_bool("GsmMetered", metered) self._wifi_manager.update_gsm_settings(self._params.get_bool("GsmRoaming"), self._params.get("GsmApn") or "", metered) From dc889587ced887e8f0380a6adc8ce12797eef3fa Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 23 Oct 2025 22:25:54 -0700 Subject: [PATCH 245/341] bump version to 0.10.2 --- RELEASES.md | 3 +++ common/version.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 558d322094..9ab60e6cb7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,6 @@ +Version 0.10.2 (2025-11-23) +======================== + Version 0.10.1 (2025-09-08) ======================== * New driving model #36276 diff --git a/common/version.h b/common/version.h index 669f303211..ef20670781 100644 --- a/common/version.h +++ b/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.10.1" +#define COMMA_VERSION "0.10.2" From 53ff5413cda11d15ec412a103c0472b29063702c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 24 Oct 2025 23:50:32 +0800 Subject: [PATCH 246/341] ui: auto-size on PC if screen is smaller than tici (#36452) auto-scale on PC to fit screen --- system/ui/lib/application.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 68e1fdc44a..4ed59f1942 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -140,7 +140,12 @@ class GuiApplication: self._fonts: dict[FontWeight, rl.Font] = {} self._width = width self._height = height - self._scale = SCALE + + if PC and SCALE == 1.0: + self._scale = self._calculate_auto_scale() + else: + self._scale = SCALE + self._scaled_width = int(self._width * self._scale) self._scaled_height = int(self._height * self._scale) self._render_texture: rl.RenderTexture | None = None @@ -460,5 +465,17 @@ class GuiApplication: cloudlog.error(f"FPS dropped critically below {fps}. Shutting down UI.") os._exit(1) + def _calculate_auto_scale(self) -> float: + # Create temporary window to query monitor info + rl.init_window(1, 1, "") + w, h = rl.get_monitor_width(0), rl.get_monitor_height(0) + rl.close_window() + + if w == 0 or h == 0 or (w >= self._width and h >= self._height): + return 1.0 + + # Apply 0.95 factor for window decorations/taskbar margin + return max(0.3, min(w / self._width, h / self._height) * 0.95) + gui_app = GuiApplication(2160, 1080) From 40a1af97b91f372f27f37d715d790cc5eaf183ec Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:12:45 -0500 Subject: [PATCH 247/341] ui: only auto scale on PC if SCALE env not set (#36455) only use auto scale if SCALE env not set --- system/ui/lib/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 4ed59f1942..54a18d96d3 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -141,7 +141,7 @@ class GuiApplication: self._width = width self._height = height - if PC and SCALE == 1.0: + if PC and os.getenv("SCALE") is None: self._scale = self._calculate_auto_scale() else: self._scale = SCALE From f2db7f7665426d93060ecb3f314187bfccd02833 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 25 Oct 2025 10:13:34 +0800 Subject: [PATCH 248/341] ui: cache emoji font to avoid repeated loading (#36451) cache font to avoid repleated loads --- system/ui/lib/emoji.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/emoji.py b/system/ui/lib/emoji.py index 54f742952e..37228e2d45 100644 --- a/system/ui/lib/emoji.py +++ b/system/ui/lib/emoji.py @@ -6,6 +6,7 @@ import pyray as rl from openpilot.system.ui.lib.application import FONT_DIR +_emoji_font: ImageFont.FreeTypeFont | None = None _cache: dict[str, rl.Texture] = {} EMOJI_REGEX = re.compile( @@ -32,6 +33,12 @@ EMOJI_REGEX = re.compile( flags=re.UNICODE ) +def _load_emoji_font() -> ImageFont.FreeTypeFont | None: + global _emoji_font + if _emoji_font is None: + _emoji_font = ImageFont.truetype(str(FONT_DIR.joinpath("NotoColorEmoji.ttf")), 109) + return _emoji_font + def find_emoji(text): return [(m.start(), m.end(), m.group()) for m in EMOJI_REGEX.finditer(text)] @@ -39,8 +46,7 @@ def emoji_tex(emoji): if emoji not in _cache: img = Image.new("RGBA", (128, 128), (0, 0, 0, 0)) draw = ImageDraw.Draw(img) - font = ImageFont.truetype(FONT_DIR.joinpath("NotoColorEmoji.ttf"), 109) - draw.text((0, 0), emoji, font=font, embedded_color=True) + draw.text((0, 0), emoji, font=_load_emoji_font(), embedded_color=True) with io.BytesIO() as buffer: img.save(buffer, format="PNG") l = buffer.tell() From 7da36b2470a4c575dcdc41d4d161db1564974bd1 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:15:14 -0500 Subject: [PATCH 249/341] multilang: fix missing translations in developer panel (#36445) fix: translate description strings in DeveloperLayout settings --- selfdrive/ui/layouts/settings/developer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index 4b8f087968..0419729a4d 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -37,7 +37,7 @@ class DeveloperLayout(Widget): # Build items and keep references for callbacks/state updates self._adb_toggle = toggle_item( lambda: tr("Enable ADB"), - description=lambda: DESCRIPTIONS["enable_adb"], + description=lambda: tr(DESCRIPTIONS["enable_adb"]), initial_state=self._params.get_bool("AdbEnabled"), callback=self._on_enable_adb, enabled=ui_state.is_offroad, @@ -50,7 +50,7 @@ class DeveloperLayout(Widget): initial_state=self._params.get_bool("SshEnabled"), callback=self._on_enable_ssh, ) - self._ssh_keys = ssh_key_item(lambda: tr("SSH Keys"), description=lambda: DESCRIPTIONS["ssh_key"]) + self._ssh_keys = ssh_key_item(lambda: tr("SSH Keys"), description=lambda: tr(DESCRIPTIONS["ssh_key"])) self._joystick_toggle = toggle_item( lambda: tr("Joystick Debug Mode"), @@ -69,7 +69,7 @@ class DeveloperLayout(Widget): self._alpha_long_toggle = toggle_item( lambda: tr("openpilot Longitudinal Control (Alpha)"), - description=lambda: DESCRIPTIONS["alpha_longitudinal"], + description=lambda: tr(DESCRIPTIONS["alpha_longitudinal"]), initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), callback=self._on_alpha_long_enabled, enabled=lambda: not ui_state.engaged, From 534f096bb84aab7ea5c5bcb7811a60d7fe6cb272 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 25 Oct 2025 10:27:03 +0800 Subject: [PATCH 250/341] ui: reset cached height when description changes (#36454) reset cached height when description changes --- system/ui/widgets/list_view.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index e5f234ed39..ed087c3f13 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -330,6 +330,8 @@ class ListItem(Widget): # do callback first in case receiver changes description if self.description_visible and self.description_opened_callback is not None: self.description_opened_callback() + # Call _update_state to catch any description changes + self._update_state() content_width = int(self._rect.width - ITEM_PADDING * 2) self._rect.height = self.get_item_height(self._font, content_width) From c8c1b0f7819ee7c1adfc7fc47df65529c361607d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 25 Oct 2025 10:27:33 +0800 Subject: [PATCH 251/341] ui: clear available camera streams after going offroad (#36441) clear available camera streams after going offroad --- selfdrive/ui/onroad/cameraview.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index 5098b6a06c..98dd00ebaa 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -114,6 +114,7 @@ class CameraView(Widget): # which drains the VisionIpcClient SubSocket for us. Re-connecting is not enough # and only clears internal buffers, not the message queue. self.frame = None + self.available_streams.clear() if self.client: del self.client self.client = VisionIpcClient(self._name, self._stream_type, conflate=True) From ad903aeaa13978ee95a96aeb628cd1bb27f473c2 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 25 Oct 2025 10:28:58 +0800 Subject: [PATCH 252/341] ui: simplify draw_border (#36440) simplify draw_border --- selfdrive/ui/onroad/augmented_road_view.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 661496a032..05e2f094ab 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -112,26 +112,13 @@ class AugmentedRoadView(CameraView): pass def _draw_border(self, rect: rl.Rectangle): - rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) + rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, rl.BLACK) border_roundness = 0.12 border_color = BORDER_COLORS.get(ui_state.status, BORDER_COLORS[UIStatus.DISENGAGED]) border_rect = rl.Rectangle(rect.x + UI_BORDER_SIZE, rect.y + UI_BORDER_SIZE, rect.width - 2 * UI_BORDER_SIZE, rect.height - 2 * UI_BORDER_SIZE) rl.draw_rectangle_rounded_lines_ex(border_rect, border_roundness, 10, UI_BORDER_SIZE, border_color) - # black bg around colored border - black_bg_thickness = UI_BORDER_SIZE - black_bg_rect = rl.Rectangle( - border_rect.x - UI_BORDER_SIZE, - border_rect.y - UI_BORDER_SIZE, - border_rect.width + 2 * UI_BORDER_SIZE, - border_rect.height + 2 * UI_BORDER_SIZE, - ) - edge_offset = (black_bg_rect.height - border_rect.height) / 2 # distance between rect edges - roundness_out = (border_roundness * border_rect.height + 2 * edge_offset) / max(1.0, black_bg_rect.height) - rl.draw_rectangle_rounded_lines_ex(black_bg_rect, roundness_out, 10, black_bg_thickness, rl.BLACK) - rl.end_scissor_mode() - def _switch_stream_if_needed(self, sm): if sm['selfdriveState'].experimentalMode and WIDE_CAM in self.available_streams: v_ego = sm['carState'].vEgo From 6061476d8e14e0a6f6646074ef88a7b55c536cb2 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 24 Oct 2025 19:43:46 -0700 Subject: [PATCH 253/341] fix spinner (#36458) --- system/ui/lib/multilang.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/system/ui/lib/multilang.py b/system/ui/lib/multilang.py index 03d519e601..bb9461864c 100644 --- a/system/ui/lib/multilang.py +++ b/system/ui/lib/multilang.py @@ -1,10 +1,14 @@ import os import json import gettext -from openpilot.common.params import Params from openpilot.common.basedir import BASEDIR from openpilot.common.swaglog import cloudlog +try: + from openpilot.common.params import Params +except ImportError: + Params = None + SYSTEM_UI_DIR = os.path.join(BASEDIR, "system", "ui") UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui") TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations") @@ -22,7 +26,7 @@ UNIFONT_LANGUAGES = [ class Multilang: def __init__(self): - self._params = Params() + self._params = Params() if Params is not None else None self._language: str = "en" self.languages = {} self.codes = {} @@ -66,9 +70,10 @@ class Multilang: self.languages = json.load(f) self.codes = {v: k for k, v in self.languages.items()} - lang = str(self._params.get("LanguageSetting")).removeprefix("main_") - if lang in self.codes: - self._language = lang + if self._params is not None: + lang = str(self._params.get("LanguageSetting")).removeprefix("main_") + if lang in self.codes: + self._language = lang multilang = Multilang() From 954b567b9ba0f3d1ae57d6aa7797fa86dd92ec6e Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 24 Oct 2025 20:45:56 -0700 Subject: [PATCH 254/341] merge a bunch of misc stuff into common.utils (#36463) just utils --- common/dict_helpers.py | 9 --- common/git.py | 2 +- common/retry.py | 30 --------- common/run.py | 28 -------- common/tests/test_file_helpers.py | 2 +- common/{file_helpers.py => utils.py} | 64 ++++++++++++++++++- selfdrive/debug/qlog_size.py | 2 +- .../pandad/tests/test_pandad_loopback.py | 2 +- selfdrive/ui/soundd.py | 2 +- system/athena/athenad.py | 2 +- system/hardware/hardwared.py | 2 +- system/loggerd/uploader.py | 2 +- system/micd.py | 2 +- system/qcomgpsd/qcomgpsd.py | 2 +- system/statsd.py | 2 +- system/ui/setup.py | 2 +- tools/clip/run.py | 2 +- tools/lib/filereader.py | 2 +- tools/lib/url_file.py | 2 +- 19 files changed, 77 insertions(+), 84 deletions(-) delete mode 100644 common/dict_helpers.py delete mode 100644 common/retry.py delete mode 100644 common/run.py rename common/{file_helpers.py => utils.py} (51%) diff --git a/common/dict_helpers.py b/common/dict_helpers.py deleted file mode 100644 index 62cff63b58..0000000000 --- a/common/dict_helpers.py +++ /dev/null @@ -1,9 +0,0 @@ -# remove all keys that end in DEPRECATED -def strip_deprecated_keys(d): - for k in list(d.keys()): - if isinstance(k, str): - if k.endswith('DEPRECATED'): - d.pop(k) - elif isinstance(d[k], dict): - strip_deprecated_keys(d[k]) - return d diff --git a/common/git.py b/common/git.py index 4406bf96b1..2296fa7088 100644 --- a/common/git.py +++ b/common/git.py @@ -1,6 +1,6 @@ from functools import cache import subprocess -from openpilot.common.run import run_cmd, run_cmd_default +from openpilot.common.utils import run_cmd, run_cmd_default @cache diff --git a/common/retry.py b/common/retry.py deleted file mode 100644 index 9bd4ac9522..0000000000 --- a/common/retry.py +++ /dev/null @@ -1,30 +0,0 @@ -import time -import functools - -from openpilot.common.swaglog import cloudlog - - -def retry(attempts=3, delay=1.0, ignore_failure=False): - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - for _ in range(attempts): - try: - return func(*args, **kwargs) - except Exception: - cloudlog.exception(f"{func.__name__} failed, trying again") - time.sleep(delay) - - if ignore_failure: - cloudlog.error(f"{func.__name__} failed after retry") - else: - raise Exception(f"{func.__name__} failed after retry") - return wrapper - return decorator - - -if __name__ == "__main__": - @retry(attempts=10) - def abc(): - raise ValueError("abc failed :(") - abc() diff --git a/common/run.py b/common/run.py deleted file mode 100644 index 75395ead1f..0000000000 --- a/common/run.py +++ /dev/null @@ -1,28 +0,0 @@ -import subprocess -from contextlib import contextmanager -from subprocess import Popen, PIPE, TimeoutExpired - - -def run_cmd(cmd: list[str], cwd=None, env=None) -> str: - return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip() - - -def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str: - try: - return run_cmd(cmd, cwd=cwd, env=env) - except subprocess.CalledProcessError: - return default - - -@contextmanager -def managed_proc(cmd: list[str], env: dict[str, str]): - proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE) - try: - yield proc - finally: - if proc.poll() is None: - proc.terminate() - try: - proc.wait(timeout=5) - except TimeoutExpired: - proc.kill() diff --git a/common/tests/test_file_helpers.py b/common/tests/test_file_helpers.py index a9977c2362..c7fe1984c5 100644 --- a/common/tests/test_file_helpers.py +++ b/common/tests/test_file_helpers.py @@ -1,7 +1,7 @@ import os from uuid import uuid4 -from openpilot.common.file_helpers import atomic_write_in_dir +from openpilot.common.utils import atomic_write_in_dir class TestFileHelpers: diff --git a/common/file_helpers.py b/common/utils.py similarity index 51% rename from common/file_helpers.py rename to common/utils.py index b0d889f163..89c0601f06 100644 --- a/common/file_helpers.py +++ b/common/utils.py @@ -2,9 +2,14 @@ import io import os import tempfile import contextlib +import subprocess +import time +import functools +from subprocess import Popen, PIPE, TimeoutExpired import zstandard as zstd +from openpilot.common.swaglog import cloudlog -LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change +LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change class CallbackReader: @@ -27,7 +32,7 @@ class CallbackReader: @contextlib.contextmanager -def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str = None, newline: str = None, +def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str | None = None, newline: str | None = None, overwrite: bool = False): """Write to a file atomically using a temporary file in the same directory as the destination file.""" dir_name = os.path.dirname(path) @@ -56,3 +61,58 @@ def get_upload_stream(filepath: str, should_compress: bool) -> tuple[io.Buffered compressed_size = compressed_stream.tell() compressed_stream.seek(0) return compressed_stream, compressed_size + + +# remove all keys that end in DEPRECATED +def strip_deprecated_keys(d): + for k in list(d.keys()): + if isinstance(k, str): + if k.endswith('DEPRECATED'): + d.pop(k) + elif isinstance(d[k], dict): + strip_deprecated_keys(d[k]) + return d + + +def run_cmd(cmd: list[str], cwd=None, env=None) -> str: + return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip() + + +def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str: + try: + return run_cmd(cmd, cwd=cwd, env=env) + except subprocess.CalledProcessError: + return default + + +@contextlib.contextmanager +def managed_proc(cmd: list[str], env: dict[str, str]): + proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE) + try: + yield proc + finally: + if proc.poll() is None: + proc.terminate() + try: + proc.wait(timeout=5) + except TimeoutExpired: + proc.kill() + + +def retry(attempts=3, delay=1.0, ignore_failure=False): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + for _ in range(attempts): + try: + return func(*args, **kwargs) + except Exception: + cloudlog.exception(f"{func.__name__} failed, trying again") + time.sleep(delay) + + if ignore_failure: + cloudlog.error(f"{func.__name__} failed after retry") + else: + raise Exception(f"{func.__name__} failed after retry") + return wrapper + return decorator diff --git a/selfdrive/debug/qlog_size.py b/selfdrive/debug/qlog_size.py index 6d494b6f75..2b54cfeebf 100755 --- a/selfdrive/debug/qlog_size.py +++ b/selfdrive/debug/qlog_size.py @@ -6,7 +6,7 @@ from collections import defaultdict import matplotlib.pyplot as plt from cereal.services import SERVICE_LIST -from openpilot.common.file_helpers import LOG_COMPRESSION_LEVEL +from openpilot.common.utils import LOG_COMPRESSION_LEVEL from openpilot.tools.lib.logreader import LogReader from tqdm import tqdm diff --git a/selfdrive/pandad/tests/test_pandad_loopback.py b/selfdrive/pandad/tests/test_pandad_loopback.py index bf1c557128..eff70d2544 100644 --- a/selfdrive/pandad/tests/test_pandad_loopback.py +++ b/selfdrive/pandad/tests/test_pandad_loopback.py @@ -9,7 +9,7 @@ from pprint import pprint import cereal.messaging as messaging from cereal import car, log from opendbc.car.can_definitions import CanData -from openpilot.common.retry import retry +from openpilot.common.utils import retry from openpilot.common.params import Params from openpilot.common.timeout import Timeout from openpilot.selfdrive.pandad import can_list_to_can_capnp diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index 44c463b18c..44116a2329 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -8,7 +8,7 @@ from cereal import car, messaging from openpilot.common.basedir import BASEDIR from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.realtime import Ratekeeper -from openpilot.common.retry import retry +from openpilot.common.utils import retry from openpilot.common.swaglog import cloudlog from openpilot.system import micd diff --git a/system/athena/athenad.py b/system/athena/athenad.py index 6ed53b759c..50c4f5408f 100755 --- a/system/athena/athenad.py +++ b/system/athena/athenad.py @@ -31,7 +31,7 @@ import cereal.messaging as messaging from cereal import log from cereal.services import SERVICE_LIST from openpilot.common.api import Api -from openpilot.common.file_helpers import CallbackReader, get_upload_stream +from openpilot.common.utils import CallbackReader, get_upload_stream from openpilot.common.params import Params from openpilot.common.realtime import set_core_affinity from openpilot.system.hardware import HARDWARE, PC diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index 1048acfe0a..1dce99273a 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -12,7 +12,7 @@ import psutil import cereal.messaging as messaging from cereal import log from cereal.services import SERVICE_LIST -from openpilot.common.dict_helpers import strip_deprecated_keys +from openpilot.common.utils import strip_deprecated_keys from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.common.realtime import DT_HW diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py index bc19572507..5b6234e1d5 100755 --- a/system/loggerd/uploader.py +++ b/system/loggerd/uploader.py @@ -12,7 +12,7 @@ from collections.abc import Iterator from cereal import log import cereal.messaging as messaging from openpilot.common.api import Api -from openpilot.common.file_helpers import get_upload_stream +from openpilot.common.utils import get_upload_stream from openpilot.common.params import Params from openpilot.common.realtime import set_core_affinity from openpilot.system.hardware.hw import Paths diff --git a/system/micd.py b/system/micd.py index b3558a15a8..9b3ccc8d29 100755 --- a/system/micd.py +++ b/system/micd.py @@ -5,7 +5,7 @@ import threading from cereal import messaging from openpilot.common.realtime import Ratekeeper -from openpilot.common.retry import retry +from openpilot.common.utils import retry from openpilot.common.swaglog import cloudlog RATE = 10 diff --git a/system/qcomgpsd/qcomgpsd.py b/system/qcomgpsd/qcomgpsd.py index 819b17f113..59f5ac0b50 100755 --- a/system/qcomgpsd/qcomgpsd.py +++ b/system/qcomgpsd/qcomgpsd.py @@ -16,7 +16,7 @@ from struct import unpack_from, calcsize, pack from cereal import log import cereal.messaging as messaging from openpilot.common.gpio import gpio_init, gpio_set -from openpilot.common.retry import retry +from openpilot.common.utils import retry from openpilot.common.time_helpers import system_time_valid from openpilot.system.hardware.tici.pins import GPIO from openpilot.common.swaglog import cloudlog diff --git a/system/statsd.py b/system/statsd.py index d60064fc91..c4216f5e76 100755 --- a/system/statsd.py +++ b/system/statsd.py @@ -13,7 +13,7 @@ from cereal.messaging import SubMaster from openpilot.system.hardware.hw import Paths from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE -from openpilot.common.file_helpers import atomic_write_in_dir +from openpilot.common.utils import atomic_write_in_dir from openpilot.system.version import get_build_metadata from openpilot.system.loggerd.config import STATS_DIR_FILE_LIMIT, STATS_SOCKET, STATS_FLUSH_TIME_S diff --git a/system/ui/setup.py b/system/ui/setup.py index 5dbf597484..da8c8d81fb 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -11,7 +11,7 @@ import shutil import pyray as rl from cereal import log -from openpilot.common.run import run_cmd +from openpilot.common.utils import run_cmd from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE diff --git a/tools/clip/run.py b/tools/clip/run.py index 8fa0e8eda3..9045a4381b 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -17,7 +17,7 @@ from cereal.messaging import SubMaster from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params, UnknownKeyName from openpilot.common.prefix import OpenpilotPrefix -from openpilot.common.run import managed_proc +from openpilot.common.utils import managed_proc from openpilot.tools.lib.route import Route from openpilot.tools.lib.logreader import LogReader diff --git a/tools/lib/filereader.py b/tools/lib/filereader.py index 02f5fd1b95..ee9ee294bb 100644 --- a/tools/lib/filereader.py +++ b/tools/lib/filereader.py @@ -2,7 +2,7 @@ import os import posixpath import socket from functools import cache -from openpilot.common.retry import retry +from openpilot.common.utils import retry from urllib.parse import urlparse from openpilot.tools.lib.url_file import URLFile diff --git a/tools/lib/url_file.py b/tools/lib/url_file.py index e80ba1399d..31c1e0ff11 100644 --- a/tools/lib/url_file.py +++ b/tools/lib/url_file.py @@ -7,7 +7,7 @@ from urllib3 import PoolManager, Retry from urllib3.response import BaseHTTPResponse from urllib3.util import Timeout -from openpilot.common.file_helpers import atomic_write_in_dir +from openpilot.common.utils import atomic_write_in_dir from openpilot.system.hardware.hw import Paths # Cache chunk size From 17152484c2f3e41a9ae892a83bf27761552ceb0e Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 24 Oct 2025 20:54:13 -0700 Subject: [PATCH 255/341] selfdrive_tests -> tests --- .github/workflows/{selfdrive_tests.yaml => tests.yaml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{selfdrive_tests.yaml => tests.yaml} (97%) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/tests.yaml similarity index 97% rename from .github/workflows/selfdrive_tests.yaml rename to .github/workflows/tests.yaml index 3f60fab73d..5ca020424c 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,4 +1,4 @@ -name: selfdrive +name: tests on: push: @@ -14,7 +14,7 @@ on: type: string concurrency: - group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }} + group: tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }} cancel-in-progress: true env: From 538ec25ad97079a186aa537927e286212751b76d Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 24 Oct 2025 21:07:04 -0700 Subject: [PATCH 256/341] gc unused stuff in HW abstraction layer (#36465) * gc unused stuff in HW abstraction layer * lil more --- system/hardware/base.h | 8 -------- system/hardware/tici/hardware.h | 19 ------------------- tools/replay/framereader.cc | 2 +- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/system/hardware/base.h b/system/hardware/base.h index df9700a017..3eded659ac 100644 --- a/system/hardware/base.h +++ b/system/hardware/base.h @@ -10,7 +10,6 @@ // no-op base hw class class HardwareNone { public: - static std::string get_os_version() { return ""; } static std::string get_name() { return ""; } static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::UNKNOWN; } static int get_voltage() { return 0; } @@ -22,14 +21,7 @@ public: return {}; } - static void reboot() {} - static void poweroff() {} - static void set_brightness(int percent) {} static void set_ir_power(int percentage) {} - static void set_display_power(bool on) {} - - static bool get_ssh_enabled() { return false; } - static void set_ssh_enabled(bool enabled) {} static bool PC() { return false; } static bool TICI() { return false; } diff --git a/system/hardware/tici/hardware.h b/system/hardware/tici/hardware.h index ed8a7e7d17..8a0c066942 100644 --- a/system/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -13,12 +13,6 @@ class HardwareTici : public HardwareNone { public: - static bool TICI() { return true; } - static bool AGNOS() { return true; } - static std::string get_os_version() { - return "AGNOS " + util::read_file("/VERSION"); - } - static std::string get_name() { std::string model = util::read_file("/sys/firmware/devicetree/base/model"); return util::strip(model.substr(std::string("comma ").size())); @@ -56,16 +50,6 @@ public: return serial; } - static void reboot() { std::system("sudo reboot"); } - static void poweroff() { std::system("sudo poweroff"); } - static void set_brightness(int percent) { - float max = std::stof(util::read_file("/sys/class/backlight/panel0-backlight/max_brightness")); - std::ofstream("/sys/class/backlight/panel0-backlight/brightness") << int(percent * (max / 100.0f)) << "\n"; - } - static void set_display_power(bool on) { - std::ofstream("/sys/class/backlight/panel0-backlight/bl_power") << (on ? "0" : "4") << "\n"; - } - static void set_ir_power(int percent) { auto device = get_device_type(); if (device == cereal::InitData::DeviceType::TICI || @@ -104,7 +88,4 @@ public: return ret; } - - static bool get_ssh_enabled() { return Params().getBool("SshEnabled"); } - static void set_ssh_enabled(bool enabled) { Params().putBool("SshEnabled", enabled); } }; diff --git a/tools/replay/framereader.cc b/tools/replay/framereader.cc index e9cd090446..a5f0f748c7 100644 --- a/tools/replay/framereader.cc +++ b/tools/replay/framereader.cc @@ -40,7 +40,7 @@ struct DecoderManager { std::unique_ptr decoder; #ifndef __APPLE__ - if (Hardware::TICI() && hw_decoder) { + if (!Hardware::PC() && hw_decoder) { decoder = std::make_unique(); } else #endif From c1cb971bcaf622681aefd1fc87f95f808d758821 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 24 Oct 2025 21:34:37 -0700 Subject: [PATCH 257/341] hardwared: disable power save when screen is on (#36466) --- system/hardware/hardwared.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index 1dce99273a..4d52d78417 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -195,6 +195,7 @@ def hardware_thread(end_event, hw_queue) -> None: should_start_prev = False in_car = False engaged_prev = False + pwrsave = False offroad_cycle_count = 0 params = Params() @@ -341,7 +342,6 @@ def hardware_thread(end_event, hw_queue) -> None: if should_start != should_start_prev or (count == 0): params.put_bool("IsEngaged", False) engaged_prev = False - HARDWARE.set_power_save(not should_start) if sm.updated['selfdriveState']: engaged = sm['selfdriveState'].enabled @@ -355,6 +355,11 @@ def hardware_thread(end_event, hw_queue) -> None: except Exception: pass + should_pwrsave = not onroad_conditions["ignition"] and msg.deviceState.screenBrightnessPercent < 1e-3 + if should_pwrsave != pwrsave or (count == 0): + HARDWARE.set_power_save(should_pwrsave) + pwrsave = should_pwrsave + if should_start: off_ts = None if started_ts is None: From 7909716c1faa077a0fe6e2fabfc852d1b57f4410 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 24 Oct 2025 21:57:45 -0700 Subject: [PATCH 258/341] ui: realtime scheduling (#36467) * ui: realtime scheduling * try this * update cpu --------- Co-authored-by: Comma Device --- selfdrive/test/test_onroad.py | 2 +- selfdrive/ui/ui.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 0c3bdea4c8..a842483857 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -42,7 +42,7 @@ PROCS = { "./encoderd": 13.0, "./camerad": 10.0, "selfdrive.controls.plannerd": 8.0, - "selfdrive.ui.ui": 24.0, + "selfdrive.ui.ui": 63.0, "system.sensord.sensord": 13.0, "selfdrive.controls.radard": 2.0, "selfdrive.modeld.modeld": 22.0, diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index 3eb72ec104..f2ca4e7700 100755 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -1,13 +1,14 @@ #!/usr/bin/env python3 import pyray as rl + +from openpilot.common.realtime import config_realtime_process from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.main import MainLayout from openpilot.selfdrive.ui.ui_state import ui_state def main(): - # TODO: https://github.com/commaai/agnos-builder/pull/490 - # os.nice(-20) + config_realtime_process([1, 2], 1) gui_app.init_window("UI") main_layout = MainLayout() From fa373af9b5992a125cd97fd1d1f4767ac8e94604 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 25 Oct 2025 13:00:38 +0800 Subject: [PATCH 259/341] ui: cleanup .gitignore (#36471) cleanup .gitignore --- selfdrive/ui/.gitignore | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/selfdrive/ui/.gitignore b/selfdrive/ui/.gitignore index 5e7b02a1a3..945928f617 100644 --- a/selfdrive/ui/.gitignore +++ b/selfdrive/ui/.gitignore @@ -1,14 +1 @@ -moc_* -*.moc - -translations/test_en.* - -ui -mui -watch3 installer/installers/* -qt/setup/setup -qt/setup/reset -qt/setup/wifi -qt/setup/updater -translations/alerts_generated.h From 2beb0ffad118a9e4cbe33a440fb43fd2ff00e66b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 25 Oct 2025 13:00:45 +0800 Subject: [PATCH 260/341] ui: move INF_POINT to constant (#36470) move INF_POINT to constant --- selfdrive/ui/onroad/augmented_road_view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 05e2f094ab..fa0528444d 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -27,6 +27,7 @@ BORDER_COLORS = { WIDE_CAM_MAX_SPEED = 10.0 # m/s (22 mph) ROAD_CAM_MIN_SPEED = 15.0 # m/s (34 mph) +INF_POINT = np.array([1000.0, 0.0, 0.0]) class AugmentedRoadView(CameraView): @@ -176,9 +177,8 @@ class AugmentedRoadView(CameraView): zoom = 2.0 if is_wide_camera else 1.1 # Calculate transforms for vanishing point - inf_point = np.array([1000.0, 0.0, 0.0]) calib_transform = intrinsic @ calibration - kep = calib_transform @ inf_point + kep = calib_transform @ INF_POINT # Calculate center points and dimensions x, y = self._content_rect.x, self._content_rect.y From 5e2f1427046c02669e0fbf766176f05e0873169f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 24 Oct 2025 22:02:25 -0700 Subject: [PATCH 261/341] increase CPU budget --- 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 a842483857..f90627e10c 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -32,7 +32,7 @@ CPU usage budget TEST_DURATION = 25 LOG_OFFSET = 8 -MAX_TOTAL_CPU = 315. # total for all 8 cores +MAX_TOTAL_CPU = 350. # total for all 8 cores PROCS = { # Baseline CPU usage by process "selfdrive.controls.controlsd": 16.0, From e0cabc117475d0dbe68862ee62bd7b8dbe0e3f28 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 24 Oct 2025 23:56:59 -0700 Subject: [PATCH 262/341] raylib: only load glyphs for unifont (#36475) * again llm is terrible * Revert "again llm is terrible" This reverts commit 423dd289ae5701e2f5bb034efd9329175fc275cc. * try this --- system/ui/lib/application.py | 39 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 54a18d96d3..f09344a9e2 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -14,7 +14,7 @@ from typing import NamedTuple from importlib.resources import as_file, files from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE, PC, TICI -from openpilot.system.ui.lib.multilang import TRANSLATIONS_DIR, multilang +from openpilot.system.ui.lib.multilang import TRANSLATIONS_DIR, UNIFONT_LANGUAGES, multilang from openpilot.common.realtime import Ratekeeper _DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) @@ -363,33 +363,48 @@ class GuiApplication: # Create a character set from our keyboard layouts from openpilot.system.ui.widgets.keyboard import KEYBOARD_LAYOUTS - all_chars = set() + base_chars = set() for layout in KEYBOARD_LAYOUTS.values(): - all_chars.update(key for row in layout for key in row) - all_chars |= set("–‑✓×°§•") + base_chars.update(key for row in layout for key in row) + base_chars |= set("–‑✓×°§•") # Load only the characters used in translations + unifont_chars = set(base_chars) for language, code in multilang.languages.items(): - all_chars |= set(language) + unifont_chars |= set(language) try: with open(os.path.join(TRANSLATIONS_DIR, f"app_{code}.po")) as f: - all_chars |= set(f.read()) + lang_chars = set(f.read()) + if code in UNIFONT_LANGUAGES: + unifont_chars |= lang_chars + else: + base_chars |= lang_chars except FileNotFoundError: cloudlog.warning(f"Translation file for language '{code}' not found when loading fonts.") - all_chars = "".join(all_chars) - cloudlog.debug(f"Loading fonts with {len(all_chars)} glyphs.") + base_chars = "".join(base_chars) + cloudlog.debug(f"Loading fonts with {len(base_chars)} glyphs.") - codepoint_count = rl.ffi.new("int *", 1) - codepoints = rl.load_codepoints(all_chars, codepoint_count) + unifont_chars = "".join(unifont_chars) + cloudlog.debug(f"Loading unifont with {len(unifont_chars)} glyphs.") + + base_codepoint_count = rl.ffi.new("int *", 1) + base_codepoints = rl.load_codepoints(base_chars, base_codepoint_count) + + unifont_codepoint_count = rl.ffi.new("int *", 1) + unifont_codepoints = rl.load_codepoints(unifont_chars, unifont_codepoint_count) for font_weight_file in FontWeight: with as_file(FONT_DIR.joinpath(font_weight_file)) as fspath: - font = rl.load_font_ex(fspath.as_posix(), 200, codepoints, codepoint_count[0]) + if font_weight_file == FontWeight.UNIFONT: + font = rl.load_font_ex(fspath.as_posix(), 200, unifont_codepoints, unifont_codepoint_count[0]) + else: + font = rl.load_font_ex(fspath.as_posix(), 200, base_codepoints, base_codepoint_count[0]) rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) self._fonts[font_weight_file] = font - rl.unload_codepoints(codepoints) + rl.unload_codepoints(base_codepoints) + rl.unload_codepoints(unifont_codepoints) rl.gui_set_font(self._fonts[FontWeight.NORMAL]) def _set_styles(self): From e92e59ca78e34781ad14c5e7b36b0da96a0d883d Mon Sep 17 00:00:00 2001 From: eFini Date: Sun, 26 Oct 2025 00:17:59 +0800 Subject: [PATCH 263/341] multilang: add gettext package (#36476) needed for gettext --- tools/install_ubuntu_dependencies.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index 97e06bcc6b..fdd21067ec 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -66,7 +66,8 @@ function install_ubuntu_common_requirements() { libqt5svg5-dev \ libqt5serialbus5-dev \ libqt5x11extras5-dev \ - libqt5opengl5-dev + libqt5opengl5-dev \ + gettext } # Install Ubuntu 24.04 LTS packages From 94ca077e69e152ca849a27d1582b4885b1f994af Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 25 Oct 2025 12:27:01 -0700 Subject: [PATCH 264/341] ui: add startup profiling (#36482) * ui: add startup profiling * lil more --- system/ui/lib/application.py | 82 +++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index f09344a9e2..f7450cbd72 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -6,6 +6,7 @@ import signal import sys import pyray as rl import threading +from contextlib import contextmanager from collections.abc import Callable from collections import deque from dataclasses import dataclass @@ -170,37 +171,68 @@ class GuiApplication: self._window_close_requested = True def init_window(self, title: str, fps: int = _DEFAULT_FPS): - def _close(sig, frame): - self.close() - sys.exit(0) - signal.signal(signal.SIGINT, _close) - atexit.register(self.close) + with self._startup_profile_context(): + def _close(sig, frame): + self.close() + sys.exit(0) + signal.signal(signal.SIGINT, _close) + atexit.register(self.close) - HARDWARE.set_display_power(True) - HARDWARE.set_screen_brightness(65) + HARDWARE.set_display_power(True) + HARDWARE.set_screen_brightness(65) - self._set_log_callback() - rl.set_trace_log_level(rl.TraceLogLevel.LOG_WARNING) + self._set_log_callback() + rl.set_trace_log_level(rl.TraceLogLevel.LOG_WARNING) - flags = rl.ConfigFlags.FLAG_MSAA_4X_HINT - if ENABLE_VSYNC: - flags |= rl.ConfigFlags.FLAG_VSYNC_HINT - rl.set_config_flags(flags) + flags = rl.ConfigFlags.FLAG_MSAA_4X_HINT + if ENABLE_VSYNC: + flags |= rl.ConfigFlags.FLAG_VSYNC_HINT + rl.set_config_flags(flags) - rl.init_window(self._scaled_width, self._scaled_height, title) - if self._scale != 1.0: - rl.set_mouse_scale(1 / self._scale, 1 / self._scale) - self._render_texture = rl.load_render_texture(self._width, self._height) - rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) - rl.set_target_fps(fps) + rl.init_window(self._scaled_width, self._scaled_height, title) + if self._scale != 1.0: + rl.set_mouse_scale(1 / self._scale, 1 / self._scale) + self._render_texture = rl.load_render_texture(self._width, self._height) + rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) + rl.set_target_fps(fps) - self._target_fps = fps - self._set_styles() - self._load_fonts() - self._patch_text_functions() + self._target_fps = fps + self._set_styles() + self._load_fonts() + self._patch_text_functions() - if not PC: - self._mouse.start() + if not PC: + self._mouse.start() + + @contextmanager + def _startup_profile_context(self): + if "PROFILE_STARTUP" not in os.environ: + yield + return + + import cProfile + import io + import pstats + + profiler = cProfile.Profile() + start_time = time.monotonic() + profiler.enable() + + # do the init + yield + + profiler.disable() + elapsed_ms = (time.monotonic() - start_time) * 1e3 + + stats_stream = io.StringIO() + pstats.Stats(profiler, stream=stats_stream).sort_stats("cumtime").print_stats(25) + print("\n=== Startup profile ===") + print(stats_stream.getvalue().rstrip()) + + green = "\033[92m" + reset = "\033[0m" + print(f"{green}UI window ready in {elapsed_ms:.1f} ms{reset}") + sys.exit(0) def set_modal_overlay(self, overlay, callback: Callable | None = None): if self._modal_overlay.overlay is not None: From f0dd0b5c8c32045f93f0a22523ed29b2f45f160b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 26 Oct 2025 00:47:07 -0700 Subject: [PATCH 265/341] raylib ui: assert system time valid (#36486) * assert system time valid * nl --- selfdrive/ui/lib/api_helpers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/ui/lib/api_helpers.py b/selfdrive/ui/lib/api_helpers.py index b83efedb60..8ed1c22a63 100644 --- a/selfdrive/ui/lib/api_helpers.py +++ b/selfdrive/ui/lib/api_helpers.py @@ -1,12 +1,16 @@ import time from functools import lru_cache from openpilot.common.api import Api +from openpilot.common.time_helpers import system_time_valid TOKEN_EXPIRY_HOURS = 2 @lru_cache(maxsize=1) def _get_token(dongle_id: str, t: int): + if not system_time_valid(): + raise RuntimeError("System time is not valid, cannot generate token") + return Api(dongle_id).get_token(expiry_hours=TOKEN_EXPIRY_HOURS) From 03cb3e9dc035ecdf7d699ab635651c8ffcb985b8 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 26 Oct 2025 12:15:34 -0700 Subject: [PATCH 266/341] make ruff happy :) --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8cb877c351..f2d8c5cad0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -235,7 +235,6 @@ lint.ignore = [ "B027", "B024", "NPY002", # new numpy random syntax is worse - "UP038", # (x, y) -> x|y for isinstance ] line-length = 160 target-version ="py311" From 0d4b0ee116d92f3b017cd3978c4c643b09a02c59 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 26 Oct 2025 13:20:11 -0700 Subject: [PATCH 267/341] pre-process fonts for raylib (#36489) * pre-process fonts for raylib * it's fast! * raylib processing * build with scons * padding * happy ruff * all exported * cleanup * more pad --- selfdrive/assets/.gitignore | 2 + selfdrive/assets/fonts/process.py | 124 ++++++++++++++++++++++++++++++ selfdrive/ui/SConscript | 16 ++++ system/ui/lib/application.py | 64 +++------------ 4 files changed, 154 insertions(+), 52 deletions(-) create mode 100755 selfdrive/assets/fonts/process.py diff --git a/selfdrive/assets/.gitignore b/selfdrive/assets/.gitignore index 1f90a2a932..fffd4b4ed9 100644 --- a/selfdrive/assets/.gitignore +++ b/selfdrive/assets/.gitignore @@ -1,2 +1,4 @@ *.cc +fonts/*.fnt +fonts/*.png translations_assets.qrc diff --git a/selfdrive/assets/fonts/process.py b/selfdrive/assets/fonts/process.py new file mode 100755 index 0000000000..f3633f3710 --- /dev/null +++ b/selfdrive/assets/fonts/process.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +from pathlib import Path +import json + +import pyray as rl + +FONT_DIR = Path(__file__).resolve().parent +SELFDRIVE_DIR = FONT_DIR.parents[1] +TRANSLATIONS_DIR = SELFDRIVE_DIR / "ui" / "translations" +LANGUAGES_FILE = TRANSLATIONS_DIR / "languages.json" + +FONT_SIZE = 200 +GLYPH_PADDING = 6 +EXTRA_CHARS = "–‑✓×°§•€£¥" +UNIFONT_LANGUAGES = {"ar", "th", "zh-CHT", "zh-CHS", "ko", "ja"} + + +def _languages(): + if not LANGUAGES_FILE.exists(): + return {} + with LANGUAGES_FILE.open(encoding="utf-8") as f: + return json.load(f) + + +def _char_sets(): + base = set(map(chr, range(32, 127))) | set(EXTRA_CHARS) + unifont = set(base) + + for language, code in _languages().items(): + unifont.update(language) + po_path = TRANSLATIONS_DIR / f"app_{code}.po" + try: + chars = set(po_path.read_text(encoding="utf-8")) + except FileNotFoundError: + continue + (unifont if code in UNIFONT_LANGUAGES else base).update(chars) + + return tuple(sorted(ord(c) for c in base)), tuple(sorted(ord(c) for c in unifont)) + + +def _glyph_metrics(glyphs, rects, codepoints): + entries = [] + min_offset_y, max_extent = None, 0 + for idx, codepoint in enumerate(codepoints): + glyph = glyphs[idx] + rect = rects[idx] + width = int(round(rect.width)) + height = int(round(rect.height)) + offset_y = int(round(glyph.offsetY)) + min_offset_y = offset_y if min_offset_y is None else min(min_offset_y, offset_y) + max_extent = max(max_extent, offset_y + height) + entries.append({ + "id": codepoint, + "x": int(round(rect.x)), + "y": int(round(rect.y)), + "width": width, + "height": height, + "xoffset": int(round(glyph.offsetX)), + "yoffset": offset_y, + "xadvance": int(round(glyph.advanceX)), + }) + + if min_offset_y is None: + raise RuntimeError("No glyphs were generated") + + line_height = int(round(max_extent - min_offset_y)) + base = int(round(max_extent)) + return entries, line_height, base + + +def _write_bmfont(path: Path, face: str, atlas_name: str, line_height: int, base: int, atlas_size, entries): + lines = [ + f"info face=\"{face}\" size=-{FONT_SIZE} bold=0 italic=0 charset=\"\" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0", + f"common lineHeight={line_height} base={base} scaleW={atlas_size[0]} scaleH={atlas_size[1]} pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4", + f"page id=0 file=\"{atlas_name}\"", + f"chars count={len(entries)}", + ] + for entry in entries: + lines.append( + ("char id={id:<4} x={x:<5} y={y:<5} width={width:<5} height={height:<5} " + + "xoffset={xoffset:<5} yoffset={yoffset:<5} xadvance={xadvance:<5} page=0 chnl=15").format(**entry) + ) + path.write_text("\n".join(lines) + "\n") + + +def _process_font(font_path: Path, codepoints: tuple[int, ...]): + print(f"Processing {font_path.name}...") + data = font_path.read_bytes() + file_buf = rl.ffi.new("unsigned char[]", data) + cp_buffer = rl.ffi.new("int[]", codepoints) + cp_ptr = rl.ffi.cast("int *", cp_buffer) + glyphs = rl.load_font_data(rl.ffi.cast("unsigned char *", file_buf), len(data), FONT_SIZE, cp_ptr, len(codepoints), rl.FontType.FONT_DEFAULT) + if glyphs == rl.ffi.NULL: + raise RuntimeError("raylib failed to load font data") + + rects_ptr = rl.ffi.new("Rectangle **") + image = rl.gen_image_font_atlas(glyphs, rects_ptr, len(codepoints), FONT_SIZE, GLYPH_PADDING, 0) + if image.width == 0 or image.height == 0: + raise RuntimeError("raylib returned an empty atlas") + + rects = rects_ptr[0] + atlas_name = f"{font_path.stem}.png" + atlas_path = FONT_DIR / atlas_name + entries, line_height, base = _glyph_metrics(glyphs, rects, codepoints) + + if not rl.export_image(image, atlas_path.as_posix()): + raise RuntimeError("Failed to export atlas image") + + _write_bmfont(FONT_DIR / f"{font_path.stem}.fnt", font_path.stem, atlas_name, line_height, base, (image.width, image.height), entries) + + +def main(): + base_cp, unifont_cp = _char_sets() + fonts = sorted(FONT_DIR.glob("*.ttf")) + sorted(FONT_DIR.glob("*.otf")) + for font in fonts: + if "emoji" in font.name.lower(): + continue + glyphs = unifont_cp if font.stem.lower().startswith("unifont") else base_cp + _process_font(font, glyphs) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 37bf4974df..8a5f5793a5 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,7 +1,23 @@ import os +import re import json +from pathlib import Path Import('env', 'arch', 'common') +# build the fonts +generator = File("#selfdrive/assets/fonts/process.py") +source_files = Glob("#selfdrive/assets/fonts/*.ttf") + Glob("#selfdrive/assets/fonts/*.otf") +output_files = [ + (f.abspath.split('.')[0] + ".fnt", f.abspath.split('.')[0] + ".png") + for f in source_files + if "NotoColor" not in f.name +] +env.Command( + target=output_files, + source=[generator, source_files], + action=f"python3 {generator}", +) + # compile gettext .po -> .mo translations with open(File("translations/languages.json").abspath) as f: languages = json.loads(f.read()) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index f7450cbd72..4d0ae5fa49 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -15,7 +15,7 @@ from typing import NamedTuple from importlib.resources import as_file, files from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE, PC, TICI -from openpilot.system.ui.lib.multilang import TRANSLATIONS_DIR, UNIFONT_LANGUAGES, multilang +from openpilot.system.ui.lib.multilang import multilang from openpilot.common.realtime import Ratekeeper _DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) @@ -43,16 +43,16 @@ FONT_DIR = ASSETS_DIR.joinpath("fonts") class FontWeight(StrEnum): - THIN = "Inter-Thin.ttf" - EXTRA_LIGHT = "Inter-ExtraLight.ttf" - LIGHT = "Inter-Light.ttf" - NORMAL = "Inter-Regular.ttf" - MEDIUM = "Inter-Medium.ttf" - SEMI_BOLD = "Inter-SemiBold.ttf" - BOLD = "Inter-Bold.ttf" - EXTRA_BOLD = "Inter-ExtraBold.ttf" - BLACK = "Inter-Black.ttf" - UNIFONT = "unifont.otf" + THIN = "Inter-Thin.fnt" + EXTRA_LIGHT = "Inter-ExtraLight.fnt" + LIGHT = "Inter-Light.fnt" + NORMAL = "Inter-Regular.fnt" + MEDIUM = "Inter-Medium.fnt" + SEMI_BOLD = "Inter-SemiBold.fnt" + BOLD = "Inter-Bold.fnt" + EXTRA_BOLD = "Inter-ExtraBold.fnt" + BLACK = "Inter-Black.fnt" + UNIFONT = "unifont.fnt" def font_fallback(font: rl.Font) -> rl.Font: @@ -392,51 +392,11 @@ class GuiApplication: return self._height def _load_fonts(self): - # Create a character set from our keyboard layouts - from openpilot.system.ui.widgets.keyboard import KEYBOARD_LAYOUTS - - base_chars = set() - for layout in KEYBOARD_LAYOUTS.values(): - base_chars.update(key for row in layout for key in row) - base_chars |= set("–‑✓×°§•") - - # Load only the characters used in translations - unifont_chars = set(base_chars) - for language, code in multilang.languages.items(): - unifont_chars |= set(language) - try: - with open(os.path.join(TRANSLATIONS_DIR, f"app_{code}.po")) as f: - lang_chars = set(f.read()) - if code in UNIFONT_LANGUAGES: - unifont_chars |= lang_chars - else: - base_chars |= lang_chars - except FileNotFoundError: - cloudlog.warning(f"Translation file for language '{code}' not found when loading fonts.") - - base_chars = "".join(base_chars) - cloudlog.debug(f"Loading fonts with {len(base_chars)} glyphs.") - - unifont_chars = "".join(unifont_chars) - cloudlog.debug(f"Loading unifont with {len(unifont_chars)} glyphs.") - - base_codepoint_count = rl.ffi.new("int *", 1) - base_codepoints = rl.load_codepoints(base_chars, base_codepoint_count) - - unifont_codepoint_count = rl.ffi.new("int *", 1) - unifont_codepoints = rl.load_codepoints(unifont_chars, unifont_codepoint_count) - for font_weight_file in FontWeight: with as_file(FONT_DIR.joinpath(font_weight_file)) as fspath: - if font_weight_file == FontWeight.UNIFONT: - font = rl.load_font_ex(fspath.as_posix(), 200, unifont_codepoints, unifont_codepoint_count[0]) - else: - font = rl.load_font_ex(fspath.as_posix(), 200, base_codepoints, base_codepoint_count[0]) + font = rl.load_font(fspath.as_posix()) rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) self._fonts[font_weight_file] = font - - rl.unload_codepoints(base_codepoints) - rl.unload_codepoints(unifont_codepoints) rl.gui_set_font(self._fonts[FontWeight.NORMAL]) def _set_styles(self): From cf5bb4e16ecb541036d218b754bc048e08436f11 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 26 Oct 2025 13:47:47 -0700 Subject: [PATCH 268/341] reduce unifont impact on init time (#36490) --- selfdrive/assets/fonts/process.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/selfdrive/assets/fonts/process.py b/selfdrive/assets/fonts/process.py index f3633f3710..06bd4e8f0e 100755 --- a/selfdrive/assets/fonts/process.py +++ b/selfdrive/assets/fonts/process.py @@ -9,7 +9,6 @@ SELFDRIVE_DIR = FONT_DIR.parents[1] TRANSLATIONS_DIR = SELFDRIVE_DIR / "ui" / "translations" LANGUAGES_FILE = TRANSLATIONS_DIR / "languages.json" -FONT_SIZE = 200 GLYPH_PADDING = 6 EXTRA_CHARS = "–‑✓×°§•€£¥" UNIFONT_LANGUAGES = {"ar", "th", "zh-CHT", "zh-CHS", "ko", "ja"} @@ -68,9 +67,9 @@ def _glyph_metrics(glyphs, rects, codepoints): return entries, line_height, base -def _write_bmfont(path: Path, face: str, atlas_name: str, line_height: int, base: int, atlas_size, entries): +def _write_bmfont(path: Path, font_size: int, face: str, atlas_name: str, line_height: int, base: int, atlas_size, entries): lines = [ - f"info face=\"{face}\" size=-{FONT_SIZE} bold=0 italic=0 charset=\"\" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0", + f"info face=\"{face}\" size=-{font_size} bold=0 italic=0 charset=\"\" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0", f"common lineHeight={line_height} base={base} scaleW={atlas_size[0]} scaleH={atlas_size[1]} pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4", f"page id=0 file=\"{atlas_name}\"", f"chars count={len(entries)}", @@ -85,16 +84,21 @@ def _write_bmfont(path: Path, face: str, atlas_name: str, line_height: int, base def _process_font(font_path: Path, codepoints: tuple[int, ...]): print(f"Processing {font_path.name}...") + + font_size = { + "unifont.otf": 24, # unifont is huge + }.get(font_path.name, 200) + data = font_path.read_bytes() file_buf = rl.ffi.new("unsigned char[]", data) cp_buffer = rl.ffi.new("int[]", codepoints) cp_ptr = rl.ffi.cast("int *", cp_buffer) - glyphs = rl.load_font_data(rl.ffi.cast("unsigned char *", file_buf), len(data), FONT_SIZE, cp_ptr, len(codepoints), rl.FontType.FONT_DEFAULT) + glyphs = rl.load_font_data(rl.ffi.cast("unsigned char *", file_buf), len(data), font_size, cp_ptr, len(codepoints), rl.FontType.FONT_DEFAULT) if glyphs == rl.ffi.NULL: raise RuntimeError("raylib failed to load font data") rects_ptr = rl.ffi.new("Rectangle **") - image = rl.gen_image_font_atlas(glyphs, rects_ptr, len(codepoints), FONT_SIZE, GLYPH_PADDING, 0) + image = rl.gen_image_font_atlas(glyphs, rects_ptr, len(codepoints), font_size, GLYPH_PADDING, 0) if image.width == 0 or image.height == 0: raise RuntimeError("raylib returned an empty atlas") @@ -106,7 +110,7 @@ def _process_font(font_path: Path, codepoints: tuple[int, ...]): if not rl.export_image(image, atlas_path.as_posix()): raise RuntimeError("Failed to export atlas image") - _write_bmfont(FONT_DIR / f"{font_path.stem}.fnt", font_path.stem, atlas_name, line_height, base, (image.width, image.height), entries) + _write_bmfont(FONT_DIR / f"{font_path.stem}.fnt", font_size, font_path.stem, atlas_name, line_height, base, (image.width, image.height), entries) def main(): From a974deeb592f9e8f82120cdd62857ca51b28d20a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 26 Oct 2025 14:13:53 -0700 Subject: [PATCH 269/341] ui: replace close button text with icon (#36492) --- selfdrive/ui/layouts/settings/settings.py | 25 +++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index f13383fa55..72d3a4bafe 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -14,13 +14,10 @@ from openpilot.system.ui.lib.wifi_manager import WifiManager from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.network import NetworkUI -# Settings close button -SETTINGS_CLOSE_TEXT = "×" -SETTINGS_CLOSE_TEXT_Y_OFFSET = 8 # The '×' character isn't quite vertically centered in the font so we need to offset it a bit to fully center it - # Constants SIDEBAR_WIDTH = 500 CLOSE_BTN_SIZE = 200 +CLOSE_ICON_SIZE = 70 NAV_BTN_HEIGHT = 110 PANEL_MARGIN = 50 @@ -68,6 +65,7 @@ class SettingsLayout(Widget): } self._font_medium = gui_app.font(FontWeight.MEDIUM) + self._close_icon = gui_app.texture("icons/close2.png", CLOSE_ICON_SIZE, CLOSE_ICON_SIZE) # Callbacks self._close_callback: Callable | None = None @@ -97,12 +95,21 @@ class SettingsLayout(Widget): close_color = CLOSE_BTN_PRESSED if pressed else CLOSE_BTN_COLOR rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color) - close_text_size = measure_text_cached(self._font_medium, SETTINGS_CLOSE_TEXT, 140) - close_text_pos = rl.Vector2( - close_btn_rect.x + (close_btn_rect.width - close_text_size.x) / 2, - close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2 - SETTINGS_CLOSE_TEXT_Y_OFFSET, + icon_color = rl.Color(255, 255, 255, 255) if not pressed else rl.Color(220, 220, 220, 255) + icon_dest = rl.Rectangle( + close_btn_rect.x + (close_btn_rect.width - self._close_icon.width) / 2, + close_btn_rect.y + (close_btn_rect.height - self._close_icon.height) / 2, + self._close_icon.width, + self._close_icon.height, + ) + rl.draw_texture_pro( + self._close_icon, + rl.Rectangle(0, 0, self._close_icon.width, self._close_icon.height), + icon_dest, + rl.Vector2(0, 0), + 0, + icon_color, ) - rl.draw_text_ex(self._font_medium, SETTINGS_CLOSE_TEXT, close_text_pos, 140, 0, TEXT_SELECTED) # Store close button rect for click detection self._close_btn_rect = close_btn_rect From 2d0340cefd1568ec3c8c903e280183e90468e4ab Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 26 Oct 2025 14:18:48 -0700 Subject: [PATCH 270/341] ui: stop loading unused fonts (#36493) Co-authored-by: Comma Device --- system/ui/lib/application.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 4d0ae5fa49..39f27ea1a4 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -43,15 +43,11 @@ FONT_DIR = ASSETS_DIR.joinpath("fonts") class FontWeight(StrEnum): - THIN = "Inter-Thin.fnt" - EXTRA_LIGHT = "Inter-ExtraLight.fnt" LIGHT = "Inter-Light.fnt" NORMAL = "Inter-Regular.fnt" MEDIUM = "Inter-Medium.fnt" SEMI_BOLD = "Inter-SemiBold.fnt" BOLD = "Inter-Bold.fnt" - EXTRA_BOLD = "Inter-ExtraBold.fnt" - BLACK = "Inter-Black.fnt" UNIFONT = "unifont.fnt" From 6c85e2c697703446c334a38c0635ae2213a0c630 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 26 Oct 2025 18:30:57 -0700 Subject: [PATCH 271/341] ModemManager restart (#36433) * res * limit * not needed * comments + explicit --- system/hardware/hardwared.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index 4d52d78417..8ddc4da2f2 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -103,8 +103,8 @@ def hw_state_thread(end_event, hw_queue): modem_version = None modem_configured = False - modem_restarted = False modem_missing_count = 0 + modem_restart_count = 0 while not end_event.is_set(): # these are expensive calls. update every 10s @@ -121,16 +121,18 @@ def hw_state_thread(end_event, hw_queue): if modem_version is not None: cloudlog.event("modem version", version=modem_version) - else: - if not modem_restarted: - # TODO: we may be able to remove this with a MM update - # ModemManager's probing on startup can fail - # rarely, restart the service to probe again. - modem_missing_count += 1 - if modem_missing_count > 3: - modem_restarted = True - cloudlog.event("restarting ModemManager") - os.system("sudo systemctl restart --no-block ModemManager") + + if AGNOS and modem_restart_count < 3 and HARDWARE.get_modem_version() is None: + # TODO: we may be able to remove this with a MM update + # ModemManager's probing on startup can fail + # rarely, restart the service to probe again. + # Also, AT commands sometimes timeout resulting in ModemManager not + # trying to use this modem anymore. + modem_missing_count += 1 + if (modem_missing_count % 4) == 0: + modem_restart_count += 1 + cloudlog.event("restarting ModemManager") + os.system("sudo systemctl restart --no-block ModemManager") tx, rx = HARDWARE.get_modem_data_usage() From ff6ed7055ddf765052e917034cfe87ea83971c48 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:28:09 -0500 Subject: [PATCH 272/341] ci: include assets path for UI label and preview (#36499) * workflow: include 'selfdrive/assets/**' path for triggering UI preview * ui: include 'selfdrive/assets/**' path in labeler configuration --- .github/labeler.yaml | 2 +- .github/workflows/raylib_ui_preview.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yaml b/.github/labeler.yaml index 127a04e9e4..63d41d5b73 100644 --- a/.github/labeler.yaml +++ b/.github/labeler.yaml @@ -12,7 +12,7 @@ simulation: ui: - changed-files: - - any-glob-to-all-files: '{selfdrive/ui/**,system/ui/**}' + - any-glob-to-all-files: '{selfdrive/assets/**,selfdrive/ui/**,system/ui/**}' tools: - changed-files: diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index ff9655d155..18880e8a17 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -8,6 +8,7 @@ on: branches: - 'master' paths: + - 'selfdrive/assets/**' - 'selfdrive/ui/**' - 'system/ui/**' workflow_dispatch: From 6efe4e19987ee979f20c7a9b02fd86994d30b9b6 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:28:27 -0500 Subject: [PATCH 273/341] ci: fix selfdrive_tests weekly run and badge (#36500) --- .github/workflows/ci_weekly_run.yaml | 4 ++-- README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_weekly_run.yaml b/.github/workflows/ci_weekly_run.yaml index d8bf91116d..acd24de163 100644 --- a/.github/workflows/ci_weekly_run.yaml +++ b/.github/workflows/ci_weekly_run.yaml @@ -11,7 +11,7 @@ concurrency: cancel-in-progress: true jobs: - selfdrive_tests: - uses: commaai/openpilot/.github/workflows/selfdrive_tests.yaml@master + tests: + uses: commaai/openpilot/.github/workflows/tests.yaml@master with: run_number: ${{ inputs.run_number }} diff --git a/README.md b/README.md index 9f1819afbf..19a30599bd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Quick start: `bash <(curl -fsSL openpilot.comma.ai)` -[![openpilot tests](https://github.com/commaai/openpilot/actions/workflows/selfdrive_tests.yaml/badge.svg)](https://github.com/commaai/openpilot/actions/workflows/selfdrive_tests.yaml) +[![openpilot tests](https://github.com/commaai/openpilot/actions/workflows/tests.yaml/badge.svg)](https://github.com/commaai/openpilot/actions/workflows/tests.yaml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![X Follow](https://img.shields.io/twitter/follow/comma_ai)](https://x.com/comma_ai) [![Discord](https://img.shields.io/discord/469524606043160576)](https://discord.comma.ai) @@ -74,7 +74,7 @@ Safety and Testing ---- * openpilot observes [ISO26262](https://en.wikipedia.org/wiki/ISO_26262) guidelines, see [SAFETY.md](docs/SAFETY.md) for more details. -* openpilot has software-in-the-loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit. +* openpilot has software-in-the-loop [tests](.github/workflows/tests.yaml) that run on every commit. * The code enforcing the safety model lives in panda and is written in C, see [code rigor](https://github.com/commaai/panda#code-rigor) for more details. * panda has software-in-the-loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety). * Internally, we have a hardware-in-the-loop Jenkins test suite that builds and unit tests the various processes. From e03673485bbb1aac87a096a2e6b727702967068d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 28 Oct 2025 04:31:55 +0800 Subject: [PATCH 274/341] ui: unify cache key in AugmentedRoadView (#36477) unified cache key --- selfdrive/ui/onroad/augmented_road_view.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index fa0528444d..259551954d 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -39,9 +39,7 @@ class AugmentedRoadView(CameraView): self.view_from_calib = view_frame_from_device_frame.copy() self.view_from_wide_calib = view_frame_from_device_frame.copy() - self._last_calib_time: float = 0 - self._last_rect_dims = (0.0, 0.0) - self._last_stream_type = stream_type + self._matrix_cache_key = (0, 0.0, 0.0, stream_type) self._cached_matrix: np.ndarray | None = None self._content_rect = rl.Rectangle() @@ -161,12 +159,13 @@ class AugmentedRoadView(CameraView): def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: # Check if we can use cached matrix - calib_time = ui_state.sm.recv_frame['liveCalibration'] - current_dims = (self._content_rect.width, self._content_rect.height) - if (self._last_calib_time == calib_time and - self._last_rect_dims == current_dims and - self._last_stream_type == self.stream_type and - self._cached_matrix is not None): + cache_key = ( + ui_state.sm.recv_frame['liveCalibration'], + self._content_rect.width, + self._content_rect.height, + self.stream_type + ) + if cache_key == self._matrix_cache_key and self._cached_matrix is not None: return self._cached_matrix # Get camera configuration @@ -201,9 +200,7 @@ class AugmentedRoadView(CameraView): x_offset, y_offset = 0, 0 # Cache the computed transformation matrix to avoid recalculations - self._last_calib_time = calib_time - self._last_rect_dims = current_dims - self._last_stream_type = self.stream_type + self._matrix_cache_key = cache_key self._cached_matrix = np.array([ [zoom * 2 * cx / w, 0, -x_offset / w * 2], [0, zoom * 2 * cy / h, -y_offset / h * 2], From debc9bf7cfd139a5b69432bd43686cb6aa354332 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:32:15 -0500 Subject: [PATCH 275/341] screenshots: reuse alert setup function (#36473) ui: refactor onroad alert setup functions for improved reusability --- .../ui/tests/test_ui/raylib_screenshots.py | 58 +++++-------------- 1 file changed, 14 insertions(+), 44 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 3080b55b95..ef3c9fe02d 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -177,68 +177,38 @@ def setup_onroad_sidebar(click, pm: PubMaster): click(100, 100) # open sidebar -def setup_onroad_small_alert(click, pm: PubMaster): +def setup_onroad_alert(click, pm: PubMaster, size: log.SelfdriveState.AlertSize, text1: str, text2: str, status: log.SelfdriveState.AlertStatus): setup_onroad(click, pm) alert = messaging.new_message('selfdriveState') - alert.selfdriveState.alertSize = AlertSize.small - alert.selfdriveState.alertText1 = "Small Alert" - alert.selfdriveState.alertText2 = "This is a small alert" - alert.selfdriveState.alertStatus = AlertStatus.normal + ss = alert.selfdriveState + ss.alertSize = size + ss.alertText1 = text1 + ss.alertText2 = text2 + ss.alertStatus = status for _ in range(5): pm.send('selfdriveState', alert) alert.clear_write_flag() time.sleep(0.05) +def setup_onroad_small_alert(click, pm: PubMaster): + setup_onroad_alert(click, pm, AlertSize.small, "Small Alert", "This is a small alert", AlertStatus.normal) + + def setup_onroad_medium_alert(click, pm: PubMaster): - setup_onroad(click, pm) - alert = messaging.new_message('selfdriveState') - alert.selfdriveState.alertSize = AlertSize.mid - alert.selfdriveState.alertText1 = "Medium Alert" - alert.selfdriveState.alertText2 = "This is a medium alert" - alert.selfdriveState.alertStatus = AlertStatus.userPrompt - for _ in range(5): - pm.send('selfdriveState', alert) - alert.clear_write_flag() - time.sleep(0.05) + setup_onroad_alert(click, pm, AlertSize.mid, "Medium Alert", "This is a medium alert", AlertStatus.userPrompt) def setup_onroad_full_alert(click, pm: PubMaster): - setup_onroad(click, pm) - alert = messaging.new_message('selfdriveState') - alert.selfdriveState.alertSize = AlertSize.full - alert.selfdriveState.alertText1 = "DISENGAGE IMMEDIATELY" - alert.selfdriveState.alertText2 = "Driver Distracted" - alert.selfdriveState.alertStatus = AlertStatus.critical - for _ in range(5): - pm.send('selfdriveState', alert) - alert.clear_write_flag() - time.sleep(0.05) + setup_onroad_alert(click, pm, AlertSize.full, "DISENGAGE IMMEDIATELY", "Driver Distracted", AlertStatus.critical) def setup_onroad_full_alert_multiline(click, pm: PubMaster): - setup_onroad(click, pm) - alert = messaging.new_message('selfdriveState') - alert.selfdriveState.alertSize = AlertSize.full - alert.selfdriveState.alertText1 = "Reverse\nGear" - alert.selfdriveState.alertStatus = AlertStatus.normal - for _ in range(5): - pm.send('selfdriveState', alert) - alert.clear_write_flag() - time.sleep(0.05) + setup_onroad_alert(click, pm, AlertSize.full, "Reverse\nGear", "", AlertStatus.normal) def setup_onroad_full_alert_long_text(click, pm: PubMaster): - setup_onroad(click, pm) - alert = messaging.new_message('selfdriveState') - alert.selfdriveState.alertSize = AlertSize.full - alert.selfdriveState.alertText1 = "TAKE CONTROL IMMEDIATELY" - alert.selfdriveState.alertText2 = "Calibration Invalid: Remount Device & Recalibrate" - alert.selfdriveState.alertStatus = AlertStatus.userPrompt - for _ in range(5): - pm.send('selfdriveState', alert) - alert.clear_write_flag() - time.sleep(0.05) + setup_onroad_alert(click, pm, AlertSize.full, "TAKE CONTROL IMMEDIATELY", "Calibration Invalid: Remount Device & Recalibrate", AlertStatus.userPrompt) CASES = { From 4e88245745c98054c15ae6e922cf7788cf93aba7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 27 Oct 2025 13:34:16 -0700 Subject: [PATCH 276/341] raylib: rename set_callbacks (#36462) rn --- system/ui/lib/wifi_manager.py | 2 +- system/ui/widgets/network.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 5594742cb3..b6305a03f5 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -186,7 +186,7 @@ class WifiManager: threading.Thread(target=worker, daemon=True).start() - def set_callbacks(self, need_auth: Callable[[str], None] | None = None, + def add_callbacks(self, need_auth: Callable[[str], None] | None = None, activated: Callable[[], None] | None = None, forgotten: Callable[[], None] | None = None, networks_updated: Callable[[list[Network]], None] | None = None, diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index e9d7a1b09e..592c9de971 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -110,7 +110,7 @@ class AdvancedNetworkSettings(Widget): def __init__(self, wifi_manager: WifiManager): super().__init__() self._wifi_manager = wifi_manager - self._wifi_manager.set_callbacks(networks_updated=self._on_network_updated) + self._wifi_manager.add_callbacks(networks_updated=self._on_network_updated) self._params = Params() self._keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) @@ -285,7 +285,7 @@ class WifiManagerUI(Widget): self._networks_buttons: dict[str, Button] = {} self._forget_networks_buttons: dict[str, Button] = {} - self._wifi_manager.set_callbacks(need_auth=self._on_need_auth, + self._wifi_manager.add_callbacks(need_auth=self._on_need_auth, activated=self._on_activated, forgotten=self._on_forgotten, networks_updated=self._on_network_updated, From 1dadb3fcc92135b8b800746dbac0a00f88d8d36d Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:34:35 -0500 Subject: [PATCH 277/341] multilang: fix missing translation for longitudinal personality toggle description (#36446) fix: add translation wrapper for longitudinal personality toggle description --- selfdrive/ui/layouts/settings/toggles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index e4441edec3..3a1265e0fe 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -94,7 +94,7 @@ class TogglesLayout(Widget): self._long_personality_setting = multiple_button_item( lambda: tr("Driving Personality"), - DESCRIPTIONS["LongitudinalPersonality"], + lambda: tr(DESCRIPTIONS["LongitudinalPersonality"]), buttons=[lambda: tr("Aggressive"), lambda: tr("Standard"), lambda: tr("Relaxed")], button_width=255, callback=self._set_longitudinal_personality, From e754b738ada0388b78308675712c9c99fe5d174d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 27 Oct 2025 15:15:48 -0700 Subject: [PATCH 278/341] raylib: fix prime state thread (#36504) fix --- selfdrive/ui/lib/prime_state.py | 1 - selfdrive/ui/ui_state.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/lib/prime_state.py b/selfdrive/ui/lib/prime_state.py index 30ad0f763a..fc72b4f9c6 100644 --- a/selfdrive/ui/lib/prime_state.py +++ b/selfdrive/ui/lib/prime_state.py @@ -33,7 +33,6 @@ class PrimeState: self._running = False self._thread = None - self.start() def _load_initial_state(self) -> PrimeType: prime_type_str = os.getenv("PRIME_TYPE") or self._params.get("PrimeType") diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index dab01f9245..5d770fb91d 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -98,6 +98,7 @@ class UIState: return not self.started def update(self) -> None: + self.prime_state.start() # start thread after manager forks ui self.sm.update(0) self._update_state() self._update_status() From 2d6df2e1259dbb2a8408ac286502c19a08ce1976 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 27 Oct 2025 19:59:35 -0700 Subject: [PATCH 279/341] raylib: minor tweaks (#36507) * try * generic * check * why was this here?! --- selfdrive/ui/__init__.py | 1 + selfdrive/ui/onroad/augmented_road_view.py | 3 ++- selfdrive/ui/onroad/driver_state.py | 3 ++- selfdrive/ui/ui_state.py | 1 - selfdrive/ui/widgets/pairing_dialog.py | 13 +------------ selfdrive/ui/widgets/prime.py | 2 +- selfdrive/ui/widgets/setup.py | 2 +- system/ui/lib/application.py | 2 ++ system/ui/widgets/button.py | 12 ++++++++++++ 9 files changed, 22 insertions(+), 17 deletions(-) diff --git a/selfdrive/ui/__init__.py b/selfdrive/ui/__init__.py index e69de29bb2..b07e842f1a 100644 --- a/selfdrive/ui/__init__.py +++ b/selfdrive/ui/__init__.py @@ -0,0 +1 @@ +UI_BORDER_SIZE = 30 diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 259551954d..1f202141c3 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -3,7 +3,8 @@ import numpy as np import pyray as rl from cereal import log, messaging from msgq.visionipc import VisionStreamType -from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus, UI_BORDER_SIZE +from openpilot.selfdrive.ui import UI_BORDER_SIZE +from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.selfdrive.ui.onroad.alert_renderer import AlertRenderer from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer from openpilot.selfdrive.ui.onroad.hud_renderer import HudRenderer diff --git a/selfdrive/ui/onroad/driver_state.py b/selfdrive/ui/onroad/driver_state.py index 8aaef1bcfb..7b3181d1ac 100644 --- a/selfdrive/ui/onroad/driver_state.py +++ b/selfdrive/ui/onroad/driver_state.py @@ -2,7 +2,8 @@ import numpy as np import pyray as rl from cereal import log from dataclasses import dataclass -from openpilot.selfdrive.ui.ui_state import ui_state, UI_BORDER_SIZE +from openpilot.selfdrive.ui import UI_BORDER_SIZE +from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import Widget diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 5d770fb91d..a947e5406b 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -12,7 +12,6 @@ from openpilot.selfdrive.ui.lib.prime_state import PrimeState from openpilot.system.ui.lib.application import gui_app from openpilot.system.hardware import HARDWARE -UI_BORDER_SIZE = 30 BACKLIGHT_OFFROAD = 50 diff --git a/selfdrive/ui/widgets/pairing_dialog.py b/selfdrive/ui/widgets/pairing_dialog.py index 85b42d1a7a..f960cf723e 100644 --- a/selfdrive/ui/widgets/pairing_dialog.py +++ b/selfdrive/ui/widgets/pairing_dialog.py @@ -11,21 +11,10 @@ from openpilot.system.ui.lib.application import FontWeight, gui_app from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.widgets.button import IconButton from openpilot.selfdrive.ui.ui_state import ui_state -class IconButton(Widget): - def __init__(self, texture: rl.Texture): - super().__init__() - self._texture = texture - - def _render(self, rect: rl.Rectangle): - color = rl.Color(180, 180, 180, 150) if self.is_pressed else rl.WHITE - draw_x = rect.x + (rect.width - self._texture.width) / 2 - draw_y = rect.y + (rect.height - self._texture.height) / 2 - rl.draw_texture(self._texture, int(draw_x), int(draw_y), color) - - class PairingDialog(Widget): """Dialog for device pairing with QR code.""" diff --git a/selfdrive/ui/widgets/prime.py b/selfdrive/ui/widgets/prime.py index 49a0e56cdc..e98e4c1e1c 100644 --- a/selfdrive/ui/widgets/prime.py +++ b/selfdrive/ui/widgets/prime.py @@ -34,7 +34,7 @@ class PrimeWidget(Widget): # Description with wrapping desc_y = y + 140 - font = gui_app.font(FontWeight.LIGHT) + font = gui_app.font(FontWeight.NORMAL) wrapped_text = "\n".join(wrap_text(font, tr("Become a comma prime member at connect.comma.ai"), 56, int(w))) text_size = measure_text_cached(font, wrapped_text, 56) rl.draw_text_ex(font, wrapped_text, rl.Vector2(x, desc_y), 56, 0, rl.WHITE) diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index fcc5a14103..3c9406688f 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -46,7 +46,7 @@ class SetupWidget(Widget): # Description desc = tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.") - light_font = gui_app.font(FontWeight.LIGHT) + light_font = gui_app.font(FontWeight.NORMAL) wrapped = wrap_text(light_font, desc, 50, int(w)) for line in wrapped: rl.draw_text_ex(light_font, line, rl.Vector2(x, y), 50, 0, rl.WHITE) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 39f27ea1a4..8ec417159f 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -274,6 +274,8 @@ class GuiApplication: rl.image_resize(image, new_width, new_height) else: rl.image_resize(image, width, height) + else: + assert keep_aspect_ratio, "Cannot resize without specifying width and height" return image def _load_texture_from_image(self, image: rl.Image) -> rl.Texture: diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 5b9c51886d..df7a52d1c0 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -169,3 +169,15 @@ class ButtonRadio(Button): icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 icon_x = self._rect.x + self._rect.width - self._icon.width - self._text_padding - ICON_PADDING rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE if self.enabled else rl.Color(255, 255, 255, 100)) + + +class IconButton(Widget): + def __init__(self, texture: rl.Texture): + super().__init__() + self._texture = texture + + def _render(self, rect: rl.Rectangle): + color = rl.Color(180, 180, 180, 150) if self.is_pressed else rl.WHITE + draw_x = rect.x + (rect.width - self._texture.width) / 2 + draw_y = rect.y + (rect.height - self._texture.height) / 2 + rl.draw_texture(self._texture, int(draw_x), int(draw_y), color) From 73ed45f9d7071058d5f4c75b97825e6648b7697b Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:00:01 -0500 Subject: [PATCH 280/341] ui screenshots: add screenshot for unifont rendering (#36506) * ui: add homescreen setup for unifont language setting * fix params --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index ef3c9fe02d..f36ad1badb 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -216,6 +216,7 @@ CASES = { "homescreen_paired": setup_homescreen, "homescreen_prime": setup_homescreen, "homescreen_update_available": setup_homescreen_update_available, + "homescreen_unifont": setup_homescreen, "settings_device": setup_settings, "settings_network": setup_settings_network, "settings_network_advanced": setup_settings_network_advanced, @@ -300,6 +301,8 @@ def create_screenshots(): params.put("PrimeType", 0) # NONE elif name == "homescreen_prime": params.put("PrimeType", 2) # LITE + elif name == "homescreen_unifont": + params.put("LanguageSetting", "zh-CHT") # Traditional Chinese t.test_ui(name, setup) From 8a77534d02960fc238075aff74cc50be9f20f041 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 27 Oct 2025 22:47:41 -0700 Subject: [PATCH 281/341] fix zipapp with multilang (#36511) * fix * fix * fix * more --- release/pack.py | 2 +- selfdrive/ui/tests/test_translations.py | 4 ++-- selfdrive/ui/update_translations.py | 2 +- system/ui/lib/application.py | 5 +++-- system/ui/lib/multilang.py | 11 ++++++----- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/release/pack.py b/release/pack.py index 1cb1a47a48..92ff68fe76 100755 --- a/release/pack.py +++ b/release/pack.py @@ -12,7 +12,7 @@ from openpilot.common.basedir import BASEDIR DIRS = ['cereal', 'openpilot'] -EXTS = ['.png', '.py', '.ttf', '.capnp'] +EXTS = ['.png', '.py', '.ttf', '.capnp', '.json', '.fnt', '.mo'] INTERPRETER = '/usr/bin/env python3' diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py index 5308a44ea5..3177814f9f 100644 --- a/selfdrive/ui/tests/test_translations.py +++ b/selfdrive/ui/tests/test_translations.py @@ -8,7 +8,7 @@ import requests from parameterized import parameterized_class from openpilot.system.ui.lib.multilang import TRANSLATIONS_DIR, LANGUAGES_FILE -with open(LANGUAGES_FILE) as f: +with open(str(LANGUAGES_FILE)) as f: translation_files = json.load(f) UNFINISHED_TRANSLATION_TAG = " Date: Wed, 29 Oct 2025 04:09:49 +0800 Subject: [PATCH 282/341] ui: skip rendering when screen is off (#36510) * skip rendering when screen is off * continue and rename * revert that * flip --------- Co-authored-by: Adeeb Shihadeh --- selfdrive/ui/ui.py | 5 ++--- selfdrive/ui/ui_state.py | 1 + system/ui/lib/application.py | 15 +++++++++++++-- system/ui/reset.py | 9 ++++----- system/ui/setup.py | 7 +++---- system/ui/updater.py | 7 +++---- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index f2ca4e7700..4a1f03fb87 100755 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -13,10 +13,9 @@ def main(): gui_app.init_window("UI") main_layout = MainLayout() main_layout.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) - for showing_dialog in gui_app.render(): + for should_render in gui_app.render(): ui_state.update() - - if not showing_dialog: + if should_render: main_layout.render() diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index a947e5406b..b08b8ef28c 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -253,6 +253,7 @@ class Device: self._awake = on cloudlog.debug(f"setting display power {int(on)}") HARDWARE.set_display_power(on) + gui_app.set_should_render(on) # Global instance diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 23fa8a6775..698ed3650b 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -156,6 +156,8 @@ class GuiApplication: self._mouse = MouseState(self._scale) self._mouse_events: list[MouseEvent] = [] + self._should_render = True + # Debug variables self._mouse_history: deque[MousePos] = deque(maxlen=MOUSE_THREAD_RATE) @@ -237,6 +239,9 @@ class GuiApplication: self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback) + def set_should_render(self, should_render: bool): + self._should_render = should_render + def texture(self, asset_path: str, width: int | None = None, height: int | None = None, alpha_premultiply=False, keep_aspect_ratio=True): cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" @@ -322,6 +327,12 @@ class GuiApplication: # Store all mouse events for the current frame self._mouse_events = self._mouse.get_events() + # Skip rendering when screen is off + if not self._should_render: + time.sleep(1 / self._target_fps) + yield False + continue + if self._render_texture: rl.begin_texture_mode(self._render_texture) rl.clear_background(rl.BLACK) @@ -344,9 +355,9 @@ class GuiApplication: self._modal_overlay = ModalOverlay() if original_modal.callback is not None: original_modal.callback(result) - yield True - else: yield False + else: + yield True if self._render_texture: rl.end_texture_mode() diff --git a/system/ui/reset.py b/system/ui/reset.py index 8f6466ce46..3922c27aac 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -126,11 +126,10 @@ def main(): if mode == ResetMode.FORMAT: reset.start_reset() - for showing_dialog in gui_app.render(): - if showing_dialog: - continue - if not reset.render(rl.Rectangle(45, 200, gui_app.width - 90, gui_app.height - 245)): - break + for should_render in gui_app.render(): + if should_render: + if not reset.render(rl.Rectangle(45, 200, gui_app.width - 90, gui_app.height - 245)): + break if __name__ == "__main__": diff --git a/system/ui/setup.py b/system/ui/setup.py index da8c8d81fb..f6d2853fa0 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -432,10 +432,9 @@ def main(): try: gui_app.init_window("Setup", 20) setup = Setup() - for showing_dialog in gui_app.render(): - if showing_dialog: - continue - setup.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + for should_render in gui_app.render(): + if should_render: + setup.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) setup.close() except Exception as e: print(f"Setup error: {e}") diff --git a/system/ui/updater.py b/system/ui/updater.py index 5dd5a69c69..2e1a8687e1 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -161,10 +161,9 @@ def main(): try: gui_app.init_window("System Update") updater = Updater(updater_path, manifest_path) - for showing_dialog in gui_app.render(): - if showing_dialog: - continue - updater.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + for should_render in gui_app.render(): + if should_render: + updater.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) finally: # Make sure we clean up even if there's an error gui_app.close() From 5d142326f5eac6567787ae2fad2ab4ee22e693ea Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 29 Oct 2025 05:47:17 +0800 Subject: [PATCH 283/341] ui: remove unused get_width() method (#36512) remove unused get_width() method --- system/ui/widgets/list_view.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index ed087c3f13..20a5f77911 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -172,10 +172,6 @@ class TextAction(ItemAction): def set_text(self, text: str | Callable[[], str]): self._text_source = text - def get_width(self) -> int: - text_width = measure_text_cached(self._font, self.text, ITEM_TEXT_FONT_SIZE).x - return int(text_width + TEXT_PADDING) - class DualButtonAction(ItemAction): def __init__(self, left_text: str | Callable[[], str], right_text: str | Callable[[], str], left_callback: Callable = None, From 47d0a95fd6d6c40411f9d24ae9ddfedf8de6e124 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Tue, 28 Oct 2025 16:49:33 -0500 Subject: [PATCH 284/341] font: remove unifont anti-aliasing and reduce font size to 16 (#36508) remove unifont anti-aliasing and reduce font size to 16 Co-authored-by: Shane Smiskol --- selfdrive/assets/fonts/process.py | 2 +- system/ui/lib/application.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/selfdrive/assets/fonts/process.py b/selfdrive/assets/fonts/process.py index 06bd4e8f0e..a0d01af148 100755 --- a/selfdrive/assets/fonts/process.py +++ b/selfdrive/assets/fonts/process.py @@ -86,7 +86,7 @@ def _process_font(font_path: Path, codepoints: tuple[int, ...]): print(f"Processing {font_path.name}...") font_size = { - "unifont.otf": 24, # unifont is huge + "unifont.otf": 16, # unifont is only 16x8 or 16x16 pixels per glyph }.get(font_path.name, 200) data = font_path.read_bytes() diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 698ed3650b..8e45191bf6 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -405,7 +405,8 @@ class GuiApplication: with as_file(FONT_DIR) as fspath: fnt_path = fspath / font_weight_file font = rl.load_font(fnt_path.as_posix()) - rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) + if font_weight_file != FontWeight.UNIFONT: + rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) self._fonts[font_weight_file] = font rl.gui_set_font(self._fonts[FontWeight.NORMAL]) From 2e636458a6c78a019532b912e936a43d8b2bd817 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 28 Oct 2025 16:47:22 -0700 Subject: [PATCH 285/341] op adb: forward all openpilot service ports (#36518) * op adb: forward all openpilot service ports * cleanup --- tools/scripts/adb_ssh.sh | 41 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/tools/scripts/adb_ssh.sh b/tools/scripts/adb_ssh.sh index 2fe2873a3d..ad65693722 100755 --- a/tools/scripts/adb_ssh.sh +++ b/tools/scripts/adb_ssh.sh @@ -1,7 +1,42 @@ #!/usr/bin/env bash -set -e +set -euo pipefail -# this is a little nicer than "adb shell" since -# "adb shell" doesn't do full terminal emulation +# Forward all openpilot service ports +mapfile -t SERVICE_PORTS < <(python3 - <<'PY' +from cereal.services import SERVICE_LIST + +FNV_PRIME = 0x100000001b3 +FNV_OFFSET_BASIS = 0xcbf29ce484222325 +START_PORT = 8023 +MAX_PORT = 65535 +PORT_RANGE = MAX_PORT - START_PORT +MASK = 0xffffffffffffffff + +def fnv1a(endpoint: str) -> int: + h = FNV_OFFSET_BASIS + for b in endpoint.encode(): + h ^= b + h = (h * FNV_PRIME) & MASK + return h + +ports = set() +for name in SERVICE_LIST.keys(): + port = START_PORT + fnv1a(name) % PORT_RANGE + ports.add((name, port)) + +for name, port in sorted(ports): + print(f"{name} {port}") +PY +) + +for entry in "${SERVICE_PORTS[@]}"; do + name="${entry% *}" + port="${entry##* }" + adb forward "tcp:${port}" "tcp:${port}" > /dev/null +done + +# Forward SSH port first for interactive shell access. adb forward tcp:2222 tcp:22 + +# SSH! ssh comma@localhost -p 2222 "$@" From 9f20eb8ce612fadfa7331e0d50e5619eaaf853b8 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 28 Oct 2025 19:15:43 -0700 Subject: [PATCH 286/341] setup: handle incompatible versions (#36520) check --- system/ui/setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/ui/setup.py b/system/ui/setup.py index f6d2853fa0..0045b45417 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -4,6 +4,7 @@ import re import threading import time import urllib.request +import urllib.error from urllib.parse import urlparse from enum import IntEnum import shutil @@ -418,6 +419,10 @@ class Setup(Widget): time.sleep(0.1) gui_app.request_close() + except urllib.error.HTTPError as e: + if e.code == 409: + error_msg = e.read().decode("utf-8") + self.download_failed(self.download_url, error_msg) except Exception: error_msg = "Ensure the entered URL is valid, and the device's internet connection is good." self.download_failed(self.download_url, error_msg) From 002a22a09766628beeb262715a334aa40b7bfc24 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 28 Oct 2025 23:05:44 -0700 Subject: [PATCH 287/341] AGNOS 14.5 (#36523) * stage * updater * prod --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 20 +++++------ system/hardware/tici/all-partitions.json | 44 ++++++++++++------------ system/hardware/tici/updater_magic | 4 +-- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 9bbe0916f7..3715ad2486 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="14.3" + export AGNOS_VERSION="14.5" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 61ba1d38ee..d6a6ab51c9 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-273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925.img.xz", - "hash": "273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925", - "hash_raw": "273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925", + "url": "https://commadist.azureedge.net/agnosupdate/boot-90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c.img.xz", + "hash": "90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c", + "hash_raw": "90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c", "size": 17496064, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "562c9137843994995188f21f5ec3bac408ac7dee030627aef701853814a1d33e" + "ondevice_hash": "35014c39b55010ac955c10f808b088e74259147c7a8cbf989b3dff7d95a1e8ae" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c.img.xz", - "hash": "2e41ebf1cf7e3801fa447c1e133d6d1b928546c412c36a843f5bd344557b9908", - "hash_raw": "71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c", + "url": "https://commadist.azureedge.net/agnosupdate/system-54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502.img.xz", + "hash": "6e2f82c249f6feacdfcf20679e9e4876d161753a94d4068fe26b5c41191bda24", + "hash_raw": "54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502", "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "1d65fd6d8bb4b0c6f9920e0b5c49c7743b5949f8057676511765f1900ab6a771", + "ondevice_hash": "f3a50de42734f747611f6df00087b3a6ec3620bed07134d327a864c1182f0df8", "alt": { - "hash": "71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c", - "url": "https://commadist.azureedge.net/agnosupdate/system-71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c.img", + "hash": "54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502", + "url": "https://commadist.azureedge.net/agnosupdate/system-54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502.img", "size": 4718592000 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 434c0567d1..c51453f4c6 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-273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925.img.xz", - "hash": "273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925", - "hash_raw": "273ba209936c5697bea478d1bc21dbc4ede1588508d33f5d2e7ad5d5c581d925", + "url": "https://commadist.azureedge.net/agnosupdate/boot-90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c.img.xz", + "hash": "90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c", + "hash_raw": "90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c", "size": 17496064, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "562c9137843994995188f21f5ec3bac408ac7dee030627aef701853814a1d33e" + "ondevice_hash": "35014c39b55010ac955c10f808b088e74259147c7a8cbf989b3dff7d95a1e8ae" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c.img.xz", - "hash": "2e41ebf1cf7e3801fa447c1e133d6d1b928546c412c36a843f5bd344557b9908", - "hash_raw": "71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c", + "url": "https://commadist.azureedge.net/agnosupdate/system-54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502.img.xz", + "hash": "6e2f82c249f6feacdfcf20679e9e4876d161753a94d4068fe26b5c41191bda24", + "hash_raw": "54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502", "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "1d65fd6d8bb4b0c6f9920e0b5c49c7743b5949f8057676511765f1900ab6a771", + "ondevice_hash": "f3a50de42734f747611f6df00087b3a6ec3620bed07134d327a864c1182f0df8", "alt": { - "hash": "71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c", - "url": "https://commadist.azureedge.net/agnosupdate/system-71b51d24f70d6831f597d369d4b37f78262258096bc3d662826275c4f1fbd42c.img", + "hash": "54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502", + "url": "https://commadist.azureedge.net/agnosupdate/system-54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502.img", "size": 4718592000 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-8ed6bf12f84d64770aca750daacbd1578d199cc470ecd81ace255cd9cf5065e8.img.xz", - "hash": "4b1aae460d6a81d27691e2aa4fd96634eca607cac65305b5147350f1578beebd", - "hash_raw": "8ed6bf12f84d64770aca750daacbd1578d199cc470ecd81ace255cd9cf5065e8", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-7b58b8c6935e998b93fae25aa358dc808e90b032c004b7f7ec6e3a60986cd41d.img.xz", + "hash": "5115d8a8bc4ad8eebaf0e3176cc68799089c72f64504f7238f411cbaf5d18497", + "hash_raw": "7b58b8c6935e998b93fae25aa358dc808e90b032c004b7f7ec6e3a60986cd41d", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "be4365d37920853fb05fbffa793f7818890a41e4869c36ab16439a5f18d8d945" + "ondevice_hash": "b0ee797f751bc776bcb161a479cfb7f339bbf92f0cb207b053797daef0ac61d8" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-11fe1e0f066bb8639f6bbbf2c71f19472f1ae436b96c92fe2d21c456dce1bf88.img.xz", - "hash": "517273cd69a34ae523e6075baa79dd46fe5a940e376faba50f26ec0b74a27610", - "hash_raw": "11fe1e0f066bb8639f6bbbf2c71f19472f1ae436b96c92fe2d21c456dce1bf88", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-a5a3e2fe9a8bf83ea37aabafdd7fd5f6e274f8ef3d1fb46ce56766cec8f13215.img.xz", + "hash": "c0348854c1f93a0557c9ce85f709c8d19f1d11df0c3ba6cd4aa01ada3d2293f3", + "hash_raw": "a5a3e2fe9a8bf83ea37aabafdd7fd5f6e274f8ef3d1fb46ce56766cec8f13215", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "498a5bc3ed091663543d2882831b45e3f92743df458a901cf6f64e791e518cb2" + "ondevice_hash": "2def7bc3fed2cdcbaf4869fd2f68ad1d62f6d89b423d44c7114faf22f449c29f" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-a60a00504f28f72179c44dfa539ef447c8e12a614aa23cef29e4b04c394505f1.img.xz", - "hash": "c32098db26bf2aefd1fa6184e3ac5db304adb35bedc6693b8729046c40aad7fb", - "hash_raw": "a60a00504f28f72179c44dfa539ef447c8e12a614aa23cef29e4b04c394505f1", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-b86b949d9ed1978e792fccf59c1484238890e2cb904a501db64ed96bb91df4f9.img.xz", + "hash": "e0cee51c596d77e265b3bae937e9a3b4f317d0e80c091f03ca1bfbbdadbc6536", + "hash_raw": "b86b949d9ed1978e792fccf59c1484238890e2cb904a501db64ed96bb91df4f9", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "cf16e217c88d92bc99439cd3e8e24e6a1776bfc4b175044643bfc8b42968bd38" + "ondevice_hash": "38d7e4d3105102ca5a8dedb2764cb16f6e36766bbbb9d87e557e0d727ec37a58" } ] \ No newline at end of file diff --git a/system/hardware/tici/updater_magic b/system/hardware/tici/updater_magic index 2487973507..b4dfa9be2e 100755 --- a/system/hardware/tici/updater_magic +++ b/system/hardware/tici/updater_magic @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffc9893e48b10096062f5fd1a14016addf7adb969f20f31ff26e68579992283c -size 20780744 +oid sha256:7990262878becdf2eaed40ffcc96835a6fc6bc4bdf52f4df88e8b6fcadd1bff8 +size 13664323 From af24fd68420615d4c6095f1f9f3aa92f627170b4 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 31 Oct 2025 00:24:22 +0800 Subject: [PATCH 288/341] remove qrcode library from third_party (#36528) --- third_party/qrcode/QrCode.cc | 862 ---------------------------------- third_party/qrcode/QrCode.hpp | 556 ---------------------- 2 files changed, 1418 deletions(-) delete mode 100644 third_party/qrcode/QrCode.cc delete mode 100644 third_party/qrcode/QrCode.hpp diff --git a/third_party/qrcode/QrCode.cc b/third_party/qrcode/QrCode.cc deleted file mode 100644 index b9de86215e..0000000000 --- a/third_party/qrcode/QrCode.cc +++ /dev/null @@ -1,862 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "QrCode.hpp" - -using std::int8_t; -using std::uint8_t; -using std::size_t; -using std::vector; - - -namespace qrcodegen { - -QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : - modeBits(mode) { - numBitsCharCount[0] = cc0; - numBitsCharCount[1] = cc1; - numBitsCharCount[2] = cc2; -} - - -int QrSegment::Mode::getModeBits() const { - return modeBits; -} - - -int QrSegment::Mode::numCharCountBits(int ver) const { - return numBitsCharCount[(ver + 7) / 17]; -} - - -const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); -const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); -const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); -const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); -const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); - - -QrSegment QrSegment::makeBytes(const vector &data) { - if (data.size() > static_cast(INT_MAX)) - throw std::length_error("Data too long"); - BitBuffer bb; - for (uint8_t b : data) - bb.appendBits(b, 8); - return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); -} - - -QrSegment QrSegment::makeNumeric(const char *digits) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *digits != '\0'; digits++, charCount++) { - char c = *digits; - if (c < '0' || c > '9') - throw std::domain_error("String contains non-numeric characters"); - accumData = accumData * 10 + (c - '0'); - accumCount++; - if (accumCount == 3) { - bb.appendBits(static_cast(accumData), 10); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 or 2 digits remaining - bb.appendBits(static_cast(accumData), accumCount * 3 + 1); - return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); -} - - -QrSegment QrSegment::makeAlphanumeric(const char *text) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *text != '\0'; text++, charCount++) { - const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); - if (temp == nullptr) - throw std::domain_error("String contains unencodable characters in alphanumeric mode"); - accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); - accumCount++; - if (accumCount == 2) { - bb.appendBits(static_cast(accumData), 11); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 character remaining - bb.appendBits(static_cast(accumData), 6); - return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); -} - - -vector QrSegment::makeSegments(const char *text) { - // Select the most efficient segment encoding automatically - vector result; - if (*text == '\0'); // Leave result empty - else if (isNumeric(text)) - result.push_back(makeNumeric(text)); - else if (isAlphanumeric(text)) - result.push_back(makeAlphanumeric(text)); - else { - vector bytes; - for (; *text != '\0'; text++) - bytes.push_back(static_cast(*text)); - result.push_back(makeBytes(bytes)); - } - return result; -} - - -QrSegment QrSegment::makeEci(long assignVal) { - BitBuffer bb; - if (assignVal < 0) - throw std::domain_error("ECI assignment value out of range"); - else if (assignVal < (1 << 7)) - bb.appendBits(static_cast(assignVal), 8); - else if (assignVal < (1 << 14)) { - bb.appendBits(2, 2); - bb.appendBits(static_cast(assignVal), 14); - } else if (assignVal < 1000000L) { - bb.appendBits(6, 3); - bb.appendBits(static_cast(assignVal), 21); - } else - throw std::domain_error("ECI assignment value out of range"); - return QrSegment(Mode::ECI, 0, std::move(bb)); -} - - -QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : - mode(md), - numChars(numCh), - data(dt) { - if (numCh < 0) - throw std::domain_error("Invalid value"); -} - - -QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : - mode(md), - numChars(numCh), - data(std::move(dt)) { - if (numCh < 0) - throw std::domain_error("Invalid value"); -} - - -int QrSegment::getTotalBits(const vector &segs, int version) { - int result = 0; - for (const QrSegment &seg : segs) { - int ccbits = seg.mode.numCharCountBits(version); - if (seg.numChars >= (1L << ccbits)) - return -1; // The segment's length doesn't fit the field's bit width - if (4 + ccbits > INT_MAX - result) - return -1; // The sum will overflow an int type - result += 4 + ccbits; - if (seg.data.size() > static_cast(INT_MAX - result)) - return -1; // The sum will overflow an int type - result += static_cast(seg.data.size()); - } - return result; -} - - -bool QrSegment::isAlphanumeric(const char *text) { - for (; *text != '\0'; text++) { - if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) - return false; - } - return true; -} - - -bool QrSegment::isNumeric(const char *text) { - for (; *text != '\0'; text++) { - char c = *text; - if (c < '0' || c > '9') - return false; - } - return true; -} - - -QrSegment::Mode QrSegment::getMode() const { - return mode; -} - - -int QrSegment::getNumChars() const { - return numChars; -} - - -const std::vector &QrSegment::getData() const { - return data; -} - - -const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; - - - -int QrCode::getFormatBits(Ecc ecl) { - switch (ecl) { - case Ecc::LOW : return 1; - case Ecc::MEDIUM : return 0; - case Ecc::QUARTILE: return 3; - case Ecc::HIGH : return 2; - default: throw std::logic_error("Assertion error"); - } -} - - -QrCode QrCode::encodeText(const char *text, Ecc ecl) { - vector segs = QrSegment::makeSegments(text); - return encodeSegments(segs, ecl); -} - - -QrCode QrCode::encodeBinary(const vector &data, Ecc ecl) { - vector segs{QrSegment::makeBytes(data)}; - return encodeSegments(segs, ecl); -} - - -QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, - int minVersion, int maxVersion, int mask, bool boostEcl) { - if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) - throw std::invalid_argument("Invalid value"); - - // Find the minimal version number to use - int version, dataUsedBits; - for (version = minVersion; ; version++) { - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = QrSegment::getTotalBits(segs, version); - if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) - break; // This version number is found to be suitable - if (version >= maxVersion) { // All versions in the range could not fit the given data - std::ostringstream sb; - if (dataUsedBits == -1) - sb << "Segment too long"; - else { - sb << "Data length = " << dataUsedBits << " bits, "; - sb << "Max capacity = " << dataCapacityBits << " bits"; - } - throw data_too_long(sb.str()); - } - } - if (dataUsedBits == -1) - throw std::logic_error("Assertion error"); - - // Increase the error correction level while the data still fits in the current version number - for (Ecc newEcl : vector{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high - if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) - ecl = newEcl; - } - - // Concatenate all segments to create the data bit string - BitBuffer bb; - for (const QrSegment &seg : segs) { - bb.appendBits(static_cast(seg.getMode().getModeBits()), 4); - bb.appendBits(static_cast(seg.getNumChars()), seg.getMode().numCharCountBits(version)); - bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); - } - if (bb.size() != static_cast(dataUsedBits)) - throw std::logic_error("Assertion error"); - - // Add terminator and pad up to a byte if applicable - size_t dataCapacityBits = static_cast(getNumDataCodewords(version, ecl)) * 8; - if (bb.size() > dataCapacityBits) - throw std::logic_error("Assertion error"); - bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); - bb.appendBits(0, (8 - static_cast(bb.size() % 8)) % 8); - if (bb.size() % 8 != 0) - throw std::logic_error("Assertion error"); - - // Pad with alternating bytes until data capacity is reached - for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); - - // Pack bits into bytes in big endian - vector dataCodewords(bb.size() / 8); - for (size_t i = 0; i < bb.size(); i++) - dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); - - // Create the QR Code object - return QrCode(version, ecl, dataCodewords, mask); -} - - -QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) : - // Initialize fields and check arguments - version(ver), - errorCorrectionLevel(ecl) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw std::domain_error("Version value out of range"); - if (msk < -1 || msk > 7) - throw std::domain_error("Mask value out of range"); - size = ver * 4 + 17; - size_t sz = static_cast(size); - modules = vector >(sz, vector(sz)); // Initially all white - isFunction = vector >(sz, vector(sz)); - - // Compute ECC, draw modules - drawFunctionPatterns(); - const vector allCodewords = addEccAndInterleave(dataCodewords); - drawCodewords(allCodewords); - - // Do masking - if (msk == -1) { // Automatically choose best mask - long minPenalty = LONG_MAX; - for (int i = 0; i < 8; i++) { - applyMask(i); - drawFormatBits(i); - long penalty = getPenaltyScore(); - if (penalty < minPenalty) { - msk = i; - minPenalty = penalty; - } - applyMask(i); // Undoes the mask due to XOR - } - } - if (msk < 0 || msk > 7) - throw std::logic_error("Assertion error"); - this->mask = msk; - applyMask(msk); // Apply the final choice of mask - drawFormatBits(msk); // Overwrite old format bits - - isFunction.clear(); - isFunction.shrink_to_fit(); -} - - -int QrCode::getVersion() const { - return version; -} - - -int QrCode::getSize() const { - return size; -} - - -QrCode::Ecc QrCode::getErrorCorrectionLevel() const { - return errorCorrectionLevel; -} - - -int QrCode::getMask() const { - return mask; -} - - -bool QrCode::getModule(int x, int y) const { - return 0 <= x && x < size && 0 <= y && y < size && module(x, y); -} - - -std::string QrCode::toSvgString(int border) const { - if (border < 0) - throw std::domain_error("Border must be non-negative"); - if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) - throw std::overflow_error("Border too large"); - - std::ostringstream sb; - sb << "\n"; - sb << "\n"; - sb << "\n"; - sb << "\t\n"; - sb << "\t\n"; - sb << "\n"; - return sb.str(); -} - - -void QrCode::drawFunctionPatterns() { - // Draw horizontal and vertical timing patterns - for (int i = 0; i < size; i++) { - setFunctionModule(6, i, i % 2 == 0); - setFunctionModule(i, 6, i % 2 == 0); - } - - // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) - drawFinderPattern(3, 3); - drawFinderPattern(size - 4, 3); - drawFinderPattern(3, size - 4); - - // Draw numerous alignment patterns - const vector alignPatPos = getAlignmentPatternPositions(); - size_t numAlign = alignPatPos.size(); - for (size_t i = 0; i < numAlign; i++) { - for (size_t j = 0; j < numAlign; j++) { - // Don't draw on the three finder corners - if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) - drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); - } - } - - // Draw configuration data - drawFormatBits(0); // Dummy mask value; overwritten later in the constructor - drawVersion(); -} - - -void QrCode::drawFormatBits(int msk) { - // Calculate error correction code and pack bits - int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 - int rem = data; - for (int i = 0; i < 10; i++) - rem = (rem << 1) ^ ((rem >> 9) * 0x537); - int bits = (data << 10 | rem) ^ 0x5412; // uint15 - if (bits >> 15 != 0) - throw std::logic_error("Assertion error"); - - // Draw first copy - for (int i = 0; i <= 5; i++) - setFunctionModule(8, i, getBit(bits, i)); - setFunctionModule(8, 7, getBit(bits, 6)); - setFunctionModule(8, 8, getBit(bits, 7)); - setFunctionModule(7, 8, getBit(bits, 8)); - for (int i = 9; i < 15; i++) - setFunctionModule(14 - i, 8, getBit(bits, i)); - - // Draw second copy - for (int i = 0; i < 8; i++) - setFunctionModule(size - 1 - i, 8, getBit(bits, i)); - for (int i = 8; i < 15; i++) - setFunctionModule(8, size - 15 + i, getBit(bits, i)); - setFunctionModule(8, size - 8, true); // Always black -} - - -void QrCode::drawVersion() { - if (version < 7) - return; - - // Calculate error correction code and pack bits - int rem = version; // version is uint6, in the range [7, 40] - for (int i = 0; i < 12; i++) - rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - long bits = static_cast(version) << 12 | rem; // uint18 - if (bits >> 18 != 0) - throw std::logic_error("Assertion error"); - - // Draw two copies - for (int i = 0; i < 18; i++) { - bool bit = getBit(bits, i); - int a = size - 11 + i % 3; - int b = i / 3; - setFunctionModule(a, b, bit); - setFunctionModule(b, a, bit); - } -} - - -void QrCode::drawFinderPattern(int x, int y) { - for (int dy = -4; dy <= 4; dy++) { - for (int dx = -4; dx <= 4; dx++) { - int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm - int xx = x + dx, yy = y + dy; - if (0 <= xx && xx < size && 0 <= yy && yy < size) - setFunctionModule(xx, yy, dist != 2 && dist != 4); - } - } -} - - -void QrCode::drawAlignmentPattern(int x, int y) { - for (int dy = -2; dy <= 2; dy++) { - for (int dx = -2; dx <= 2; dx++) - setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); - } -} - - -void QrCode::setFunctionModule(int x, int y, bool isBlack) { - size_t ux = static_cast(x); - size_t uy = static_cast(y); - modules .at(uy).at(ux) = isBlack; - isFunction.at(uy).at(ux) = true; -} - - -bool QrCode::module(int x, int y) const { - return modules.at(static_cast(y)).at(static_cast(x)); -} - - -vector QrCode::addEccAndInterleave(const vector &data) const { - if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) - throw std::invalid_argument("Invalid argument"); - - // Calculate parameter numbers - int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)][version]; - int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast(errorCorrectionLevel)][version]; - int rawCodewords = getNumRawDataModules(version) / 8; - int numShortBlocks = numBlocks - rawCodewords % numBlocks; - int shortBlockLen = rawCodewords / numBlocks; - - // Split data into blocks and append ECC to each block - vector > blocks; - const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); - for (int i = 0, k = 0; i < numBlocks; i++) { - vector dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); - k += static_cast(dat.size()); - const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); - if (i < numShortBlocks) - dat.push_back(0); - dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); - blocks.push_back(std::move(dat)); - } - - // Interleave (not concatenate) the bytes from every block into a single sequence - vector result; - for (size_t i = 0; i < blocks.at(0).size(); i++) { - for (size_t j = 0; j < blocks.size(); j++) { - // Skip the padding byte in short blocks - if (i != static_cast(shortBlockLen - blockEccLen) || j >= static_cast(numShortBlocks)) - result.push_back(blocks.at(j).at(i)); - } - } - if (result.size() != static_cast(rawCodewords)) - throw std::logic_error("Assertion error"); - return result; -} - - -void QrCode::drawCodewords(const vector &data) { - if (data.size() != static_cast(getNumRawDataModules(version) / 8)) - throw std::invalid_argument("Invalid argument"); - - size_t i = 0; // Bit index into the data - // Do the funny zigzag scan - for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair - if (right == 6) - right = 5; - for (int vert = 0; vert < size; vert++) { // Vertical counter - for (int j = 0; j < 2; j++) { - size_t x = static_cast(right - j); // Actual x coordinate - bool upward = ((right + 1) & 2) == 0; - size_t y = static_cast(upward ? size - 1 - vert : vert); // Actual y coordinate - if (!isFunction.at(y).at(x) && i < data.size() * 8) { - modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); - i++; - } - // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method - } - } - } - if (i != data.size() * 8) - throw std::logic_error("Assertion error"); -} - - -void QrCode::applyMask(int msk) { - if (msk < 0 || msk > 7) - throw std::domain_error("Mask value out of range"); - size_t sz = static_cast(size); - for (size_t y = 0; y < sz; y++) { - for (size_t x = 0; x < sz; x++) { - bool invert; - switch (msk) { - case 0: invert = (x + y) % 2 == 0; break; - case 1: invert = y % 2 == 0; break; - case 2: invert = x % 3 == 0; break; - case 3: invert = (x + y) % 3 == 0; break; - case 4: invert = (x / 3 + y / 2) % 2 == 0; break; - case 5: invert = x * y % 2 + x * y % 3 == 0; break; - case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; - case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; - default: throw std::logic_error("Assertion error"); - } - modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); - } - } -} - - -long QrCode::getPenaltyScore() const { - long result = 0; - - // Adjacent modules in row having same color, and finder-like patterns - for (int y = 0; y < size; y++) { - bool runColor = false; - int runX = 0; - std::array runHistory = {}; - for (int x = 0; x < size; x++) { - if (module(x, y) == runColor) { - runX++; - if (runX == 5) - result += PENALTY_N1; - else if (runX > 5) - result++; - } else { - finderPenaltyAddHistory(runX, runHistory); - if (!runColor) - result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; - runColor = module(x, y); - runX = 1; - } - } - result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; - } - // Adjacent modules in column having same color, and finder-like patterns - for (int x = 0; x < size; x++) { - bool runColor = false; - int runY = 0; - std::array runHistory = {}; - for (int y = 0; y < size; y++) { - if (module(x, y) == runColor) { - runY++; - if (runY == 5) - result += PENALTY_N1; - else if (runY > 5) - result++; - } else { - finderPenaltyAddHistory(runY, runHistory); - if (!runColor) - result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; - runColor = module(x, y); - runY = 1; - } - } - result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; - } - - // 2*2 blocks of modules having same color - for (int y = 0; y < size - 1; y++) { - for (int x = 0; x < size - 1; x++) { - bool color = module(x, y); - if ( color == module(x + 1, y) && - color == module(x, y + 1) && - color == module(x + 1, y + 1)) - result += PENALTY_N2; - } - } - - // Balance of black and white modules - int black = 0; - for (const vector &row : modules) { - for (bool color : row) { - if (color) - black++; - } - } - int total = size * size; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - int k = static_cast((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1; - result += k * PENALTY_N4; - return result; -} - - -vector QrCode::getAlignmentPatternPositions() const { - if (version == 1) - return vector(); - else { - int numAlign = version / 7 + 2; - int step = (version == 32) ? 26 : - (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; - vector result; - for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) - result.insert(result.begin(), pos); - result.insert(result.begin(), 6); - return result; - } -} - - -int QrCode::getNumRawDataModules(int ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw std::domain_error("Version number out of range"); - int result = (16 * ver + 128) * ver + 64; - if (ver >= 2) { - int numAlign = ver / 7 + 2; - result -= (25 * numAlign - 10) * numAlign - 55; - if (ver >= 7) - result -= 36; - } - if (!(208 <= result && result <= 29648)) - throw std::logic_error("Assertion error"); - return result; -} - - -int QrCode::getNumDataCodewords(int ver, Ecc ecl) { - return getNumRawDataModules(ver) / 8 - - ECC_CODEWORDS_PER_BLOCK [static_cast(ecl)][ver] - * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; -} - - -vector QrCode::reedSolomonComputeDivisor(int degree) { - if (degree < 1 || degree > 255) - throw std::domain_error("Degree out of range"); - // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. - // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - vector result(static_cast(degree)); - result.at(result.size() - 1) = 1; // Start off with the monomial x^0 - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // and drop the highest monomial term which is always 1x^degree. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - uint8_t root = 1; - for (int i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (size_t j = 0; j < result.size(); j++) { - result.at(j) = reedSolomonMultiply(result.at(j), root); - if (j + 1 < result.size()) - result.at(j) ^= result.at(j + 1); - } - root = reedSolomonMultiply(root, 0x02); - } - return result; -} - - -vector QrCode::reedSolomonComputeRemainder(const vector &data, const vector &divisor) { - vector result(divisor.size()); - for (uint8_t b : data) { // Polynomial division - uint8_t factor = b ^ result.at(0); - result.erase(result.begin()); - result.push_back(0); - for (size_t i = 0; i < result.size(); i++) - result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); - } - return result; -} - - -uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { - // Russian peasant multiplication - int z = 0; - for (int i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >> 7) * 0x11D); - z ^= ((y >> i) & 1) * x; - } - if (z >> 8 != 0) - throw std::logic_error("Assertion error"); - return static_cast(z); -} - - -int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) const { - int n = runHistory.at(1); - if (n > size * 3) - throw std::logic_error("Assertion error"); - bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; - return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) - + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); -} - - -int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const { - if (currentRunColor) { // Terminate black run - finderPenaltyAddHistory(currentRunLength, runHistory); - currentRunLength = 0; - } - currentRunLength += size; // Add white border to final run - finderPenaltyAddHistory(currentRunLength, runHistory); - return finderPenaltyCountPatterns(runHistory); -} - - -void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const { - if (runHistory.at(0) == 0) - currentRunLength += size; // Add white border to initial run - std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); - runHistory.at(0) = currentRunLength; -} - - -bool QrCode::getBit(long x, int i) { - return ((x >> i) & 1) != 0; -} - - -/*---- Tables of constants ----*/ - -const int QrCode::PENALTY_N1 = 3; -const int QrCode::PENALTY_N2 = 3; -const int QrCode::PENALTY_N3 = 40; -const int QrCode::PENALTY_N4 = 10; - - -const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { - // Version: (note that index 0 is for padding, and is set to an illegal value) - //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level - {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low - {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium - {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile - {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High -}; - -const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { - // Version: (note that index 0 is for padding, and is set to an illegal value) - //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level - {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low - {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium - {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile - {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High -}; - - -data_too_long::data_too_long(const std::string &msg) : - std::length_error(msg) {} - - - -BitBuffer::BitBuffer() - : std::vector() {} - - -void BitBuffer::appendBits(std::uint32_t val, int len) { - if (len < 0 || len > 31 || val >> len != 0) - throw std::domain_error("Value out of range"); - for (int i = len - 1; i >= 0; i--) // Append bit by bit - this->push_back(((val >> i) & 1) != 0); -} - -} diff --git a/third_party/qrcode/QrCode.hpp b/third_party/qrcode/QrCode.hpp deleted file mode 100644 index 7341e41029..0000000000 --- a/third_party/qrcode/QrCode.hpp +++ /dev/null @@ -1,556 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * 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. - */ - -#pragma once - -#include -#include -#include -#include -#include - - -namespace qrcodegen { - -/* - * A segment of character/binary/control data in a QR Code symbol. - * Instances of this class are immutable. - * The mid-level way to create a segment is to take the payload data - * and call a static factory function such as QrSegment::makeNumeric(). - * The low-level way to create a segment is to custom-make the bit buffer - * and call the QrSegment() constructor with appropriate values. - * This segment class imposes no length restrictions, but QR Codes have restrictions. - * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes. - */ -class QrSegment final { - - /*---- Public helper enumeration ----*/ - - /* - * Describes how a segment's data bits are interpreted. Immutable. - */ - public: class Mode final { - - /*-- Constants --*/ - - public: static const Mode NUMERIC; - public: static const Mode ALPHANUMERIC; - public: static const Mode BYTE; - public: static const Mode KANJI; - public: static const Mode ECI; - - - /*-- Fields --*/ - - // The mode indicator bits, which is a uint4 value (range 0 to 15). - private: int modeBits; - - // Number of character count bits for three different version ranges. - private: int numBitsCharCount[3]; - - - /*-- Constructor --*/ - - private: Mode(int mode, int cc0, int cc1, int cc2); - - - /*-- Methods --*/ - - /* - * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). - */ - public: int getModeBits() const; - - /* - * (Package-private) Returns the bit width of the character count field for a segment in - * this mode in a QR Code at the given version number. The result is in the range [0, 16]. - */ - public: int numCharCountBits(int ver) const; - - }; - - - - /*---- Static factory functions (mid level) ----*/ - - /* - * Returns a segment representing the given binary data encoded in - * byte mode. All input byte vectors are acceptable. Any text string - * can be converted to UTF-8 bytes and encoded as a byte mode segment. - */ - public: static QrSegment makeBytes(const std::vector &data); - - - /* - * Returns a segment representing the given string of decimal digits encoded in numeric mode. - */ - public: static QrSegment makeNumeric(const char *digits); - - - /* - * Returns a segment representing the given text string encoded in alphanumeric mode. - * The characters allowed are: 0 to 9, A to Z (uppercase only), space, - * dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - public: static QrSegment makeAlphanumeric(const char *text); - - - /* - * Returns a list of zero or more segments to represent the given text string. The result - * may use various segment modes and switch modes to optimize the length of the bit stream. - */ - public: static std::vector makeSegments(const char *text); - - - /* - * Returns a segment representing an Extended Channel Interpretation - * (ECI) designator with the given assignment value. - */ - public: static QrSegment makeEci(long assignVal); - - - /*---- Public static helper functions ----*/ - - /* - * Tests whether the given string can be encoded as a segment in alphanumeric mode. - * A string is encodable iff each character is in the following set: 0 to 9, A to Z - * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - public: static bool isAlphanumeric(const char *text); - - - /* - * Tests whether the given string can be encoded as a segment in numeric mode. - * A string is encodable iff each character is in the range 0 to 9. - */ - public: static bool isNumeric(const char *text); - - - - /*---- Instance fields ----*/ - - /* The mode indicator of this segment. Accessed through getMode(). */ - private: Mode mode; - - /* The length of this segment's unencoded data. Measured in characters for - * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - * Always zero or positive. Not the same as the data's bit length. - * Accessed through getNumChars(). */ - private: int numChars; - - /* The data bits of this segment. Accessed through getData(). */ - private: std::vector data; - - - /*---- Constructors (low level) ----*/ - - /* - * Creates a new QR Code segment with the given attributes and data. - * The character count (numCh) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is copied and stored. - */ - public: QrSegment(Mode md, int numCh, const std::vector &dt); - - - /* - * Creates a new QR Code segment with the given parameters and data. - * The character count (numCh) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is moved and stored. - */ - public: QrSegment(Mode md, int numCh, std::vector &&dt); - - - /*---- Methods ----*/ - - /* - * Returns the mode field of this segment. - */ - public: Mode getMode() const; - - - /* - * Returns the character count field of this segment. - */ - public: int getNumChars() const; - - - /* - * Returns the data bits of this segment. - */ - public: const std::vector &getData() const; - - - // (Package-private) Calculates the number of bits needed to encode the given segments at - // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a - // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. - public: static int getTotalBits(const std::vector &segs, int version); - - - /*---- Private constant ----*/ - - /* The set of all legal characters in alphanumeric mode, where - * each character value maps to the index in the string. */ - private: static const char *ALPHANUMERIC_CHARSET; - -}; - - - -/* - * A QR Code symbol, which is a type of two-dimension barcode. - * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this class represent an immutable square grid of black and white cells. - * The class provides static factory functions to create a QR Code from text or binary data. - * The class covers the QR Code Model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. - * - * Ways to create a QR Code object: - * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). - * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). - * - Low level: Custom-make the array of data codeword bytes (including - * segment headers and final padding, excluding error correction codewords), - * supply the appropriate version number, and call the QrCode() constructor. - * (Note that all ways require supplying the desired error correction level.) - */ -class QrCode final { - - /*---- Public helper enumeration ----*/ - - /* - * The error correction level in a QR Code symbol. - */ - public: enum class Ecc { - LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords - MEDIUM , // The QR Code can tolerate about 15% erroneous codewords - QUARTILE, // The QR Code can tolerate about 25% erroneous codewords - HIGH , // The QR Code can tolerate about 30% erroneous codewords - }; - - - // Returns a value in the range 0 to 3 (unsigned 2-bit integer). - private: static int getFormatBits(Ecc ecl); - - - - /*---- Static factory functions (high level) ----*/ - - /* - * Returns a QR Code representing the given Unicode text string at the given error correction level. - * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer - * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible - * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than - * the ecl argument if it can be done without increasing the version. - */ - public: static QrCode encodeText(const char *text, Ecc ecl); - - - /* - * Returns a QR Code representing the given binary data at the given error correction level. - * This function always encodes using the binary segment mode, not any text mode. The maximum number of - * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. - * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. - */ - public: static QrCode encodeBinary(const std::vector &data, Ecc ecl); - - - /*---- Static factory functions (mid level) ----*/ - - /* - * Returns a QR Code representing the given segments with the given encoding parameters. - * The smallest possible QR Code version within the given range is automatically - * chosen for the output. Iff boostEcl is true, then the ECC level of the result - * may be higher than the ecl argument if it can be done without increasing the - * version. The mask number is either between 0 to 7 (inclusive) to force that - * mask, or -1 to automatically choose an appropriate mask (which may be slow). - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). - */ - public: static QrCode encodeSegments(const std::vector &segs, Ecc ecl, - int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters - - - - /*---- Instance fields ----*/ - - // Immutable scalar parameters: - - /* The version number of this QR Code, which is between 1 and 40 (inclusive). - * This determines the size of this barcode. */ - private: int version; - - /* The width and height of this QR Code, measured in modules, between - * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ - private: int size; - - /* The error correction level used in this QR Code. */ - private: Ecc errorCorrectionLevel; - - /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). - * Even if a QR Code is created with automatic masking requested (mask = -1), - * the resulting object still has a mask value between 0 and 7. */ - private: int mask; - - // Private grids of modules/pixels, with dimensions of size*size: - - // The modules of this QR Code (false = white, true = black). - // Immutable after constructor finishes. Accessed through getModule(). - private: std::vector > modules; - - // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. - private: std::vector > isFunction; - - - - /*---- Constructor (low level) ----*/ - - /* - * Creates a new QR Code with the given version number, - * error correction level, data codeword bytes, and mask number. - * This is a low-level API that most users should not use directly. - * A mid-level API is the encodeSegments() function. - */ - public: QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int msk); - - - - /*---- Public instance methods ----*/ - - /* - * Returns this QR Code's version, in the range [1, 40]. - */ - public: int getVersion() const; - - - /* - * Returns this QR Code's size, in the range [21, 177]. - */ - public: int getSize() const; - - - /* - * Returns this QR Code's error correction level. - */ - public: Ecc getErrorCorrectionLevel() const; - - - /* - * Returns this QR Code's mask, in the range [0, 7]. - */ - public: int getMask() const; - - - /* - * Returns the color of the module (pixel) at the given coordinates, which is false - * for white or true for black. The top left corner has the coordinates (x=0, y=0). - * If the given coordinates are out of bounds, then false (white) is returned. - */ - public: bool getModule(int x, int y) const; - - - /* - * Returns a string of SVG code for an image depicting this QR Code, with the given number - * of border modules. The string always uses Unix newlines (\n), regardless of the platform. - */ - public: std::string toSvgString(int border) const; - - - - /*---- Private helper methods for constructor: Drawing function modules ----*/ - - // Reads this object's version field, and draws and marks all function modules. - private: void drawFunctionPatterns(); - - - // Draws two copies of the format bits (with its own error correction code) - // based on the given mask and this object's error correction level field. - private: void drawFormatBits(int msk); - - - // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field, iff 7 <= version <= 40. - private: void drawVersion(); - - - // Draws a 9*9 finder pattern including the border separator, - // with the center module at (x, y). Modules can be out of bounds. - private: void drawFinderPattern(int x, int y); - - - // Draws a 5*5 alignment pattern, with the center module - // at (x, y). All modules must be in bounds. - private: void drawAlignmentPattern(int x, int y); - - - // Sets the color of a module and marks it as a function module. - // Only used by the constructor. Coordinates must be in bounds. - private: void setFunctionModule(int x, int y, bool isBlack); - - - // Returns the color of the module at the given coordinates, which must be in range. - private: bool module(int x, int y) const; - - - /*---- Private helper methods for constructor: Codewords and masking ----*/ - - // Returns a new byte string representing the given data with the appropriate error correction - // codewords appended to it, based on this object's version and error correction level. - private: std::vector addEccAndInterleave(const std::vector &data) const; - - - // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code. Function modules need to be marked off before this is called. - private: void drawCodewords(const std::vector &data); - - - // XORs the codeword modules in this QR Code with the given mask pattern. - // The function modules must be marked and the codeword bits must be drawn - // before masking. Due to the arithmetic of XOR, calling applyMask() with - // the same mask value a second time will undo the mask. A final well-formed - // QR Code needs exactly one (not zero, two, etc.) mask applied. - private: void applyMask(int msk); - - - // Calculates and returns the penalty score based on state of this QR Code's current modules. - // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. - private: long getPenaltyScore() const; - - - - /*---- Private helper functions ----*/ - - // Returns an ascending list of positions of alignment patterns for this version number. - // Each position is in the range [0,177), and are used on both the x and y axes. - // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. - private: std::vector getAlignmentPatternPositions() const; - - - // Returns the number of data bits that can be stored in a QR Code of the given version number, after - // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. - // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. - private: static int getNumRawDataModules(int ver); - - - // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any - // QR Code of the given version number and error correction level, with remainder bits discarded. - // This stateless pure function could be implemented as a (40*4)-cell lookup table. - private: static int getNumDataCodewords(int ver, Ecc ecl); - - - // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be - // implemented as a lookup table over all possible parameter values, instead of as an algorithm. - private: static std::vector reedSolomonComputeDivisor(int degree); - - - // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. - private: static std::vector reedSolomonComputeRemainder(const std::vector &data, const std::vector &divisor); - - - // Returns the product of the two given field elements modulo GF(2^8/0x11D). - // All inputs are valid. This could be implemented as a 256*256 lookup table. - private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); - - - // Can only be called immediately after a white run is added, and - // returns either 0, 1, or 2. A helper function for getPenaltyScore(). - private: int finderPenaltyCountPatterns(const std::array &runHistory) const; - - - // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). - private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const; - - - // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). - private: void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const; - - - // Returns true iff the i'th bit of x is set to 1. - private: static bool getBit(long x, int i); - - - /*---- Constants and tables ----*/ - - // The minimum version number supported in the QR Code Model 2 standard. - public: static constexpr int MIN_VERSION = 1; - - // The maximum version number supported in the QR Code Model 2 standard. - public: static constexpr int MAX_VERSION = 40; - - - // For use in getPenaltyScore(), when evaluating which mask is best. - private: static const int PENALTY_N1; - private: static const int PENALTY_N2; - private: static const int PENALTY_N3; - private: static const int PENALTY_N4; - - - private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; - private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; - -}; - - - -/*---- Public exception class ----*/ - -/* - * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: - * - Decrease the error correction level if it was greater than Ecc::LOW. - * - If the encodeSegments() function was called with a maxVersion argument, then increase - * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other - * factory functions because they search all versions up to QrCode::MAX_VERSION.) - * - Split the text data into better or optimal segments in order to reduce the number of bits required. - * - Change the text or binary data to be shorter. - * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). - * - Propagate the error upward to the caller/user. - */ -class data_too_long : public std::length_error { - - public: explicit data_too_long(const std::string &msg); - -}; - - - -/* - * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. - */ -class BitBuffer final : public std::vector { - - /*---- Constructor ----*/ - - // Creates an empty bit buffer (length 0). - public: BitBuffer(); - - - - /*---- Method ----*/ - - // Appends the given number of low-order bits of the given value - // to this buffer. Requires 0 <= len <= 31 and val < 2^len. - public: void appendBits(std::uint32_t val, int len); - -}; - -} From fc4e5007fd55784a5aaf471744f431baa11b118f Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:29:38 -0700 Subject: [PATCH 289/341] latcontrol_torque: make feed-forward jerk independent of individual platform lag (#36334) --- selfdrive/controls/lib/latcontrol_torque.py | 19 ++++++++++++------- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 443fd1851c..1e3d80c2d5 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -27,8 +27,10 @@ INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30] KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP] LP_FILTER_CUTOFF_HZ = 1.2 +JERK_LOOKAHEAD_SECONDS = 0.19 +JERK_GAIN = 0.3 LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 -VERSION = 0 +VERSION = 0 # bump this when changing controller class LatControlTorque(LatControl): def __init__(self, CP, CI, dt): @@ -39,10 +41,12 @@ class LatControlTorque(LatControl): self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, KD, rate=1/self.dt) self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg + self.lookahead_frames = int(JERK_LOOKAHEAD_SECONDS / self.dt) self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt) self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len) - self.previous_measurement = 0.0 + self.jerk_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt) self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt) + self.previous_measurement = 0.0 def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction): self.torque_params.latAccelFactor = latAccelFactor @@ -68,25 +72,26 @@ class LatControlTorque(LatControl): delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len)) expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames] - # TODO factor out lateral jerk from error to later replace it with delay independent alternative + lookahead_idx = int(np.clip(-delay_frames + self.lookahead_frames, -self.lat_accel_request_buffer_len+1, -2)) + raw_lateral_jerk = (self.lat_accel_request_buffer[lookahead_idx+1] - self.lat_accel_request_buffer[lookahead_idx-1]) / (2 * self.dt) + desired_lateral_jerk = self.jerk_filter.update(raw_lateral_jerk) future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2 self.lat_accel_request_buffer.append(future_desired_lateral_accel) gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation - desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay + setpoint = expected_lateral_accel measurement = measured_curvature * CS.vEgo ** 2 measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt) self.previous_measurement = measurement - setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel - error = setpoint - measurement + error = setpoint - measurement + JERK_GAIN * desired_lateral_jerk # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly pid_log.error = float(error) ff = gravity_adjusted_future_lateral_accel # latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll ff -= self.torque_params.latAccelOffset - # TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it + # TODO remove lateral jerk from feed forward - moving it from error means jerk is not scaled by low speed factor ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index cdd4301fcf..37d1e8f8d3 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -b508f43fb0481bce0859c9b6ab4f45ee690b8dab \ No newline at end of file +9a7d8dd0660ba6a344ac61be89b2e832e6756184 \ No newline at end of file From 76c5cb6d8737c11235b8d4843a3a921645faea8b Mon Sep 17 00:00:00 2001 From: felsager <76905857+felsager@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:34:44 -0700 Subject: [PATCH 290/341] latcontrol_torque: retune torque controller (#36392) --- selfdrive/controls/lib/latcontrol_torque.py | 17 ++++++----------- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 1e3d80c2d5..f36aabc1d2 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -21,8 +21,8 @@ from openpilot.common.pid import PIDController # to be overcome to move it at all, this is compensated for too. KP = 1.0 -KI = 0.3 -KD = 0.0 +KI = 0.1 +KD = 0.3 INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30] KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP] @@ -30,7 +30,7 @@ LP_FILTER_CUTOFF_HZ = 1.2 JERK_LOOKAHEAD_SECONDS = 0.19 JERK_GAIN = 0.3 LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 -VERSION = 0 # bump this when changing controller +VERSION = 1 # bump this when changing controller class LatControlTorque(LatControl): def __init__(self, CP, CI, dt): @@ -84,22 +84,17 @@ class LatControlTorque(LatControl): measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt) self.previous_measurement = measurement - error = setpoint - measurement + JERK_GAIN * desired_lateral_jerk + error = setpoint - measurement # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly pid_log.error = float(error) ff = gravity_adjusted_future_lateral_accel # latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll ff -= self.torque_params.latAccelOffset - # TODO remove lateral jerk from feed forward - moving it from error means jerk is not scaled by low speed factor - ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) + ff += get_friction(error + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 - output_lataccel = self.pid.update(pid_log.error, - -measurement_rate, - feedforward=ff, - speed=CS.vEgo, - freeze_integrator=freeze_integrator) + output_lataccel = self.pid.update(pid_log.error, -measurement_rate, CS.vEgo, ff, freeze_integrator) output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params) pid_log.active = True diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 37d1e8f8d3..f313edbc62 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -9a7d8dd0660ba6a344ac61be89b2e832e6756184 \ No newline at end of file +d74c33b02938cafb3422f3500cfd083bd965e637 From c4a0e5704685e5cfb49c250a0941a05fd568f90f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 30 Oct 2025 15:52:56 -0700 Subject: [PATCH 291/341] ui: add debug toggle (#36529) * ui: add debug toggle * initial state --- common/params_keys.h | 1 + selfdrive/ui/layouts/settings/developer.py | 21 +++++++++++++++++---- system/ui/lib/application.py | 12 ++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index f6e8d781c8..badc162149 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -108,6 +108,7 @@ inline static std::unordered_map keys = { {"RecordFront", {PERSISTENT, BOOL}}, {"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet {"SecOCKey", {PERSISTENT | DONT_LOG, STRING}}, + {"ShowDebugInfo", {PERSISTENT, BOOL}}, {"RouteCount", {PERSISTENT, INT, "0"}}, {"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}}, {"SshEnabled", {PERSISTENT, BOOL}}, diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index 0419729a4d..cb7b0f9e91 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -75,16 +75,23 @@ class DeveloperLayout(Widget): enabled=lambda: not ui_state.engaged, ) - items = [ + self._ui_debug_toggle = toggle_item( + lambda: tr("UI Debug Mode"), + description="", + initial_state=self._params.get_bool("ShowDebugInfo"), + callback=self._on_enable_ui_debug, + ) + self._on_enable_ui_debug(self._params.get_bool("ShowDebugInfo")) + + self._scroller = Scroller([ self._adb_toggle, self._ssh_toggle, self._ssh_keys, self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle, - ] - - self._scroller = Scroller(items, line_separator=True, spacing=0) + self._ui_debug_toggle, + ], line_separator=True, spacing=0) # Toggles should be not available to change in onroad state ui_state.add_offroad_transition_callback(self._update_toggles) @@ -130,9 +137,15 @@ class DeveloperLayout(Widget): ("JoystickDebugMode", self._joystick_toggle), ("LongitudinalManeuverMode", self._long_maneuver_toggle), ("AlphaLongitudinalEnabled", self._alpha_long_toggle), + ("ShowDebugInfo", self._ui_debug_toggle), ): item.action_item.set_state(self._params.get_bool(key)) + def _on_enable_ui_debug(self, state: bool): + self._params.put_bool("ShowDebugInfo", state) + gui_app.set_show_touches(state) + gui_app.set_show_fps(state) + def _on_enable_adb(self, state: bool): self._params.put_bool("AdbEnabled", state) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 8e45191bf6..bdd103c5c1 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -160,6 +160,14 @@ class GuiApplication: # Debug variables self._mouse_history: deque[MousePos] = deque(maxlen=MOUSE_THREAD_RATE) + self._show_touches = SHOW_TOUCHES + self._show_fps = SHOW_FPS + + def set_show_touches(self, show: bool): + self._show_touches = show + + def set_show_fps(self, show: bool): + self._show_fps = show @property def target_fps(self): @@ -367,10 +375,10 @@ class GuiApplication: dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height)) rl.draw_texture_pro(self._render_texture.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) - if SHOW_FPS: + if self._show_fps: rl.draw_fps(10, 10) - if SHOW_TOUCHES: + if self._show_touches: for mouse_event in self._mouse_events: if mouse_event.left_pressed: self._mouse_history.clear() From f57617c944afb2217b2737a42f445a6b1908c755 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 30 Oct 2025 16:03:28 -0700 Subject: [PATCH 292/341] expose more state from gui_app --- system/ui/lib/application.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index bdd103c5c1..6f67139c5a 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -14,11 +14,11 @@ from enum import StrEnum from typing import NamedTuple from importlib.resources import as_file, files from openpilot.common.swaglog import cloudlog -from openpilot.system.hardware import HARDWARE, PC, TICI +from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.multilang import multilang from openpilot.common.realtime import Ratekeeper -_DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) +_DEFAULT_FPS = int(os.getenv("FPS", {'tizi': 20}.get(HARDWARE.get_device_type(), 60))) FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions @@ -149,12 +149,15 @@ class GuiApplication: self._textures: dict[str, rl.Texture] = {} self._target_fps: int = _DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() + self._frame = 0 self._window_close_requested = False self._trace_log_callback = None self._modal_overlay = ModalOverlay() + self._modal_overlay_shown = False self._mouse = MouseState(self._scale) self._mouse_events: list[MouseEvent] = [] + self._last_mouse_event: MouseEvent = MouseEvent(MousePos(0, 0), 0, False, False, False, 0.0) self._should_render = True @@ -163,6 +166,10 @@ class GuiApplication: self._show_touches = SHOW_TOUCHES self._show_fps = SHOW_FPS + @property + def frame(self): + return self._frame + def set_show_touches(self, show: bool): self._show_touches = show @@ -325,6 +332,10 @@ class GuiApplication: def mouse_events(self) -> list[MouseEvent]: return self._mouse_events + @property + def last_mouse_event(self) -> MouseEvent: + return self._last_mouse_event + def render(self): try: while not (self._window_close_requested or rl.window_should_close()): @@ -334,6 +345,8 @@ class GuiApplication: # Store all mouse events for the current frame self._mouse_events = self._mouse.get_events() + if len(self._mouse_events) > 0: + self._last_mouse_event = self._mouse_events[-1] # Skip rendering when screen is off if not self._should_render: @@ -357,6 +370,11 @@ class GuiApplication: else: raise Exception + # Send show event to Widget + if not self._modal_overlay_shown and hasattr(self._modal_overlay.overlay, 'show_event'): + self._modal_overlay.overlay.show_event() + self._modal_overlay_shown = True + if result >= 0: # Clear the overlay and execute the callback original_modal = self._modal_overlay @@ -365,6 +383,7 @@ class GuiApplication: original_modal.callback(result) yield False else: + self._modal_overlay_shown = False yield True if self._render_texture: @@ -394,6 +413,7 @@ class GuiApplication: rl.end_drawing() self._monitor_fps() + self._frame += 1 except KeyboardInterrupt: pass From 62aef9cd34a216a970670b707f906b2b080afbf6 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 31 Oct 2025 23:16:55 +0800 Subject: [PATCH 293/341] tools: update README (#36531) update readme --- tools/camerastream/README.md | 4 ++-- tools/replay/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/camerastream/README.md b/tools/camerastream/README.md index 7dbafb4be5..2f7498a07c 100644 --- a/tools/camerastream/README.md +++ b/tools/camerastream/README.md @@ -39,7 +39,7 @@ Decode the stream with `compressed_vipc.py`: To actually display the stream, run `watch3` in separate terminal: -```cd ~/openpilot/selfdrive/ui/ && ./watch3``` +```cd ~/openpilot/selfdrive/ui/ && ./watch3.py``` ## compressed_vipc.py usage ``` @@ -62,5 +62,5 @@ options: ## Example: ``` cd ~/openpilot/tools/camerastream && ./compressed_vipc.py comma-ffffffff --cams 0 -cd ~/openpilot/selfdrive/ui/ && ./watch3 +cd ~/openpilot/selfdrive/ui/ && ./watch3.py ``` diff --git a/tools/replay/README.md b/tools/replay/README.md index 72216e2cc8..794c08f6a3 100644 --- a/tools/replay/README.md +++ b/tools/replay/README.md @@ -88,7 +88,7 @@ To visualize the replay within the openpilot UI, run the following commands: ```bash tools/replay/replay -cd selfdrive/ui && ./ui +cd selfdrive/ui && ./ui.py ``` ## Work with plotjuggler @@ -110,7 +110,7 @@ simply replay a route using the `--dcam` and `--ecam` flags: cd tools/replay && ./replay --demo --dcam --ecam # then start watch3 -cd selfdrive/ui && ./watch3 +cd selfdrive/ui && ./watch3.py ``` ![](https://i.imgur.com/IeaOdAb.png) From c7b115b68e9a7fbee55a7fbd01ebc8e28d4588b2 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 2 Nov 2025 04:30:08 -0800 Subject: [PATCH 294/341] raylib: fix text measure with emojis (#36546) fix --- system/ui/lib/text_measure.py | 1 + 1 file changed, 1 insertion(+) diff --git a/system/ui/lib/text_measure.py b/system/ui/lib/text_measure.py index ea6fa63365..544ba5b870 100644 --- a/system/ui/lib/text_measure.py +++ b/system/ui/lib/text_measure.py @@ -20,6 +20,7 @@ def measure_text_cached(font: rl.Font, text: str, font_size: int, spacing: int = for start, end, _ in emoji: non_emoji_text += text[last_index:start] last_index = end + non_emoji_text += text[last_index:] else: non_emoji_text = text From 525b6e48e99a00f95cf5fe12fce71c9434219583 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 2 Nov 2025 04:32:29 -0800 Subject: [PATCH 295/341] raylib: fix word wrap (#36545) * fix word wrap underestimating width * and that --- system/ui/lib/wrap_text.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/system/ui/lib/wrap_text.py b/system/ui/lib/wrap_text.py index d51975087a..745d37b468 100644 --- a/system/ui/lib/wrap_text.py +++ b/system/ui/lib/wrap_text.py @@ -67,8 +67,6 @@ def wrap_text(font: rl.Font, text: str, font_size: int, max_width: int) -> list[ lines: list[str] = [] current_line: list[str] = [] - current_width = 0 - space_width = int(measure_text_cached(font, " ", font_size).x) for word in words: word_width = int(measure_text_cached(font, word, font_size).x) @@ -79,28 +77,23 @@ def wrap_text(font: rl.Font, text: str, font_size: int, max_width: int) -> list[ if current_line: lines.append(" ".join(current_line)) current_line = [] - current_width = 0 # Break the long word into parts lines.extend(_break_long_word(font, word, font_size, max_width)) continue - # Calculate width if we add this word - needed_width = current_width - if current_line: # Need space before word - needed_width += space_width - needed_width += word_width + # Measure the actual joined string to get accurate width (accounts for kerning, etc.) + test_line = " ".join(current_line + [word]) if current_line else word + test_width = int(measure_text_cached(font, test_line, font_size).x) # Check if word fits on current line - if needed_width <= max_width: + if test_width <= max_width: current_line.append(word) - current_width = needed_width else: # Start new line with this word if current_line: lines.append(" ".join(current_line)) current_line = [word] - current_width = word_width # Add remaining words if current_line: From 5ea5f6f267301d5bc83bdc17004807740608abd0 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 2 Nov 2025 21:48:33 -0800 Subject: [PATCH 296/341] ui: timeout touch points (#36550) --- system/ui/lib/application.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 6f67139c5a..d1d74d3ac6 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -24,6 +24,7 @@ FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions MOUSE_THREAD_RATE = 140 # touch controller runs at 140Hz MAX_TOUCH_SLOTS = 2 +TOUCH_HISTORY_TIMEOUT = 3.0 # Seconds before touch points fade out ENABLE_VSYNC = os.getenv("ENABLE_VSYNC", "0") == "1" SHOW_FPS = os.getenv("SHOW_FPS") == "1" @@ -69,6 +70,12 @@ class MousePos(NamedTuple): y: float +class MousePosWithTime(NamedTuple): + x: float + y: float + t: float + + class MouseEvent(NamedTuple): pos: MousePos slot: int @@ -162,7 +169,7 @@ class GuiApplication: self._should_render = True # Debug variables - self._mouse_history: deque[MousePos] = deque(maxlen=MOUSE_THREAD_RATE) + self._mouse_history: deque[MousePosWithTime] = deque(maxlen=MOUSE_THREAD_RATE) self._show_touches = SHOW_TOUCHES self._show_fps = SHOW_FPS @@ -398,10 +405,16 @@ class GuiApplication: rl.draw_fps(10, 10) if self._show_touches: + current_time = time.monotonic() + for mouse_event in self._mouse_events: if mouse_event.left_pressed: self._mouse_history.clear() - self._mouse_history.append(mouse_event.pos) + self._mouse_history.append(MousePosWithTime(mouse_event.pos.x, mouse_event.pos.y, current_time)) + + # Remove old touch points that exceed the timeout + while self._mouse_history and (current_time - self._mouse_history[0].t) > TOUCH_HISTORY_TIMEOUT: + self._mouse_history.popleft() if self._mouse_history: mouse_pos = self._mouse_history[-1] From 9bf904e8a644a6d642c387b05f6a66fcc305e802 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 4 Nov 2025 00:55:05 +0800 Subject: [PATCH 297/341] ui: scale mouse positions in touch history (#36530) scale mouse position in touch history --- system/ui/lib/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index d1d74d3ac6..97aaaa27b4 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -410,7 +410,7 @@ class GuiApplication: for mouse_event in self._mouse_events: if mouse_event.left_pressed: self._mouse_history.clear() - self._mouse_history.append(MousePosWithTime(mouse_event.pos.x, mouse_event.pos.y, current_time)) + self._mouse_history.append(MousePosWithTime(mouse_event.pos.x * self._scale, mouse_event.pos.y * self._scale, current_time)) # Remove old touch points that exceed the timeout while self._mouse_history and (current_time - self._mouse_history[0].t) > TOUCH_HISTORY_TIMEOUT: From 177c7f1cf327a54a005f30e0d12d1b6954dc2648 Mon Sep 17 00:00:00 2001 From: felsager Date: Mon, 3 Nov 2025 10:31:22 -0800 Subject: [PATCH 298/341] Revert "latcontrol_torque: retune torque controller (#36392)" This reverts commit 76c5cb6d8737c11235b8d4843a3a921645faea8b. --- selfdrive/controls/lib/latcontrol_torque.py | 17 +++++++++++------ selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index f36aabc1d2..1e3d80c2d5 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -21,8 +21,8 @@ from openpilot.common.pid import PIDController # to be overcome to move it at all, this is compensated for too. KP = 1.0 -KI = 0.1 -KD = 0.3 +KI = 0.3 +KD = 0.0 INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30] KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP] @@ -30,7 +30,7 @@ LP_FILTER_CUTOFF_HZ = 1.2 JERK_LOOKAHEAD_SECONDS = 0.19 JERK_GAIN = 0.3 LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 -VERSION = 1 # bump this when changing controller +VERSION = 0 # bump this when changing controller class LatControlTorque(LatControl): def __init__(self, CP, CI, dt): @@ -84,17 +84,22 @@ class LatControlTorque(LatControl): measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt) self.previous_measurement = measurement - error = setpoint - measurement + error = setpoint - measurement + JERK_GAIN * desired_lateral_jerk # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly pid_log.error = float(error) ff = gravity_adjusted_future_lateral_accel # latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll ff -= self.torque_params.latAccelOffset - ff += get_friction(error + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) + # TODO remove lateral jerk from feed forward - moving it from error means jerk is not scaled by low speed factor + ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 - output_lataccel = self.pid.update(pid_log.error, -measurement_rate, CS.vEgo, ff, freeze_integrator) + output_lataccel = self.pid.update(pid_log.error, + -measurement_rate, + feedforward=ff, + speed=CS.vEgo, + freeze_integrator=freeze_integrator) output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params) pid_log.active = True diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index f313edbc62..37d1e8f8d3 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -d74c33b02938cafb3422f3500cfd083bd965e637 +9a7d8dd0660ba6a344ac61be89b2e832e6756184 \ No newline at end of file From 736e1fa7b772b0a936e3138cbb25c469ce2ab312 Mon Sep 17 00:00:00 2001 From: felsager Date: Mon, 3 Nov 2025 10:31:27 -0800 Subject: [PATCH 299/341] Revert "latcontrol_torque: make feed-forward jerk independent of individual platform lag (#36334)" This reverts commit fc4e5007fd55784a5aaf471744f431baa11b118f. --- selfdrive/controls/lib/latcontrol_torque.py | 19 +++++++------------ selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 1e3d80c2d5..443fd1851c 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -27,10 +27,8 @@ INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30] KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP] LP_FILTER_CUTOFF_HZ = 1.2 -JERK_LOOKAHEAD_SECONDS = 0.19 -JERK_GAIN = 0.3 LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 -VERSION = 0 # bump this when changing controller +VERSION = 0 class LatControlTorque(LatControl): def __init__(self, CP, CI, dt): @@ -41,12 +39,10 @@ class LatControlTorque(LatControl): self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, KD, rate=1/self.dt) self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg - self.lookahead_frames = int(JERK_LOOKAHEAD_SECONDS / self.dt) self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt) self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len) - self.jerk_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt) - self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt) self.previous_measurement = 0.0 + self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt) def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction): self.torque_params.latAccelFactor = latAccelFactor @@ -72,26 +68,25 @@ class LatControlTorque(LatControl): delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len)) expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames] - lookahead_idx = int(np.clip(-delay_frames + self.lookahead_frames, -self.lat_accel_request_buffer_len+1, -2)) - raw_lateral_jerk = (self.lat_accel_request_buffer[lookahead_idx+1] - self.lat_accel_request_buffer[lookahead_idx-1]) / (2 * self.dt) - desired_lateral_jerk = self.jerk_filter.update(raw_lateral_jerk) + # TODO factor out lateral jerk from error to later replace it with delay independent alternative future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2 self.lat_accel_request_buffer.append(future_desired_lateral_accel) gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation - setpoint = expected_lateral_accel + desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay measurement = measured_curvature * CS.vEgo ** 2 measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt) self.previous_measurement = measurement - error = setpoint - measurement + JERK_GAIN * desired_lateral_jerk + setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel + error = setpoint - measurement # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly pid_log.error = float(error) ff = gravity_adjusted_future_lateral_accel # latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll ff -= self.torque_params.latAccelOffset - # TODO remove lateral jerk from feed forward - moving it from error means jerk is not scaled by low speed factor + # TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 37d1e8f8d3..cdd4301fcf 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -9a7d8dd0660ba6a344ac61be89b2e832e6756184 \ No newline at end of file +b508f43fb0481bce0859c9b6ab4f45ee690b8dab \ No newline at end of file From 2cc4885a2e848339f56716c9a5bbd3e14577cc67 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 3 Nov 2025 11:17:38 -0800 Subject: [PATCH 300/341] raylib: fix window freezing (#36517) fix window freezing --- system/ui/lib/application.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 97aaaa27b4..bca836334f 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -357,6 +357,8 @@ class GuiApplication: # Skip rendering when screen is off if not self._should_render: + if PC: + rl.poll_input_events() time.sleep(1 / self._target_fps) yield False continue From 137d4b89b4174504d3e3b9842f1c65cc45ec4d9f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 4 Nov 2025 03:20:08 +0800 Subject: [PATCH 301/341] ui: fix icon vertical positioning using width instead of height (#36542) fix icon vertical positioning using width instead of height --- system/ui/widgets/list_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index 20a5f77911..cfb8ab58a3 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -354,7 +354,7 @@ class ListItem(Widget): if self.title: # Draw icon if present if self.icon: - rl.draw_texture(self._icon_texture, int(content_x), int(self._rect.y + (ITEM_BASE_HEIGHT - self._icon_texture.width) // 2), rl.WHITE) + rl.draw_texture(self._icon_texture, int(content_x), int(self._rect.y + (ITEM_BASE_HEIGHT - self._icon_texture.height) // 2), rl.WHITE) text_x += ICON_SIZE + ITEM_PADDING # Draw main text From 1c0b08710560806122a62fdd2b0bfff79c4095d6 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 4 Nov 2025 03:20:24 +0800 Subject: [PATCH 302/341] ui: fix keyboard.reset() to properly clear all interaction state (#36541) fix keyboard.reset() to properly clear all interaction state --- system/ui/widgets/keyboard.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index b0e1035687..4ec92f507a 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -251,6 +251,10 @@ class Keyboard(Widget): if min_text_size is not None: self._min_text_size = min_text_size self._render_return_status = -1 + self._last_shift_press_time = 0 + self._backspace_pressed = False + self._backspace_press_time = 0.0 + self._backspace_last_repeat = 0.0 self.clear() From 9ce9920ff7503e16f5ea6eefcbf4ab1697546299 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 4 Nov 2025 03:21:20 +0800 Subject: [PATCH 303/341] ui: fix label text eliding to account for icon width (#36539) fix label text eliding to account for icon width --- system/ui/widgets/label.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 4259208107..59d6a5947b 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -145,6 +145,8 @@ class Label(Widget): # Elide text to fit within the rectangle text_size = measure_text_cached(self._font, text, self._font_size) content_width = self._rect.width - self._text_padding * 2 + if self._icon: + content_width -= self._icon.width + ICON_PADDING if text_size.x > content_width: _ellipsis = "..." left, right = 0, len(text) From 350b846d3a3b6d9677a4da332895d61fbd1b67ca Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 4 Nov 2025 03:21:41 +0800 Subject: [PATCH 304/341] ui: fix vertical centering for multi-line labels (#36538) fix vertical centering for multi-line labels --- system/ui/widgets/label.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 59d6a5947b..7d76802565 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -175,7 +175,8 @@ class Label(Widget): text_size = self._text_size[0] if self._text_size else rl.Vector2(0.0, 0.0) if self._text_alignment_vertical == rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE: - text_pos = rl.Vector2(self._rect.x, (self._rect.y + (self._rect.height - text_size.y) // 2)) + total_text_height = sum(ts.y for ts in self._text_size) or self._font_size * FONT_SCALE + text_pos = rl.Vector2(self._rect.x, (self._rect.y + (self._rect.height - total_text_height) // 2)) else: text_pos = rl.Vector2(self._rect.x, self._rect.y) From 215ef168036b2a5fc5427150b19d810457a2ebc3 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 4 Nov 2025 03:22:42 +0800 Subject: [PATCH 305/341] ui: fix LineSeparator horizontal centering issue (#36533) fix LineSeparator horizontal centering issue --- system/ui/widgets/scroller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/scroller.py b/system/ui/widgets/scroller.py index f19a6fbfdb..a843010d56 100644 --- a/system/ui/widgets/scroller.py +++ b/system/ui/widgets/scroller.py @@ -18,7 +18,7 @@ class LineSeparator(Widget): def _render(self, _): rl.draw_line(int(self._rect.x) + LINE_PADDING, int(self._rect.y), - int(self._rect.x + self._rect.width) - LINE_PADDING * 2, int(self._rect.y), + int(self._rect.x + self._rect.width) - LINE_PADDING, int(self._rect.y), LINE_COLOR) From c7494aed0fc7ea87a291784d8e8c854f679eee2b Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 3 Nov 2025 14:31:45 -0800 Subject: [PATCH 306/341] ui: move to GPU core (#36553) * ui: move to GPU core * we're on the big boy core now --- selfdrive/test/test_onroad.py | 2 +- selfdrive/ui/ui.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index f90627e10c..69d920c1a0 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -42,7 +42,7 @@ PROCS = { "./encoderd": 13.0, "./camerad": 10.0, "selfdrive.controls.plannerd": 8.0, - "selfdrive.ui.ui": 63.0, + "selfdrive.ui.ui": 40.0, "system.sensord.sensord": 13.0, "selfdrive.controls.radard": 2.0, "selfdrive.modeld.modeld": 22.0, diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index 4a1f03fb87..222a25b87b 100755 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -1,14 +1,16 @@ #!/usr/bin/env python3 +import os import pyray as rl -from openpilot.common.realtime import config_realtime_process +from openpilot.common.realtime import config_realtime_process, set_core_affinity from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.main import MainLayout from openpilot.selfdrive.ui.ui_state import ui_state def main(): - config_realtime_process([1, 2], 1) + cores = {7, } + config_realtime_process(0, 1) gui_app.init_window("UI") main_layout = MainLayout() @@ -18,6 +20,13 @@ def main(): if should_render: main_layout.render() + # reaffine after power save offlines our core + if os.sched_getaffinity(0) != cores: + try: + set_core_affinity(list(cores)) + except OSError: + pass + if __name__ == "__main__": main() From ecdcb5d0c6ad3e67ab2f378828b991d20c96eae8 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 3 Nov 2025 14:36:19 -0800 Subject: [PATCH 307/341] tici: affine DRM IRQ to same core as ui (#36554) --- system/hardware/tici/hardware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 36e65ad91c..ff2622e963 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -419,12 +419,12 @@ class Tici(HardwareBase): sudo_write("f", "/proc/irq/default_smp_affinity") # move these off the default core - affine_irq(1, "msm_drm") # display affine_irq(1, "msm_vidc") # encoders affine_irq(1, "i2c_geni") # sensors # *** GPU config *** # https://github.com/commaai/agnos-kernel-sdm845/blob/master/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi#L216 + affine_irq(7, "msm_drm") # display sudo_write("1", "/sys/class/kgsl/kgsl-3d0/min_pwrlevel") sudo_write("1", "/sys/class/kgsl/kgsl-3d0/max_pwrlevel") sudo_write("1", "/sys/class/kgsl/kgsl-3d0/force_bus_on") From cbc8f98682d6ff7534993cfd05b023d644e5c180 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 3 Nov 2025 16:20:36 -0800 Subject: [PATCH 308/341] ui: fix RuntimeError on exit on PC --- system/ui/lib/wifi_manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index b6305a03f5..4cf2ccebc8 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -746,8 +746,10 @@ class WifiManager: def stop(self): if not self._exit: self._exit = True - self._scan_thread.join() - self._state_thread.join() + if self._scan_thread.is_alive(): + self._scan_thread.join() + if self._state_thread.is_alive(): + self._state_thread.join() self._router_main.close() self._router_main.conn.close() From e8a11591a8b423ad3730846c1b39a4441b4e8d03 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 3 Nov 2025 21:45:46 -0800 Subject: [PATCH 309/341] ui: add render loop profiling (#36558) --- system/ui/lib/application.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index bca836334f..f9d7b0c9c1 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -31,6 +31,7 @@ SHOW_FPS = os.getenv("SHOW_FPS") == "1" SHOW_TOUCHES = os.getenv("SHOW_TOUCHES") == "1" STRICT_MODE = os.getenv("STRICT_MODE") == "1" SCALE = float(os.getenv("SCALE", "1.0")) +PROFILE_RENDER = int(os.getenv("PROFILE_RENDER", "0")) DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE @@ -172,6 +173,9 @@ class GuiApplication: self._mouse_history: deque[MousePosWithTime] = deque(maxlen=MOUSE_THREAD_RATE) self._show_touches = SHOW_TOUCHES self._show_fps = SHOW_FPS + self._profile_render_frames = PROFILE_RENDER + self._render_profiler = None + self._render_profile_start_time = None @property def frame(self): @@ -345,6 +349,12 @@ class GuiApplication: def render(self): try: + if self._profile_render_frames > 0: + import cProfile + self._render_profiler = cProfile.Profile() + self._render_profile_start_time = time.monotonic() + self._render_profiler.enable() + while not (self._window_close_requested or rl.window_should_close()): if PC: # Thread is not used on PC, need to manually add mouse events @@ -429,6 +439,9 @@ class GuiApplication: rl.end_drawing() self._monitor_fps() self._frame += 1 + + if self._profile_render_frames > 0 and self._frame >= self._profile_render_frames: + self._output_render_profile() except KeyboardInterrupt: pass @@ -526,6 +539,25 @@ class GuiApplication: cloudlog.error(f"FPS dropped critically below {fps}. Shutting down UI.") os._exit(1) + def _output_render_profile(self): + import io + import pstats + + self._render_profiler.disable() + elapsed_ms = (time.monotonic() - self._render_profile_start_time) * 1e3 + avg_frame_time = elapsed_ms / self._frame if self._frame > 0 else 0 + + stats_stream = io.StringIO() + pstats.Stats(self._render_profiler, stream=stats_stream).sort_stats("cumtime").print_stats(25) + print("\n=== Render loop profile ===") + print(stats_stream.getvalue().rstrip()) + + green = "\033[92m" + reset = "\033[0m" + print(f"\n{green}Rendered {self._frame} frames in {elapsed_ms:.1f} ms{reset}") + print(f"{green}Average frame time: {avg_frame_time:.2f} ms ({1000/avg_frame_time:.1f} FPS){reset}") + sys.exit(0) + def _calculate_auto_scale(self) -> float: # Create temporary window to query monitor info rl.init_window(1, 1, "") From 5198b1b079c37742c1050f02ce0aa6dd42b038b9 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 3 Nov 2025 22:45:00 -0800 Subject: [PATCH 310/341] support ECDSA (#36555) * keys * remove * remove * too small --- common/api.py | 16 +++++++++++++--- system/athena/registration.py | 17 +++++++---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/common/api.py b/common/api.py index 005655b21d..656c05ed59 100644 --- a/common/api.py +++ b/common/api.py @@ -7,11 +7,14 @@ from openpilot.system.version import get_version API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com') + # name : jwt signature algorithm +KEYS = {"id_rsa" : "RS256", + "id_ecdsa" : "ES256"} + class Api: def __init__(self, dongle_id): self.dongle_id = dongle_id - with open(Paths.persist_root()+'/comma/id_rsa') as f: - self.private_key = f.read() + self.jwt_algorithm, self.private_key, _ = get_key_pair() def get(self, *args, **kwargs): return self.request('GET', *args, **kwargs) @@ -32,7 +35,7 @@ class Api: } if payload_extra is not None: payload.update(payload_extra) - token = jwt.encode(payload, self.private_key, algorithm='RS256') + token = jwt.encode(payload, self.private_key, algorithm=self.jwt_algorithm) if isinstance(token, bytes): token = token.decode('utf8') return token @@ -46,3 +49,10 @@ def api_get(endpoint, method='GET', timeout=None, access_token=None, **params): headers['User-Agent'] = "openpilot-" + get_version() return requests.request(method, API_HOST + "/" + endpoint, timeout=timeout, headers=headers, params=params) + +def get_key_pair(): + for key in KEYS: + if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'): + with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public: + return KEYS[key], private.read(), public.read() + return None, None, None diff --git a/system/athena/registration.py b/system/athena/registration.py index 80c087188d..81aee1ebf9 100755 --- a/system/athena/registration.py +++ b/system/athena/registration.py @@ -5,7 +5,7 @@ import jwt from pathlib import Path from datetime import datetime, timedelta, UTC -from openpilot.common.api import api_get +from openpilot.common.api import api_get, get_key_pair from openpilot.common.params import Params from openpilot.common.spinner import Spinner from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert @@ -39,20 +39,17 @@ def register(show_spinner=False) -> str | None: with open(Paths.persist_root()+"/comma/dongle_id") as f: dongle_id = f.read().strip() - pubkey = Path(Paths.persist_root()+"/comma/id_rsa.pub") - if not pubkey.is_file(): + # Create registration token, in the future, this key will make JWTs directly + jwt_algo, private_key, public_key = get_key_pair() + + if not public_key: dongle_id = UNREGISTERED_DONGLE_ID - cloudlog.warning(f"missing public key: {pubkey}") + cloudlog.warning("missing public key") elif dongle_id is None: if show_spinner: spinner = Spinner() spinner.update("registering device") - # Create registration token, in the future, this key will make JWTs directly - with open(Paths.persist_root()+"/comma/id_rsa.pub") as f1, open(Paths.persist_root()+"/comma/id_rsa") as f2: - public_key = f1.read() - private_key = f2.read() - # Block until we get the imei serial = HARDWARE.get_serial() start_time = time.monotonic() @@ -72,7 +69,7 @@ def register(show_spinner=False) -> str | None: start_time = time.monotonic() while True: try: - register_token = jwt.encode({'register': True, 'exp': datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1)}, private_key, algorithm='RS256') + register_token = jwt.encode({'register': True, 'exp': datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1)}, private_key, algorithm=jwt_algo) cloudlog.info("getting pilotauth") resp = api_get("v2/pilotauth/", method='POST', timeout=15, imei=imei1, imei2=imei2, serial=serial, public_key=public_key, register_token=register_token) From 38eb400e41c18bfdb6fbde00f1217661aec22e81 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Tue, 4 Nov 2025 17:00:15 -0700 Subject: [PATCH 311/341] monitoring: account for OS cam distribution shift (#36567) * this should match * roughly matching FPR at 2 to 1 cost --- selfdrive/monitoring/helpers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfdrive/monitoring/helpers.py b/selfdrive/monitoring/helpers.py index c2e5bc3fe4..83904e2fb8 100644 --- a/selfdrive/monitoring/helpers.py +++ b/selfdrive/monitoring/helpers.py @@ -9,6 +9,7 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.common.stat_live import RunningStatFilter from openpilot.common.transformations.camera import DEVICE_CAMERAS +from openpilot.system.hardware import HARDWARE EventName = log.OnroadEvent.EventName @@ -34,7 +35,10 @@ class DRIVER_MONITOR_SETTINGS: self._SG_THRESHOLD = 0.9 self._BLINK_THRESHOLD = 0.865 - self._EE_THRESH11 = 0.4 + if HARDWARE.get_device_type() == 'mici': + self._EE_THRESH11 = 0.75 + else: + self._EE_THRESH11 = 0.4 self._EE_THRESH12 = 15.0 self._EE_MAX_OFFSET1 = 0.06 self._EE_MIN_OFFSET1 = 0.025 From 36e53c7394730be54c8e2e28c811d8e16d5e54ea Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 4 Nov 2025 19:18:16 -0800 Subject: [PATCH 312/341] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 615009cf0f..1c44035563 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 615009cf0f8fb8f3feadac160fbb0a07e4de171b +Subproject commit 1c440355633370320268ba6a3ca003dc34eb6697 From 0a44b48e216067212f578f94a4a082455c461b67 Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Wed, 5 Nov 2025 13:35:56 -0500 Subject: [PATCH 313/341] gitignore: add raylib test UI screenshots report path (#36570) ui: update .gitignore to include raylib_report --- selfdrive/ui/tests/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/.gitignore b/selfdrive/ui/tests/.gitignore index 91898ac59a..d926a7ae86 100644 --- a/selfdrive/ui/tests/.gitignore +++ b/selfdrive/ui/tests/.gitignore @@ -1,3 +1,4 @@ test test_translations -test_ui/report_1 \ No newline at end of file +test_ui/report_1 +test_ui/raylib_report From ee8970dc4289b2b2fc6c6d78d1fa30e64d02d372 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 5 Nov 2025 16:23:33 -0800 Subject: [PATCH 314/341] ui: add route-based profiler (#36576) * ui: add route-based profiler * cleanup * this is stupid --- .gitignore | 1 + selfdrive/ui/tests/profile_onroad.py | 112 +++++++++++++++++++++++++++ tools/lib/tests/test_logreader.py | 1 + 3 files changed, 114 insertions(+) create mode 100755 selfdrive/ui/tests/profile_onroad.py diff --git a/.gitignore b/.gitignore index b700df0c88..e4992a3d05 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ a.out *.vcd *.mo *_pyx.cpp +*.stats config.json clcache compile_commands.json diff --git a/selfdrive/ui/tests/profile_onroad.py b/selfdrive/ui/tests/profile_onroad.py new file mode 100755 index 0000000000..0294125ceb --- /dev/null +++ b/selfdrive/ui/tests/profile_onroad.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +import os +import time +import cProfile +import pyray as rl +import numpy as np + +from msgq.visionipc import VisionIpcServer, VisionStreamType +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.layouts.main import MainLayout +from openpilot.system.ui.lib.application import gui_app +from openpilot.tools.lib.logreader import LogReader +from openpilot.tools.plotjuggler.juggle import DEMO_ROUTE + +FPS = 60 + + +def chunk_messages_by_time(messages): + dt_ns = 1e9 / FPS + chunks = [] + current_services = {} + next_time = messages[0].logMonoTime + dt_ns if messages else 0 + + for msg in messages: + if msg.logMonoTime >= next_time: + chunks.append(current_services) + current_services = {} + next_time += dt_ns * ((msg.logMonoTime - next_time) // dt_ns + 1) + current_services[msg.which()] = msg + + if current_services: + chunks.append(current_services) + return chunks + + +def patch_submaster(message_chunks): + def mock_update(timeout=None): + sm = ui_state.sm + sm.updated = dict.fromkeys(sm.services, False) + current_time = time.monotonic() + for service, msg in message_chunks[sm.frame].items(): + if service in sm.data: + sm.seen[service] = True + sm.updated[service] = True + + msg_builder = msg.as_builder() + sm.data[service] = getattr(msg_builder, service) + sm.logMonoTime[service] = msg.logMonoTime + sm.recv_time[service] = current_time + sm.recv_frame[service] = sm.frame + sm.valid[service] = True + sm.frame += 1 + ui_state.sm.update = mock_update + + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser(description='Profile openpilot UI rendering and state updates') + parser.add_argument('route', type=str, nargs='?', default=DEMO_ROUTE + "/1", + help='Route to use for profiling') + parser.add_argument('--loop', type=int, default=1, + help='Number of times to loop the log (default: 1)') + parser.add_argument('--output', type=str, default='cachegrind.out.ui', + help='Output file prefix (default: cachegrind.out.ui)') + parser.add_argument('--max-seconds', type=float, default=None, + help='Maximum seconds of messages to process (default: all)') + parser.add_argument('--headless', action='store_true', + help='Run in headless mode without GPU (for CI/testing)') + args = parser.parse_args() + + print(f"Loading log from {args.route}...") + lr = LogReader(args.route, sort_by_time=True) + messages = list(lr) * args.loop + + print("Chunking messages...") + message_chunks = chunk_messages_by_time(messages) + if args.max_seconds: + message_chunks = message_chunks[:int(args.max_seconds * FPS)] + + print("Initializing UI with GPU rendering...") + + if args.headless: + os.environ['SDL_VIDEODRIVER'] = 'dummy' + + gui_app.init_window("UI Profiling") + main_layout = MainLayout() + main_layout.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + + print("Running...") + patch_submaster(message_chunks) + + W, H = 1928, 1208 + vipc = VisionIpcServer("camerad") + vipc.create_buffers(VisionStreamType.VISION_STREAM_ROAD, 5, 1928, 1208) + vipc.start_listener() + yuv_buffer_size = W * H + (W // 2) * (H // 2) * 2 + yuv_data = np.random.randint(0, 256, yuv_buffer_size, dtype=np.uint8).tobytes() + with cProfile.Profile() as pr: + for should_render in gui_app.render(): + if ui_state.sm.frame >= len(message_chunks): + break + if ui_state.sm.frame % 3 == 0: + eof = int((ui_state.sm.frame % 3) * 0.05 * 1e9) + vipc.send(VisionStreamType.VISION_STREAM_ROAD, yuv_data, ui_state.sm.frame % 3, eof, eof) + ui_state.update() + if should_render: + main_layout.render() + pr.dump_stats(f'{args.output}_deterministic.stats') + + rl.close_window() + print("\nProfiling complete!") + print(f" run: python -m pstats {args.output}_deterministic.stats") diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 8d0870171f..ee75a8b1ce 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -72,6 +72,7 @@ class TestLogReader: (f"https://useradmin.comma.ai/?onebox={TEST_ROUTE.replace('/', '|')}", ALL_SEGS), (f"https://useradmin.comma.ai/?onebox={TEST_ROUTE.replace('/', '%7C')}", ALL_SEGS), ]) + @pytest.mark.skip("this got flaky. internet tests are stupid.") def test_indirect_parsing(self, identifier, expected): parsed = parse_indirect(identifier) sr = SegmentRange(parsed) From dc5f5eaf65186ecc447dc192363aeebd5be73ef8 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 5 Nov 2025 16:34:19 -0800 Subject: [PATCH 315/341] make github LFS work if you want it --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 648afdae57..094503cfa7 100644 --- a/SConstruct +++ b/SConstruct @@ -22,7 +22,7 @@ AddOption('--ccflags', action='store', type='string', default='', help='pass arb AddOption('--minimal', action='store_false', dest='extras', - default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS) + default=os.path.exists(File('#.gitattributes').abspath), # minimal by default on release branch (where there's no LFS) help='the minimum build to run openpilot. no tests, tools, etc.') # Detect platform From 89919c8832df73904a873c0205c44faae85e7af7 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 5 Nov 2025 18:55:50 -0800 Subject: [PATCH 316/341] this is not a good api --- system/ubloxd/ubloxd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ubloxd/ubloxd.py b/system/ubloxd/ubloxd.py index 84a926dd78..6882ad0955 100755 --- a/system/ubloxd/ubloxd.py +++ b/system/ubloxd/ubloxd.py @@ -498,7 +498,7 @@ def main(): sock = messaging.sub_sock('ubloxRaw', timeout=100, conflate=False) while True: - msg = messaging.recv_one_or_none(sock) + msg = messaging.recv_one(sock) if msg is None: continue From 2d31b422c84b1ebba9e7aa5eedc228bb7dd41593 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 5 Nov 2025 23:01:10 -0800 Subject: [PATCH 317/341] ui: prep for 60fps (#36585) --- .gitmodules | 2 +- launch_env.sh | 9 +++++++++ selfdrive/ui/ui.py | 4 ++-- system/hardware/tici/hardware.py | 3 ++- tinygrad_repo | 2 +- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index ad6530de9a..54c7393986 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,4 +15,4 @@ url = ../../commaai/teleoprtc [submodule "tinygrad"] path = tinygrad_repo - url = https://github.com/tinygrad/tinygrad.git + url = https://github.com/commaai/tinygrad.git diff --git a/launch_env.sh b/launch_env.sh index 3715ad2486..a7ac013c51 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -6,6 +6,15 @@ export NUMEXPR_NUM_THREADS=1 export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 +# models get lower priority than ui +# - ui is ~5ms +# - modeld is 20ms +# - DM is 10ms +# in order to run ui at 60fps (16.67ms), we need to allow +# it to preempt the model workloads. we have enough +# headroom for this until ui is moved to the CPU. +export QCOM_PRIORITY=12 + if [ -z "$AGNOS_VERSION" ]; then export AGNOS_VERSION="14.5" fi diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index 222a25b87b..33d05f89e8 100755 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -9,8 +9,8 @@ from openpilot.selfdrive.ui.ui_state import ui_state def main(): - cores = {7, } - config_realtime_process(0, 1) + cores = {5, } + config_realtime_process(0, 51) gui_app.init_window("UI") main_layout = MainLayout() diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index ff2622e963..9791f15f34 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -424,7 +424,8 @@ class Tici(HardwareBase): # *** GPU config *** # https://github.com/commaai/agnos-kernel-sdm845/blob/master/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi#L216 - affine_irq(7, "msm_drm") # display + affine_irq(5, "fts_ts") # touch + affine_irq(5, "msm_drm") # display sudo_write("1", "/sys/class/kgsl/kgsl-3d0/min_pwrlevel") sudo_write("1", "/sys/class/kgsl/kgsl-3d0/max_pwrlevel") sudo_write("1", "/sys/class/kgsl/kgsl-3d0/force_bus_on") diff --git a/tinygrad_repo b/tinygrad_repo index a6fd96f620..7296c74cbd 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit a6fd96f62050efd4a2fe7c885d1a11f87e3c5b0a +Subproject commit 7296c74cbd2666da7dce95d7ca6dab5340653a5c From 10100e34e191d32c70eba513529175fec80a9ed9 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 6 Nov 2025 01:04:17 -0800 Subject: [PATCH 318/341] bump raylib --- third_party/raylib/build.sh | 2 +- third_party/raylib/larch64/libraylib.a | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/third_party/raylib/build.sh b/third_party/raylib/build.sh index 0b50244482..0a9fffc1d4 100755 --- a/third_party/raylib/build.sh +++ b/third_party/raylib/build.sh @@ -30,7 +30,7 @@ fi cd raylib_repo -COMMIT=${1:-aa6ade09ac4bfb2847a356535f2d9f87e49ab089} +COMMIT=${1:-d6ced9338be75fd92b2ac72cdb87a4fb252b3a89} git fetch origin $COMMIT git reset --hard $COMMIT git clean -xdff . diff --git a/third_party/raylib/larch64/libraylib.a b/third_party/raylib/larch64/libraylib.a index 954aa0d486..e7a435b93e 100644 --- a/third_party/raylib/larch64/libraylib.a +++ b/third_party/raylib/larch64/libraylib.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bb734fb8733e1762081945f4f3ddf8d3f7379a0ea4790ee925803cd00129ccb -size 3156548 +oid sha256:873a985904830f64e90b404f560f2502ba30b8744afff98dd9972d5feb66c58b +size 2002068 From e5a7deb6ad53c24bc5dfac32259bedabf15afd4d Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 6 Nov 2025 01:22:37 -0800 Subject: [PATCH 319/341] AGNOS 14.6 (#36586) * stage * ver * prod --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++---- system/hardware/tici/all-partitions.json | 36 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index a7ac013c51..ee4d860e98 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -16,7 +16,7 @@ export VECLIB_MAXIMUM_THREADS=1 export QCOM_PRIORITY=12 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="14.5" + export AGNOS_VERSION="14.6" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index d6a6ab51c9..e664f31e87 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502.img.xz", - "hash": "6e2f82c249f6feacdfcf20679e9e4876d161753a94d4068fe26b5c41191bda24", - "hash_raw": "54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502", + "url": "https://commadist.azureedge.net/agnosupdate/system-88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf.img.xz", + "hash": "8e28707820902c887904ae86911a02caedb02a6f8780ad75e00e841fdda0b88e", + "hash_raw": "88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf", "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "f3a50de42734f747611f6df00087b3a6ec3620bed07134d327a864c1182f0df8", + "ondevice_hash": "e18c6f54c9d9dec4b00361403749517f5bb509e6bccb7c114cbc0916ed32477e", "alt": { - "hash": "54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502", - "url": "https://commadist.azureedge.net/agnosupdate/system-54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502.img", + "hash": "88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf", + "url": "https://commadist.azureedge.net/agnosupdate/system-88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf.img", "size": 4718592000 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index c51453f4c6..04a690e32d 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502.img.xz", - "hash": "6e2f82c249f6feacdfcf20679e9e4876d161753a94d4068fe26b5c41191bda24", - "hash_raw": "54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502", + "url": "https://commadist.azureedge.net/agnosupdate/system-88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf.img.xz", + "hash": "8e28707820902c887904ae86911a02caedb02a6f8780ad75e00e841fdda0b88e", + "hash_raw": "88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf", "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "f3a50de42734f747611f6df00087b3a6ec3620bed07134d327a864c1182f0df8", + "ondevice_hash": "e18c6f54c9d9dec4b00361403749517f5bb509e6bccb7c114cbc0916ed32477e", "alt": { - "hash": "54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502", - "url": "https://commadist.azureedge.net/agnosupdate/system-54ca532116840b8532ef3c5a3e9ef4b073ed8c6b66cb5e30ad299e28fff35502.img", + "hash": "88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf", + "url": "https://commadist.azureedge.net/agnosupdate/system-88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf.img", "size": 4718592000 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-7b58b8c6935e998b93fae25aa358dc808e90b032c004b7f7ec6e3a60986cd41d.img.xz", - "hash": "5115d8a8bc4ad8eebaf0e3176cc68799089c72f64504f7238f411cbaf5d18497", - "hash_raw": "7b58b8c6935e998b93fae25aa358dc808e90b032c004b7f7ec6e3a60986cd41d", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-fa4b3568883ccaab51696ef7a9e000fccd6f1f2ce0e13b7ec5c7422a251b777e.img.xz", + "hash": "f3666af71ab0c07c754871700cb64915312ddb2ef5d66223e696481a8bfeea6a", + "hash_raw": "fa4b3568883ccaab51696ef7a9e000fccd6f1f2ce0e13b7ec5c7422a251b777e", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "b0ee797f751bc776bcb161a479cfb7f339bbf92f0cb207b053797daef0ac61d8" + "ondevice_hash": "36330a8d454370db7e24a5967ec82490a214303b5b117bc2e53fa23097c44e8a" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-a5a3e2fe9a8bf83ea37aabafdd7fd5f6e274f8ef3d1fb46ce56766cec8f13215.img.xz", - "hash": "c0348854c1f93a0557c9ce85f709c8d19f1d11df0c3ba6cd4aa01ada3d2293f3", - "hash_raw": "a5a3e2fe9a8bf83ea37aabafdd7fd5f6e274f8ef3d1fb46ce56766cec8f13215", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-c6b25bd390e42e2d6ab6c18725fce059f904c2a6a36ff6dec054a38e7a930419.img.xz", + "hash": "02cdc0ce26a96c39f9b5077c53fb02afccfaae7d02a4f1ea5cbb2a3da33299f3", + "hash_raw": "c6b25bd390e42e2d6ab6c18725fce059f904c2a6a36ff6dec054a38e7a930419", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "2def7bc3fed2cdcbaf4869fd2f68ad1d62f6d89b423d44c7114faf22f449c29f" + "ondevice_hash": "1f70fe65585c2d2561819bd92bbc1255fceeacb3507b67c9d596797a06b936e0" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-b86b949d9ed1978e792fccf59c1484238890e2cb904a501db64ed96bb91df4f9.img.xz", - "hash": "e0cee51c596d77e265b3bae937e9a3b4f317d0e80c091f03ca1bfbbdadbc6536", - "hash_raw": "b86b949d9ed1978e792fccf59c1484238890e2cb904a501db64ed96bb91df4f9", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-ef5138ccb96cd0acb5d2e04d1b38fe2595c28e1beff7f610993c19f811f85d3c.img.xz", + "hash": "759a2db9bc8ee3cdc3d6a6c4fcbbaaed13daeda40d2969bbb9bf11718f24c521", + "hash_raw": "ef5138ccb96cd0acb5d2e04d1b38fe2595c28e1beff7f610993c19f811f85d3c", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "38d7e4d3105102ca5a8dedb2764cb16f6e36766bbbb9d87e557e0d727ec37a58" + "ondevice_hash": "23a7eae03e4c3c204a87d83f02017805f061f5df88e25c482525afd18b66a243" } ] \ No newline at end of file From b6bcc8cca32f9df8e63afe7c98f5d738b5383da5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 6 Nov 2025 09:42:23 -0800 Subject: [PATCH 320/341] ui: fix running on macOS --- selfdrive/ui/ui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index 33d05f89e8..a4b99825e9 100755 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -2,6 +2,7 @@ import os import pyray as rl +from openpilot.system.hardware import TICI from openpilot.common.realtime import config_realtime_process, set_core_affinity from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.main import MainLayout @@ -21,7 +22,7 @@ def main(): main_layout.render() # reaffine after power save offlines our core - if os.sched_getaffinity(0) != cores: + if TICI and os.sched_getaffinity(0) != cores: try: set_core_affinity(list(cores)) except OSError: From fb34601d5a414e832a68e4357dc5b1653f0807cb Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 6 Nov 2025 20:46:04 -0800 Subject: [PATCH 321/341] Revert "bump panda" (#36594) This reverts commit 36e53c7394730be54c8e2e28c811d8e16d5e54ea. --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 1c44035563..615009cf0f 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 1c440355633370320268ba6a3ca003dc34eb6697 +Subproject commit 615009cf0f8fb8f3feadac160fbb0a07e4de171b From 2dcb67091ff3644fee09d974d954ada148ed501c Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Thu, 6 Nov 2025 23:51:17 -0500 Subject: [PATCH 322/341] remove unused MAX_POINTS constant from model_renderer.py (#36593) --- selfdrive/ui/onroad/model_renderer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 1e4fbbb783..b9f601f8fb 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -15,8 +15,6 @@ CLIP_MARGIN = 500 MIN_DRAW_DISTANCE = 10.0 MAX_DRAW_DISTANCE = 100.0 -MAX_POINTS = 200 - THROTTLE_COLORS = [ rl.Color(13, 248, 122, 102), # HSLF(148/360, 0.94, 0.51, 0.4) rl.Color(114, 255, 92, 89), # HSLF(112/360, 1.0, 0.68, 0.35) From 16336410558feeb4799236e915611ce7fe3d1193 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 6 Nov 2025 21:00:26 -0800 Subject: [PATCH 323/341] bump raylib (#36596) this --- third_party/raylib/build.sh | 4 ++-- third_party/raylib/larch64/libraylib.a | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/third_party/raylib/build.sh b/third_party/raylib/build.sh index 0a9fffc1d4..ab6bbb977b 100755 --- a/third_party/raylib/build.sh +++ b/third_party/raylib/build.sh @@ -30,7 +30,7 @@ fi cd raylib_repo -COMMIT=${1:-d6ced9338be75fd92b2ac72cdb87a4fb252b3a89} +COMMIT=${1:-97dc6a9f1da2b5bbca6fee86b28ac79f7b28b573} git fetch origin $COMMIT git reset --hard $COMMIT git clean -xdff . @@ -57,7 +57,7 @@ if [ -f /TICI ]; then cd raylib_python_repo - BINDINGS_COMMIT="ab0191f445272ca66758f9dd345b7395518d6a77" + BINDINGS_COMMIT="a0710d95af3c12fd7f4b639589be9a13dad93cb6" git fetch origin $BINDINGS_COMMIT git reset --hard $BINDINGS_COMMIT git clean -xdff . diff --git a/third_party/raylib/larch64/libraylib.a b/third_party/raylib/larch64/libraylib.a index e7a435b93e..a0f7734d27 100644 --- a/third_party/raylib/larch64/libraylib.a +++ b/third_party/raylib/larch64/libraylib.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:873a985904830f64e90b404f560f2502ba30b8744afff98dd9972d5feb66c58b -size 2002068 +oid sha256:f97b6b4ca09ccf8a3e2cca1c8dadffadf12ffad6e25a4c91898e5bb34c8c243f +size 2001812 From 890b1cf512b9a09d47731d254338ac9456e03fb5 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 7 Nov 2025 02:54:52 -0800 Subject: [PATCH 324/341] AGNOS 14.7 (#36597) * stage * prod --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++---- system/hardware/tici/all-partitions.json | 36 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index ee4d860e98..6fc51c5b84 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -16,7 +16,7 @@ export VECLIB_MAXIMUM_THREADS=1 export QCOM_PRIORITY=12 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="14.6" + export AGNOS_VERSION="14.7" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index e664f31e87..1143f4f08a 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf.img.xz", - "hash": "8e28707820902c887904ae86911a02caedb02a6f8780ad75e00e841fdda0b88e", - "hash_raw": "88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf", + "url": "https://commadist.azureedge.net/agnosupdate/system-2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268.img.xz", + "hash": "fbc85e68a9c94fd520953c6fea64b1ec070ce12c09787de637fc0cc5cbb91846", + "hash_raw": "2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268", "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "e18c6f54c9d9dec4b00361403749517f5bb509e6bccb7c114cbc0916ed32477e", + "ondevice_hash": "dfd29ca36003da518c8f0e91476c2210c3e61f2e2d8e96c1abb8f04d0c2c62a3", "alt": { - "hash": "88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf", - "url": "https://commadist.azureedge.net/agnosupdate/system-88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf.img", + "hash": "2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268", + "url": "https://commadist.azureedge.net/agnosupdate/system-2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268.img", "size": 4718592000 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 04a690e32d..5082647a1c 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf.img.xz", - "hash": "8e28707820902c887904ae86911a02caedb02a6f8780ad75e00e841fdda0b88e", - "hash_raw": "88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf", + "url": "https://commadist.azureedge.net/agnosupdate/system-2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268.img.xz", + "hash": "fbc85e68a9c94fd520953c6fea64b1ec070ce12c09787de637fc0cc5cbb91846", + "hash_raw": "2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268", "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "e18c6f54c9d9dec4b00361403749517f5bb509e6bccb7c114cbc0916ed32477e", + "ondevice_hash": "dfd29ca36003da518c8f0e91476c2210c3e61f2e2d8e96c1abb8f04d0c2c62a3", "alt": { - "hash": "88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf", - "url": "https://commadist.azureedge.net/agnosupdate/system-88820a52e0abff08143c13c00aa3982ae00a8b5c676d131f91ff51add76ea4bf.img", + "hash": "2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268", + "url": "https://commadist.azureedge.net/agnosupdate/system-2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268.img", "size": 4718592000 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-fa4b3568883ccaab51696ef7a9e000fccd6f1f2ce0e13b7ec5c7422a251b777e.img.xz", - "hash": "f3666af71ab0c07c754871700cb64915312ddb2ef5d66223e696481a8bfeea6a", - "hash_raw": "fa4b3568883ccaab51696ef7a9e000fccd6f1f2ce0e13b7ec5c7422a251b777e", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-43fd7a1632a23a878d19f5878eb983e3fb763eb4be1aa0a1c7deb4a06bf732de.img.xz", + "hash": "67ec07f8180f666b9b5de1c8859d0f73240b88bdc97b24c6890f6d07f356c2d6", + "hash_raw": "43fd7a1632a23a878d19f5878eb983e3fb763eb4be1aa0a1c7deb4a06bf732de", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "36330a8d454370db7e24a5967ec82490a214303b5b117bc2e53fa23097c44e8a" + "ondevice_hash": "aadc8097899fcacb8037395f38bb452fa68f50d87ce31c40671fb8eff8e2eac2" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-c6b25bd390e42e2d6ab6c18725fce059f904c2a6a36ff6dec054a38e7a930419.img.xz", - "hash": "02cdc0ce26a96c39f9b5077c53fb02afccfaae7d02a4f1ea5cbb2a3da33299f3", - "hash_raw": "c6b25bd390e42e2d6ab6c18725fce059f904c2a6a36ff6dec054a38e7a930419", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-42ec6bf06ed94ff99a760d944e82f618583fa6576f80d1ca4c40cfe5949e2775.img.xz", + "hash": "e24e0a66f4de2b3bc23adc7995b78b3d547b881ede9456b2abfdcd77657bd71e", + "hash_raw": "42ec6bf06ed94ff99a760d944e82f618583fa6576f80d1ca4c40cfe5949e2775", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "1f70fe65585c2d2561819bd92bbc1255fceeacb3507b67c9d596797a06b936e0" + "ondevice_hash": "c3644225eb21fba5ff65971954b1d17986ca1b6bb44cae6729dab7d1181904bc" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-ef5138ccb96cd0acb5d2e04d1b38fe2595c28e1beff7f610993c19f811f85d3c.img.xz", - "hash": "759a2db9bc8ee3cdc3d6a6c4fcbbaaed13daeda40d2969bbb9bf11718f24c521", - "hash_raw": "ef5138ccb96cd0acb5d2e04d1b38fe2595c28e1beff7f610993c19f811f85d3c", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-8cb7da131c91753c7e53206f1ad55f0a1d9857dfb779f87e274992ba40c627eb.img.xz", + "hash": "e6990b2dfbaca7f45ab336079bcfc9d6bf198c5b05aa692277507f50f041cae8", + "hash_raw": "8cb7da131c91753c7e53206f1ad55f0a1d9857dfb779f87e274992ba40c627eb", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "23a7eae03e4c3c204a87d83f02017805f061f5df88e25c482525afd18b66a243" + "ondevice_hash": "3301335a96fcce7adbc56e85ef0f8b526480604444f2d33106cdc7c0ffe8a055" } ] \ No newline at end of file From 1262fca36b757b62f4f5d7a23ff55d1287a41c4f Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Fri, 7 Nov 2025 15:18:45 -0800 Subject: [PATCH 325/341] add check driver camera alert (#36577) * add event * missing arg * creation_delay is wrong * add logging * set offroad alert * Update selfdrive/selfdrived/alerts_offroad.json Co-authored-by: Shane Smiskol * rm onard * add details * rename to DM * log rename * no poss --------- Co-authored-by: Shane Smiskol --- cereal/log.capnp | 1 + common/params_keys.h | 1 + selfdrive/monitoring/helpers.py | 27 ++++++++++++++++++++++-- selfdrive/monitoring/test_monitoring.py | 2 +- selfdrive/selfdrived/alerts_offroad.json | 4 ++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/cereal/log.capnp b/cereal/log.capnp index 6cd8196ae0..981cfd468f 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -2224,6 +2224,7 @@ struct DriverMonitoringState @0xb83cda094a1da284 { hiStdCount @14 :UInt32; isActiveMode @16 :Bool; isRHD @4 :Bool; + uncertainCount @19 :UInt32; isPreviewDEPRECATED @15 :Bool; rhdCheckedDEPRECATED @5 :Bool; diff --git a/common/params_keys.h b/common/params_keys.h index badc162149..8d4c8d9e4b 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -97,6 +97,7 @@ inline static std::unordered_map keys = { {"Offroad_TemperatureTooHigh", {CLEAR_ON_MANAGER_START, JSON}}, {"Offroad_UnregisteredHardware", {CLEAR_ON_MANAGER_START, JSON}}, {"Offroad_UpdateFailed", {CLEAR_ON_MANAGER_START, JSON}}, + {"Offroad_DriverMonitoringUncertain", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}}, {"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}}, {"OpenpilotEnabledToggle", {PERSISTENT, BOOL, "1"}}, {"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}}, diff --git a/selfdrive/monitoring/helpers.py b/selfdrive/monitoring/helpers.py index 83904e2fb8..02d8ff5c71 100644 --- a/selfdrive/monitoring/helpers.py +++ b/selfdrive/monitoring/helpers.py @@ -4,6 +4,7 @@ import numpy as np from cereal import car, log import cereal.messaging as messaging from openpilot.selfdrive.selfdrived.events import Events +from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.common.realtime import DT_DMON from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params @@ -57,6 +58,9 @@ class DRIVER_MONITOR_SETTINGS: self._YAW_MAX_OFFSET = 0.289 self._YAW_MIN_OFFSET = -0.0246 + self._DCAM_UNCERTAIN_ALERT_THRESHOLD = 0.1 + self._DCAM_UNCERTAIN_ALERT_COUNT = int(60 / self._DT_DMON) + self._DCAM_UNCERTAIN_RESET_COUNT = int(20 / self._DT_DMON) self._POSESTD_THRESHOLD = 0.3 self._HI_STD_FALLBACK_TIME = int(10 / self._DT_DMON) # fall back to wheel touch if model is uncertain for 10s self._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz @@ -158,6 +162,9 @@ class DriverMonitoring: self.hi_stds = 0 self.threshold_pre = self.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME self.threshold_prompt = self.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME + self.dcam_uncertain_cnt = 0 + self.dcam_uncertain_alerted = False # once per drive + self.dcam_reset_cnt = 0 self.params = Params() self.too_distracted = self.params.get_bool("DriverTooDistracted") @@ -245,7 +252,7 @@ class DriverMonitoring: return distracted_types - def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged): + def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstill): rhd_pred = driver_state.wheelOnRightProb # calibrates only when there's movement and either face detected if car_speed > self.settings._WHEELPOS_CALIB_MIN_SPEED and (driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLD or @@ -296,6 +303,16 @@ class DriverMonitoring: self.pose.yaw_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT self.ee1_calibrated = self.ee1_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT + if self.face_detected and not self.driver_distracted: + if model_std_max > self.settings._DCAM_UNCERTAIN_ALERT_THRESHOLD: + if not standstill: + self.dcam_uncertain_cnt += 1 + self.dcam_reset_cnt = 0 + else: + self.dcam_reset_cnt += 1 + if self.dcam_reset_cnt > self.settings._DCAM_UNCERTAIN_RESET_COUNT: + self.dcam_uncertain_cnt = 0 + self.is_model_uncertain = self.hi_stds > self.settings._HI_STD_FALLBACK_TIME self._set_timers(self.face_detected and not self.is_model_uncertain) if self.face_detected and not self.pose.low_std and not self.driver_distracted: @@ -372,6 +389,10 @@ class DriverMonitoring: if alert is not None: self.current_events.add(alert) + if self.dcam_uncertain_cnt > self.settings._DCAM_UNCERTAIN_ALERT_COUNT and not self.dcam_uncertain_alerted: + set_offroad_alert("Offroad_DriverMonitoringUncertain", True) + self.dcam_uncertain_alerted = True + def get_state_packet(self, valid=True): # build driverMonitoringState packet @@ -393,6 +414,7 @@ class DriverMonitoring: "hiStdCount": self.hi_stds, "isActiveMode": self.active_monitoring_mode, "isRHD": self.wheel_on_right, + "uncertainCount": self.dcam_uncertain_cnt, } return dat @@ -408,7 +430,8 @@ class DriverMonitoring: driver_state=sm['driverStateV2'], cal_rpy=sm['liveCalibration'].rpyCalib, car_speed=sm['carState'].vEgo, - op_engaged=sm['selfdriveState'].enabled + op_engaged=sm['selfdriveState'].enabled, + standstill=sm['carState'].standstill, ) # Update distraction events diff --git a/selfdrive/monitoring/test_monitoring.py b/selfdrive/monitoring/test_monitoring.py index 1cc7101880..1f8babe029 100644 --- a/selfdrive/monitoring/test_monitoring.py +++ b/selfdrive/monitoring/test_monitoring.py @@ -53,7 +53,7 @@ class TestMonitoring: DM = DriverMonitoring() events = [] for idx in range(len(msgs)): - DM._update_states(msgs[idx], [0, 0, 0], 0, engaged[idx]) + DM._update_states(msgs[idx], [0, 0, 0], 0, engaged[idx], standstill[idx]) # cal_rpy and car_speed don't matter here # evaluate events at 10Hz for tests diff --git a/selfdrive/selfdrived/alerts_offroad.json b/selfdrive/selfdrived/alerts_offroad.json index 3d97135f60..b52dfa4d88 100644 --- a/selfdrive/selfdrived/alerts_offroad.json +++ b/selfdrive/selfdrived/alerts_offroad.json @@ -37,6 +37,10 @@ "text": "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.", "severity": 0 }, + "Offroad_DriverMonitoringUncertain": { + "text": "openpilot detected poor visibility for driver monitoring. Ensure the device has a clear view of the driver. This can be checked using Settings -> Device -> Driver Camera Preview. Extreme lighting conditions and/or unconventional mounting positions may also trigger this alert.", + "severity": 0 + }, "Offroad_ExcessiveActuation": { "text": "openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting.", "severity": 1, From d4185a5d571583f02a2683ff635a964313ea9a25 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Fri, 7 Nov 2025 17:37:40 -0800 Subject: [PATCH 326/341] docs: car porting (#36590) * checkpoint * door states, notes * updates * not worth it yet * wordsmith * more * more reverse engineering script content * Revise stationary ignition-only test steps Updated the steps for stationary ignition-only tests to include closing the driver's door and fastening the seatbelt before pressing the accelerator and brake pedals. * fix numbering --- docs/car-porting/car-state-signals.md | 65 +++++++++++++++++++ docs/car-porting/reverse-engineering.md | 85 +++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 docs/car-porting/car-state-signals.md create mode 100644 docs/car-porting/reverse-engineering.md diff --git a/docs/car-porting/car-state-signals.md b/docs/car-porting/car-state-signals.md new file mode 100644 index 0000000000..669bd0ee23 --- /dev/null +++ b/docs/car-porting/car-state-signals.md @@ -0,0 +1,65 @@ +# CarState signals + +## Required for basic lateral control + +* `brakePressed` +* `cruiseState` +* `doorOpen` +* `espDisabled` +* `gasPressed` +* `gearShifter` +* `leftBlinker` / `rightBlinker` +* `seatbeltUnlatched` +* `standstill` +* `steeringAngleDeg` +* `steeringPressed` +* `steeringTorque` +* `steerFaultPermanent` +* `steerFaultTemporary` +* `vCruise` +* `wheelSpeeds.[fl|fr|rl|rr]`: Speed of each of the car's four wheels, in m/s. The car's CAN bus often broadcasts the +speed in kph, so the helper function `parse_wheel_speeds` performs this conversion by default. + +## Recommended / Required for openpilot longitudinal control + +* `accFaulted` +* `espActive` +* `parkingBrake` + +## Application Dependent + +* `blockPcmEnable` +* `buttonEnable` +* `brakeHoldActive` +* `carFaultedNonCritical` +* `invalidLkasSetting` +* `lowSpeedAlert` +* `regenBraking` +* `steeringAngleOffsetDeg` +* `steeringDisengage` +* `steeringTorqueEps` +* `stockLkas` +* `vCruiseCluster` +* `vEgoCluster` +* `vehicleSensorsInvalid` + +## Automatically populated + +* `buttonEvents` + +These values are populated automatically by `parse_wheel_speeds`: + +* `aEgo`: Acceleration of the ego vehicle, Kalman filtered derivative of `vEgo`. +* `vEgo`: Speed of the ego vehicle, Kalman filtered from `vEgoRaw`. +* `vEgoRaw`: Speed of the ego vehicle, based on the average of all four wheel speeds, unfiltered. + +## Optional + +* `brake` +* `charging` +* `fuelGauge` +* `leftBlindspot` / `rightBlindspot` +* `steeringRateDeg` +* `stockAeb` +* `stockFcw` +* `yawRate` diff --git a/docs/car-porting/reverse-engineering.md b/docs/car-porting/reverse-engineering.md new file mode 100644 index 0000000000..128ec8e776 --- /dev/null +++ b/docs/car-porting/reverse-engineering.md @@ -0,0 +1,85 @@ +# Stimulus-Response Tests + +These are example test drives that can help identify the CAN bus messaging necessary for ADAS control. Each scripted +test should be done in a separate route (ignition cycle). These tests are a guide, not necessarily exhaustive. + +While testing, constant power to the comma device is highly recommended, using [comma power](https://comma.ai/shop/comma-power) if +necessary to make sure all test activity is fully captured and for ease of uploading. If constant power isn't +available, keep the ignition on for at least one minute after your test to make sure power loss doesn't result +in loss of the last minute of testing data. + +## Stationary ignition-only tests, part 1 + +1. Ignition on, but don't start engine, remain in Park +2. Open and close each door in a defined order: driver, passenger, rear left, rear right +3. Re-enter the vehicle, close the driver's door, and fasten the driver's seatbelt +4. Slowly press and release the accelerator pedal 3 times +5. Slowly press and release the brake pedal 3 times +6. Hold the brake and move the gearshift to reverse, then neutral, then drive, then sport/eco/etc if applicable +7. Return to Park, ignition off + +Brake-pressed information may show up in several messages and signals, both as on/off states and as a percentage or +pressure. It may reflect a switch on the driver's brake pedal, or a pressure-threshold state, or signals to turn on +the rear brake lights. Start by identifying all the potential signals, and confirm while driving with ACC later. + +Locate signals for all four door states if possible, but some cars only expose the driver's door state on the ADAS bus. +Driver/passenger door signals may or may not change positions for LHD vs RHD cars. For cars where only the driver's +door signal is available, the same signal may follow the driver. + +## Stationary ignition-only tests, part 2 + +1. Ignition on, but don't start engine, remain in Park +2. Press each ACC button in a defined order: main switch on/off, set, resume, cancel, accel, decel, gap adjust +3. Set the left turn signal for about five seconds +4. Operate the left turn signal one time in its touch-to-pass mode +5. Set the right turn signal for about five seconds +6. Operate the right turn signal one time in its touch-to-pass mode +7. Set the hazard / emergency indicator switch for about five seconds +8. Ignition off + +Your vehicle may have a momentary-press main ACC switch or a physical toggle that remains set. Actual ACC engagement +isn't necessary for purposes of detecting the ACC button presses. + +## Steering angle and steering torque tests + +Power steering should be available. On ICE cars, engine RPM may be present. + +1. Ignition on, start engine if applicable, remain in Park +2. Rotate the steering wheel as follows, with a few seconds pause between each step + * Start as close to exact center as possible + * Turn to 45 degrees right and hold + * Turn to 90 degrees right and hold + * Turn to 180 degrees right and hold + * Turn to full lock right and hold, with firm pressure against lock + * Release the wheel and allow it to bounce back slightly from lock + * Turn to 180 degrees left and hold + * Return to center and release +3. Ignition off + +Performing the full test to the right, followed by an abbreviated test to the left, helps give additional confirmation +of signal scale, and sign/direction for both the steering wheel angle and driver input torque signals. + +## Low speed / parking lot driving tests + +Before this test, drive to a place like an empty parking lot where you are free to drive in a series of curves. + +1. Ignition on, start engine if applicable, prepare to drive +2. Slowly (10-20mph at most) drive a figure-8 if possible, or at least one sharp left and one sharp right. +3. Come to a complete stop +4. When and where safe, drive in reverse for a short distance (10-15 feet) +5. Park the car in a safe place, ignition off + +## High speed / highway driving tests + +Select a place and time where you can safely set cruise control at normal travel speeds with little interference from +traffic ahead, and safely test the response of your factory lane guidance system. + +1. Ignition on, start engine if applicable, prepare to drive +2. When safely able, engage adaptive cruise control below 50 mph +3. When safely able, use the ACC buttons to accelerate to 50mph, then 55mph, then 60mph +4. Disengage adaptive cruise +5. When safely able, allow your factory lane guidance to prevent lane departures, 2-3 times on both the left and right + +The series of setpoints can be adjusted to local traffic regulations, and of course metric units. The specific cruise +setpoints are useful for locating the ACC HUD signals later, and confirming their precise scaling. When the car reaches +and holds the setpoint, that can also provide additional confirmation of wheel speed scaling. From 8fceb9d957e7c16045a0ed655862ec3be485df19 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 11 Nov 2025 05:58:23 +0800 Subject: [PATCH 327/341] cabana: replace deprecated Qt and OpenSSL functions (#36605) replace deprecated functions --- tools/cabana/SConscript | 1 - tools/cabana/chart/chart.cc | 8 +++++--- tools/cabana/signalview.cc | 12 ++++++------ tools/cabana/utils/api.cc | 37 +++++++++++++++++++++++-------------- tools/cabana/videowidget.cc | 2 +- 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index e3674452d5..d4cfc67280 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -50,7 +50,6 @@ qt_flags = [ "-DQT_MESSAGELOGCONTEXT", ] qt_env['CXXFLAGS'] += qt_flags -qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] qt_env['LIBPATH'] += ['#selfdrive/ui', ] qt_env['LIBS'] = qt_libs diff --git a/tools/cabana/chart/chart.cc b/tools/cabana/chart/chart.cc index 505b2b8053..bc2380e550 100644 --- a/tools/cabana/chart/chart.cc +++ b/tools/cabana/chart/chart.cc @@ -324,7 +324,8 @@ void ChartView::updateSeries(const cabana::Signal *sig, const MessageEventsMap * if (!can->liveStreaming()) { s.segment_tree.build(s.vals); } - s.series->replace(QVector::fromStdVector(series_type == SeriesType::StepLine ? s.step_vals : s.vals)); + const auto &points = series_type == SeriesType::StepLine ? s.step_vals : s.vals; + s.series->replace(QVector(points.cbegin(), points.cend())); } } updateAxisY(); @@ -382,7 +383,7 @@ void ChartView::updateAxisY() { QFontMetrics fm(axis_y->labelsFont()); for (int i = 0; i < tick_count; i++) { qreal value = min_y + (i * (max_y - min_y) / (tick_count - 1)); - max_label_width = std::max(max_label_width, fm.width(QString::number(value, 'f', n))); + max_label_width = std::max(max_label_width, fm.horizontalAdvance(QString::number(value, 'f', n))); } int title_spacing = unit.isEmpty() ? 0 : QFontMetrics(axis_y->titleFont()).size(Qt::TextSingleLine, unit).height(); @@ -838,7 +839,8 @@ void ChartView::setSeriesType(SeriesType type) { } for (auto &s : sigs) { s.series = createSeries(series_type, s.sig->color); - s.series->replace(QVector::fromStdVector(series_type == SeriesType::StepLine ? s.step_vals : s.vals)); + const auto &points = series_type == SeriesType::StepLine ? s.step_vals : s.vals; + s.series->replace(QVector(points.cbegin(), points.cend())); } updateSeriesPoints(); updateTitle(); diff --git a/tools/cabana/signalview.cc b/tools/cabana/signalview.cc index 35537d75e3..0a0c07a155 100644 --- a/tools/cabana/signalview.cc +++ b/tools/cabana/signalview.cc @@ -265,7 +265,7 @@ QSize SignalItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo text += item->sig->type == cabana::Signal::Type::Multiplexor ? QString(" M ") : QString(" m%1 ").arg(item->sig->multiplex_value); spacing += (option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1) * 2; } - width = std::min(option.widget->size().width() / 3.0, option.fontMetrics.width(text) + spacing); + width = std::min(option.widget->size().width() / 3.0, option.fontMetrics.horizontalAdvance(text) + spacing); } return {width, option.fontMetrics.height() + option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin) * 2}; } @@ -308,7 +308,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op // multiplexer indicator if (item->sig->type != cabana::Signal::Type::Normal) { QString indicator = item->sig->type == cabana::Signal::Type::Multiplexor ? QString(" M ") : QString(" m%1 ").arg(item->sig->multiplex_value); - QRect indicator_rect{rect.x(), rect.y(), option.fontMetrics.width(indicator), rect.height()}; + QRect indicator_rect{rect.x(), rect.y(), option.fontMetrics.horizontalAdvance(indicator), rect.height()}; painter->setBrush(Qt::gray); painter->setPen(Qt::NoPen); painter->drawRoundedRect(indicator_rect, 3, 3); @@ -342,13 +342,13 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, max); painter->drawText(rect, Qt::AlignLeft | Qt::AlignBottom, min); QFontMetrics fm(minmax_font); - value_adjust = std::max(fm.width(min), fm.width(max)) + 5; + value_adjust = std::max(fm.horizontalAdvance(min), fm.horizontalAdvance(max)) + 5; } else if (!item->sparkline.isEmpty() && item->sig->type == cabana::Signal::Type::Multiplexed) { // display freq of multiplexed signal painter->setFont(label_font); QString freq = QString("%1 hz").arg(item->sparkline.freq(), 0, 'g', 2); painter->drawText(rect.adjusted(5, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter, freq); - value_adjust = QFontMetrics(label_font).width(freq) + 10; + value_adjust = QFontMetrics(label_font).horizontalAdvance(freq) + 10; } // signal value painter->setFont(option.font); @@ -622,13 +622,13 @@ void SignalView::updateState(const std::set *msgs) { double value = 0; if (item->sig->getValue(last_msg.dat.data(), last_msg.dat.size(), &value)) { item->sig_val = item->sig->formatValue(value); - max_value_width = std::max(max_value_width, fontMetrics().width(item->sig_val)); + max_value_width = std::max(max_value_width, fontMetrics().horizontalAdvance(item->sig_val)); } } auto [first_visible, last_visible] = visibleSignalRange(); if (first_visible.isValid() && last_visible.isValid()) { - const static int min_max_width = QFontMetrics(delegate->minmax_font).width("-000.00") + 5; + const static int min_max_width = QFontMetrics(delegate->minmax_font).horizontalAdvance("-000.00") + 5; int available_width = value_column_width - delegate->button_size.width(); int value_width = std::min(max_value_width + min_max_width, available_width / 2); QSize size(available_width - value_width, diff --git a/tools/cabana/utils/api.cc b/tools/cabana/utils/api.cc index 2ed461fdf6..89379a8434 100644 --- a/tools/cabana/utils/api.cc +++ b/tools/cabana/utils/api.cc @@ -1,7 +1,7 @@ #include "tools/cabana/utils/api.h" +#include #include -#include #include #include @@ -39,29 +39,38 @@ std::optional getDongleId() { namespace CommaApi { -RSA *get_rsa_private_key() { - static std::unique_ptr rsa_private(nullptr, RSA_free); - if (!rsa_private) { +EVP_PKEY *get_private_key() { + static std::unique_ptr pkey(nullptr, EVP_PKEY_free); + if (!pkey) { FILE *fp = fopen(Path::rsa_file().c_str(), "rb"); if (!fp) { - qDebug() << "No RSA private key found, please run manager.py or registration.py"; + qDebug() << "No private key found, please run manager.py or registration.py"; return nullptr; } - rsa_private.reset(PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL)); + pkey.reset(PEM_read_PrivateKey(fp, nullptr, nullptr, nullptr)); fclose(fp); } - return rsa_private.get(); + return pkey.get(); } QByteArray rsa_sign(const QByteArray &data) { - RSA *rsa_private = get_rsa_private_key(); - if (!rsa_private) return {}; + EVP_PKEY *pkey = get_private_key(); + if (!pkey) return {}; - QByteArray sig(RSA_size(rsa_private), Qt::Uninitialized); - unsigned int sig_len; - int ret = RSA_sign(NID_sha256, (unsigned char*)data.data(), data.size(), (unsigned char*)sig.data(), &sig_len, rsa_private); - assert(ret == 1); - assert(sig.size() == sig_len); + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + if (!mdctx) return {}; + + QByteArray sig(EVP_PKEY_size(pkey), Qt::Uninitialized); + size_t sig_len = sig.size(); + + int ret = EVP_DigestSignInit(mdctx, nullptr, EVP_sha256(), nullptr, pkey); + ret &= EVP_DigestSignUpdate(mdctx, data.data(), data.size()); + ret &= EVP_DigestSignFinal(mdctx, (unsigned char*)sig.data(), &sig_len); + + EVP_MD_CTX_free(mdctx); + + if (ret != 1) return {}; + sig.resize(sig_len); return sig; } diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index c857c9ffbe..55018f28f0 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -134,7 +134,7 @@ void VideoWidget::createSpeedDropdown(QToolBar *toolbar) { QFont font = speed_btn->font(); font.setBold(true); speed_btn->setFont(font); - speed_btn->setMinimumWidth(speed_btn->fontMetrics().width("0.05x ") + style()->pixelMetric(QStyle::PM_MenuButtonIndicator)); + speed_btn->setMinimumWidth(speed_btn->fontMetrics().horizontalAdvance("0.05x ") + style()->pixelMetric(QStyle::PM_MenuButtonIndicator)); } QWidget *VideoWidget::createCameraWidget() { From 3099f4f12da7576d8dd4f7689bbaa62a69c5c8e3 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 11 Nov 2025 10:05:55 +0800 Subject: [PATCH 328/341] ui: cache the version text to avoid redundant Params.get calls every frame (#36601) cache the version text to avoid redundant Params.get calls every frame --- selfdrive/ui/layouts/home.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 34a7558cdc..7f477d4241 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -45,6 +45,7 @@ class HomeLayout(Widget): self.update_available = False self.alert_count = 0 + self._version_text = "" self._prev_update_available = False self._prev_alerts_present = False @@ -178,7 +179,7 @@ class HomeLayout(Widget): version_rect = rl.Rectangle(self.header_rect.x + self.header_rect.width - version_text_width, self.header_rect.y, version_text_width, self.header_rect.height) - gui_label(version_rect, self._get_version_text(), 48, rl.WHITE, alignment=rl.GuiTextAlignment.TEXT_ALIGN_RIGHT) + gui_label(version_rect, self._version_text, 48, rl.WHITE, alignment=rl.GuiTextAlignment.TEXT_ALIGN_RIGHT) def _render_home_content(self): self._render_left_column() @@ -209,7 +210,7 @@ class HomeLayout(Widget): self._setup_widget.render(setup_rect) def _refresh(self): - # TODO: implement _update_state with a timer + self._version_text = self._get_version_text() update_available = self.update_alert.refresh() alert_count = self.offroad_alert.refresh() alerts_present = alert_count > 0 From ed42cfe6994ecd6ef197bb8a84e4cfc365f57b2f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 11 Nov 2025 10:08:02 +0800 Subject: [PATCH 329/341] ui: refactor GuiApplication.render into smaller helper methods (#36569) refactor render into smaller helper method --- system/ui/lib/application.py | 85 ++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index f9d7b0c9c1..3c5d3a6936 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -381,28 +381,9 @@ class GuiApplication: rl.clear_background(rl.BLACK) # Handle modal overlay rendering and input processing - if self._modal_overlay.overlay: - if hasattr(self._modal_overlay.overlay, 'render'): - result = self._modal_overlay.overlay.render(rl.Rectangle(0, 0, self.width, self.height)) - elif callable(self._modal_overlay.overlay): - result = self._modal_overlay.overlay() - else: - raise Exception - - # Send show event to Widget - if not self._modal_overlay_shown and hasattr(self._modal_overlay.overlay, 'show_event'): - self._modal_overlay.overlay.show_event() - self._modal_overlay_shown = True - - if result >= 0: - # Clear the overlay and execute the callback - original_modal = self._modal_overlay - self._modal_overlay = ModalOverlay() - if original_modal.callback is not None: - original_modal.callback(result) + if self._handle_modal_overlay(): yield False else: - self._modal_overlay_shown = False yield True if self._render_texture: @@ -417,24 +398,7 @@ class GuiApplication: rl.draw_fps(10, 10) if self._show_touches: - current_time = time.monotonic() - - for mouse_event in self._mouse_events: - if mouse_event.left_pressed: - self._mouse_history.clear() - self._mouse_history.append(MousePosWithTime(mouse_event.pos.x * self._scale, mouse_event.pos.y * self._scale, current_time)) - - # Remove old touch points that exceed the timeout - while self._mouse_history and (current_time - self._mouse_history[0].t) > TOUCH_HISTORY_TIMEOUT: - self._mouse_history.popleft() - - if self._mouse_history: - mouse_pos = self._mouse_history[-1] - rl.draw_circle(int(mouse_pos.x), int(mouse_pos.y), 15, rl.RED) - for idx, mouse_pos in enumerate(self._mouse_history): - perc = idx / len(self._mouse_history) - color = rl.Color(min(int(255 * (1.5 - perc)), 255), int(min(255 * (perc + 0.5), 255)), 50, 255) - rl.draw_circle(int(mouse_pos.x), int(mouse_pos.y), 5, color) + self._draw_touch_points() rl.end_drawing() self._monitor_fps() @@ -456,6 +420,31 @@ class GuiApplication: def height(self): return self._height + def _handle_modal_overlay(self) -> bool: + if self._modal_overlay.overlay: + if hasattr(self._modal_overlay.overlay, 'render'): + result = self._modal_overlay.overlay.render(rl.Rectangle(0, 0, self.width, self.height)) + elif callable(self._modal_overlay.overlay): + result = self._modal_overlay.overlay() + else: + raise Exception + + # Send show event to Widget + if not self._modal_overlay_shown and hasattr(self._modal_overlay.overlay, 'show_event'): + self._modal_overlay.overlay.show_event() + self._modal_overlay_shown = True + + if result >= 0: + # Clear the overlay and execute the callback + original_modal = self._modal_overlay + self._modal_overlay = ModalOverlay() + if original_modal.callback is not None: + original_modal.callback(result) + return True + else: + self._modal_overlay_shown = False + return False + def _load_fonts(self): for font_weight_file in FontWeight: with as_file(FONT_DIR) as fspath: @@ -539,6 +528,26 @@ class GuiApplication: cloudlog.error(f"FPS dropped critically below {fps}. Shutting down UI.") os._exit(1) + def _draw_touch_points(self): + current_time = time.monotonic() + + for mouse_event in self._mouse_events: + if mouse_event.left_pressed: + self._mouse_history.clear() + self._mouse_history.append(MousePosWithTime(mouse_event.pos.x * self._scale, mouse_event.pos.y * self._scale, current_time)) + + # Remove old touch points that exceed the timeout + while self._mouse_history and (current_time - self._mouse_history[0].t) > TOUCH_HISTORY_TIMEOUT: + self._mouse_history.popleft() + + if self._mouse_history: + mouse_pos = self._mouse_history[-1] + rl.draw_circle(int(mouse_pos.x), int(mouse_pos.y), 15, rl.RED) + for idx, mouse_pos in enumerate(self._mouse_history): + perc = idx / len(self._mouse_history) + color = rl.Color(min(int(255 * (1.5 - perc)), 255), int(min(255 * (perc + 0.5), 255)), 50, 255) + rl.draw_circle(int(mouse_pos.x), int(mouse_pos.y), 5, color) + def _output_render_profile(self): import io import pstats From 85404c184b410a5cd8af73d89ba964e7c81addab Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:08:55 -1000 Subject: [PATCH 330/341] fix: badges (#36566) * re-add * need to validate * ok looks good * oops * lint --- .github/workflows/badges.yaml | 2 +- selfdrive/ui/translations/create_badges.py | 108 +++++++++++++++++++++ selfdrive/ui/update_translations.py | 1 + 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100755 selfdrive/ui/translations/create_badges.py diff --git a/.github/workflows/badges.yaml b/.github/workflows/badges.yaml index 63ee736dca..cd30e4f370 100644 --- a/.github/workflows/badges.yaml +++ b/.github/workflows/badges.yaml @@ -23,7 +23,7 @@ jobs: - uses: ./.github/workflows/setup-with-retry - name: Push badges run: | - ${{ env.RUN }} "scons -j$(nproc) && python3 selfdrive/ui/translations/create_badges.py" + ${{ env.RUN }} "python3 selfdrive/ui/translations/create_badges.py" rm .gitattributes diff --git a/selfdrive/ui/translations/create_badges.py b/selfdrive/ui/translations/create_badges.py new file mode 100755 index 0000000000..2948c4857d --- /dev/null +++ b/selfdrive/ui/translations/create_badges.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +import json +import os +import requests +import xml.etree.ElementTree as ET + +from openpilot.common.basedir import BASEDIR +from openpilot.selfdrive.ui.update_translations import LANGUAGES_FILE, TRANSLATIONS_DIR + +BADGE_HEIGHT = 20 + 8 +SHIELDS_URL = "https://img.shields.io/badge" + +def parse_po_file(file_path): + """ + Parse a .po file and count total and unfinished translations. + Returns: (total_translations, unfinished_translations) + """ + with open(file_path) as f: + content = f.read() + + total_translations = 0 + unfinished_translations = 0 + + # Split into entries (separated by blank lines) + entries = content.split('\n\n') + + for entry in entries: + # Skip header entry (contains Project-Id-Version) + if 'Project-Id-Version' in entry: + continue + + # Check if this entry has a msgid (translation entry) + # After skipping header, any entry with msgid " is a translation + # (both msgid "content" and msgid "" for multiline contain msgid ") + if 'msgid "' not in entry: + continue + + total_translations += 1 + + # Check if msgstr is empty (unfinished translation) + if 'msgstr ""' in entry: + # Check if there are continuation lines with content after msgstr "" + lines = entry.split('\n') + msgstr_idx = None + for i, line in enumerate(lines): + if line.strip().startswith('msgstr ""'): + msgstr_idx = i + break + + if msgstr_idx is not None: + # Check if any continuation lines have content + has_content = False + for line in lines[msgstr_idx + 1:]: + stripped = line.strip() + # Continuation line with content + if stripped.startswith('"') and len(stripped) > 2: + has_content = True + break + # End of entry + if stripped.startswith(('msgid', '#')) or not stripped: + break + + if not has_content: + unfinished_translations += 1 + + return (total_translations, unfinished_translations) + +if __name__ == "__main__": + with open(LANGUAGES_FILE) as f: + translation_files = json.load(f) + + badge_svg = [] + max_badge_width = 0 # keep track of max width to set parent element + for idx, (name, file) in enumerate(translation_files.items()): + po_file_path = os.path.join(str(TRANSLATIONS_DIR), f"app_{file}.po") + + total_translations, unfinished_translations = parse_po_file(po_file_path) + + percent_finished = int(100 - (unfinished_translations / total_translations * 100.)) if total_translations > 0 else 0 + color = f"rgb{(94, 188, 0) if percent_finished == 100 else (248, 255, 50) if percent_finished > 90 else (204, 55, 27)}" + + # Download badge + badge_label = f"LANGUAGE {name}" + badge_message = f"{percent_finished}% complete" + if unfinished_translations != 0: + badge_message += f" ({unfinished_translations} unfinished)" + + r = requests.get(f"{SHIELDS_URL}/{badge_label}-{badge_message}-{color}", timeout=10) + assert r.status_code == 200, "Error downloading badge" + content_svg = r.content.decode("utf-8") + + xml = ET.fromstring(content_svg) + assert "width" in xml.attrib + max_badge_width = max(max_badge_width, int(xml.attrib["width"])) + + # Make tag ids in each badge unique to combine them into one svg + for tag in ("r", "s"): + content_svg = content_svg.replace(f'id="{tag}"', f'id="{tag}{idx}"') + content_svg = content_svg.replace(f'"url(#{tag})"', f'"url(#{tag}{idx})"') + + badge_svg.extend([f'', content_svg, ""]) + + badge_svg.insert(0, '') + badge_svg.append("") + + with open(os.path.join(BASEDIR, "translation_badge.svg"), "w") as badge_f: + badge_f.write("\n".join(badge_svg)) diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py index b692552fb8..bded80b2e5 100755 --- a/selfdrive/ui/update_translations.py +++ b/selfdrive/ui/update_translations.py @@ -4,6 +4,7 @@ import os from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.multilang import SYSTEM_UI_DIR, UI_DIR, TRANSLATIONS_DIR, multilang +LANGUAGES_FILE = os.path.join(str(TRANSLATIONS_DIR), "languages.json") POT_FILE = os.path.join(str(TRANSLATIONS_DIR), "app.pot") From 124eb427581c2be9b271e0561b1601d1eebcff9c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 11 Nov 2025 10:10:50 +0800 Subject: [PATCH 331/341] ui: fix CameraView crash caused by stale frame (#36563) fix CameraView crash from stale frame --- selfdrive/ui/onroad/cameraview.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index 98dd00ebaa..87db7cc636 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -155,6 +155,8 @@ class CameraView(Widget): if self.shader and self.shader.id: rl.unload_shader(self.shader) + self.frame = None + self.available_streams.clear() self.client = None def __del__(self): @@ -191,6 +193,9 @@ class CameraView(Widget): if buffer: self._texture_needs_update = True self.frame = buffer + elif not self.client.is_connected(): + # ensure we clear the displayed frame when the connection is lost + self.frame = None if not self.frame: self._draw_placeholder(rect) From 9689de426bd0dad14e33620ce27693f4b6834371 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:38:49 -1000 Subject: [PATCH 332/341] chore: adb rules (#36544) * chore: adb rules * i think 51 is common, lets use our own --- tools/install_ubuntu_dependencies.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index fdd21067ec..5c2131d4bf 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -116,6 +116,11 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddcc", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddee", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddcc", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddee", MODE="0666" +EOF + + # Setup adb udev rules + $SUDO tee /etc/udev/rules.d/50-comma-adb.rules > /dev/null < Date: Mon, 10 Nov 2025 19:57:59 -0800 Subject: [PATCH 333/341] enable ADB in release --- selfdrive/ui/layouts/settings/developer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index cb7b0f9e91..9ea1019f54 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -108,7 +108,7 @@ class DeveloperLayout(Widget): # Hide non-release toggles on release builds # TODO: we can do an onroad cycle, but alpha long toggle requires a deinit function to re-enable radar and not fault - for item in (self._adb_toggle, self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle): + for item in (self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle): item.set_visible(not self._is_release) # CP gating From dad7bb53a2db74c1eeab64c8396c31ec63c62904 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 10 Nov 2025 22:24:01 -0800 Subject: [PATCH 334/341] ui: let ui_state set brightness --- system/ui/lib/application.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 3c5d3a6936..ddee0f41b5 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -202,9 +202,6 @@ class GuiApplication: signal.signal(signal.SIGINT, _close) atexit.register(self.close) - HARDWARE.set_display_power(True) - HARDWARE.set_screen_brightness(65) - self._set_log_callback() rl.set_trace_log_level(rl.TraceLogLevel.LOG_WARNING) From 6a257fe2defaea5f4da94414b8c6d1a220311556 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 12 Nov 2025 08:10:37 +0800 Subject: [PATCH 335/341] ui: increase profile output from 25 to 100 functions (#36607) increase profile output from 20 to 100 functions --- system/ui/lib/application.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index ddee0f41b5..af254f4b57 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -32,6 +32,7 @@ SHOW_TOUCHES = os.getenv("SHOW_TOUCHES") == "1" STRICT_MODE = os.getenv("STRICT_MODE") == "1" SCALE = float(os.getenv("SCALE", "1.0")) PROFILE_RENDER = int(os.getenv("PROFILE_RENDER", "0")) +PROFILE_STATS = int(os.getenv("PROFILE_STATS", "100")) # Number of functions to show in profile output DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE @@ -554,7 +555,7 @@ class GuiApplication: avg_frame_time = elapsed_ms / self._frame if self._frame > 0 else 0 stats_stream = io.StringIO() - pstats.Stats(self._render_profiler, stream=stats_stream).sort_stats("cumtime").print_stats(25) + pstats.Stats(self._render_profiler, stream=stats_stream).sort_stats("cumtime").print_stats(PROFILE_STATS) print("\n=== Render loop profile ===") print(stats_stream.getvalue().rstrip()) From 9ee66008dbcbc67e744db99d6c19f38282d885d5 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 11 Nov 2025 22:59:54 -0800 Subject: [PATCH 336/341] AGNOS 15 (#36611) * stage * production --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 44 +++++++-------- system/hardware/tici/all-partitions.json | 68 ++++++++++++------------ 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 6fc51c5b84..0fba08bb24 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -16,7 +16,7 @@ export VECLIB_MAXIMUM_THREADS=1 export QCOM_PRIORITY=12 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="14.7" + export AGNOS_VERSION="15" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 1143f4f08a..58c3d2a4e6 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -1,25 +1,25 @@ [ { "name": "xbl", - "url": "https://commadist.azureedge.net/agnosupdate/xbl-98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723.img.xz", - "hash": "98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723", - "hash_raw": "98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723", + "url": "https://commadist.azureedge.net/agnosupdate/xbl-dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6.img.xz", + "hash": "dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6", + "hash_raw": "dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6", "size": 3282256, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "907c705f72ebcbd3030e03da9ef4c65a3d599e056a79aa9e7c369fdff8e54dc4" + "ondevice_hash": "d47a08914d2376557b03f1231b7233508222c04b57d781f9daf77c63eab92c2e" }, { "name": "xbl_config", - "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f.img.xz", - "hash": "4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f", - "hash_raw": "4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f", + "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9.img.xz", + "hash": "1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9", + "hash_raw": "1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9", "size": 98124, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "46c472f52fb97a4836d08d0e790f5c8512651f520ce004bc3bbc6a143fc7a3c2" + "ondevice_hash": "e7d04d9f040c9c040cdf013335d0b6d6e9346311458baeb2461b193e954f5f1c" }, { "name": "abl", @@ -34,25 +34,25 @@ }, { "name": "aop", - "url": "https://commadist.azureedge.net/agnosupdate/aop-d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7.img.xz", - "hash": "d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7", - "hash_raw": "d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7", + "url": "https://commadist.azureedge.net/agnosupdate/aop-4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788.img.xz", + "hash": "4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788", + "hash_raw": "4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788", "size": 184364, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "e320da0d3f73aa09277a8be740c59f9cc605d2098b46a842c93ea2ac0ac97cb0" + "ondevice_hash": "3aa0a79149ec57f4bc8c38f7bbdf4f6630dd659e49a111ce6258d2d06a07c8e5" }, { "name": "devcfg", - "url": "https://commadist.azureedge.net/agnosupdate/devcfg-7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8.img.xz", - "hash": "7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8", - "hash_raw": "7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8", + "url": "https://commadist.azureedge.net/agnosupdate/devcfg-2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585.img.xz", + "hash": "2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585", + "hash_raw": "2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585", "size": 40336, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "7e9412d154036216e56c2346d24455dd45f56d6de4c9e8837597f22d59c83d93" + "ondevice_hash": "3d7bb33588491a2a40091a7e1cf6cb65e6dd503f69b640aba484d723f1ad47e8" }, { "name": "boot", @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268.img.xz", - "hash": "fbc85e68a9c94fd520953c6fea64b1ec070ce12c09787de637fc0cc5cbb91846", - "hash_raw": "2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268", + "url": "https://commadist.azureedge.net/agnosupdate/system-8757f4a9d2489585249970142578029ab1dfdc5851da75fd703d2376b6f2a26b.img.xz", + "hash": "e9e99988d78c7287f29ad840130f65d5a11fa2301463d5298f1072399406f889", + "hash_raw": "8757f4a9d2489585249970142578029ab1dfdc5851da75fd703d2376b6f2a26b", "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "dfd29ca36003da518c8f0e91476c2210c3e61f2e2d8e96c1abb8f04d0c2c62a3", + "ondevice_hash": "21d3726fcdd39d126c9ecf05ccc43a104c8486b929045a63bf7e3ac8a8bb7a50", "alt": { - "hash": "2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268", - "url": "https://commadist.azureedge.net/agnosupdate/system-2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268.img", + "hash": "8757f4a9d2489585249970142578029ab1dfdc5851da75fd703d2376b6f2a26b", + "url": "https://commadist.azureedge.net/agnosupdate/system-8757f4a9d2489585249970142578029ab1dfdc5851da75fd703d2376b6f2a26b.img", "size": 4718592000 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 5082647a1c..bac2dfc594 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -130,25 +130,25 @@ }, { "name": "xbl", - "url": "https://commadist.azureedge.net/agnosupdate/xbl-98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723.img.xz", - "hash": "98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723", - "hash_raw": "98e0642e2af77596e64d107b2c525a36e54d41d879268fd2fcab9255a2b29723", + "url": "https://commadist.azureedge.net/agnosupdate/xbl-dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6.img.xz", + "hash": "dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6", + "hash_raw": "dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6", "size": 3282256, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "907c705f72ebcbd3030e03da9ef4c65a3d599e056a79aa9e7c369fdff8e54dc4" + "ondevice_hash": "d47a08914d2376557b03f1231b7233508222c04b57d781f9daf77c63eab92c2e" }, { "name": "xbl_config", - "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f.img.xz", - "hash": "4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f", - "hash_raw": "4d8add65e80b3e5ca49a64fac76025ee3a57a1523abd9caa407aa8c5fb721b0f", + "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9.img.xz", + "hash": "1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9", + "hash_raw": "1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9", "size": 98124, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "46c472f52fb97a4836d08d0e790f5c8512651f520ce004bc3bbc6a143fc7a3c2" + "ondevice_hash": "e7d04d9f040c9c040cdf013335d0b6d6e9346311458baeb2461b193e954f5f1c" }, { "name": "abl", @@ -163,14 +163,14 @@ }, { "name": "aop", - "url": "https://commadist.azureedge.net/agnosupdate/aop-d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7.img.xz", - "hash": "d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7", - "hash_raw": "d8add1d4c1b6b443debf7bb80040e88a12140d248a328650d65ceaa0df04c1b7", + "url": "https://commadist.azureedge.net/agnosupdate/aop-4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788.img.xz", + "hash": "4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788", + "hash_raw": "4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788", "size": 184364, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "e320da0d3f73aa09277a8be740c59f9cc605d2098b46a842c93ea2ac0ac97cb0" + "ondevice_hash": "3aa0a79149ec57f4bc8c38f7bbdf4f6630dd659e49a111ce6258d2d06a07c8e5" }, { "name": "bluetooth", @@ -207,14 +207,14 @@ }, { "name": "devcfg", - "url": "https://commadist.azureedge.net/agnosupdate/devcfg-7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8.img.xz", - "hash": "7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8", - "hash_raw": "7e8a836cf75a9097b1c78960d36f883699fcc3858d8a1d28338f889f6af25cc8", + "url": "https://commadist.azureedge.net/agnosupdate/devcfg-2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585.img.xz", + "hash": "2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585", + "hash_raw": "2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585", "size": 40336, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "7e9412d154036216e56c2346d24455dd45f56d6de4c9e8837597f22d59c83d93" + "ondevice_hash": "3d7bb33588491a2a40091a7e1cf6cb65e6dd503f69b640aba484d723f1ad47e8" }, { "name": "devinfo", @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268.img.xz", - "hash": "fbc85e68a9c94fd520953c6fea64b1ec070ce12c09787de637fc0cc5cbb91846", - "hash_raw": "2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268", + "url": "https://commadist.azureedge.net/agnosupdate/system-8757f4a9d2489585249970142578029ab1dfdc5851da75fd703d2376b6f2a26b.img.xz", + "hash": "e9e99988d78c7287f29ad840130f65d5a11fa2301463d5298f1072399406f889", + "hash_raw": "8757f4a9d2489585249970142578029ab1dfdc5851da75fd703d2376b6f2a26b", "size": 4718592000, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "dfd29ca36003da518c8f0e91476c2210c3e61f2e2d8e96c1abb8f04d0c2c62a3", + "ondevice_hash": "21d3726fcdd39d126c9ecf05ccc43a104c8486b929045a63bf7e3ac8a8bb7a50", "alt": { - "hash": "2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268", - "url": "https://commadist.azureedge.net/agnosupdate/system-2714d2d2cad6957060c4cfa61fcaa2a3826238278e849e28fa6241c18b18a268.img", + "hash": "8757f4a9d2489585249970142578029ab1dfdc5851da75fd703d2376b6f2a26b", + "url": "https://commadist.azureedge.net/agnosupdate/system-8757f4a9d2489585249970142578029ab1dfdc5851da75fd703d2376b6f2a26b.img", "size": 4718592000 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-43fd7a1632a23a878d19f5878eb983e3fb763eb4be1aa0a1c7deb4a06bf732de.img.xz", - "hash": "67ec07f8180f666b9b5de1c8859d0f73240b88bdc97b24c6890f6d07f356c2d6", - "hash_raw": "43fd7a1632a23a878d19f5878eb983e3fb763eb4be1aa0a1c7deb4a06bf732de", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-1d461d8be17827735a28c2588bb9fcad27d4b80fba15cd2740f3a04c8f29cc90.img.xz", + "hash": "763c7366049b3c0ad71bd19abbbf5c68d2c43597d4da5dafad890507ff489899", + "hash_raw": "1d461d8be17827735a28c2588bb9fcad27d4b80fba15cd2740f3a04c8f29cc90", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "aadc8097899fcacb8037395f38bb452fa68f50d87ce31c40671fb8eff8e2eac2" + "ondevice_hash": "90a265b8756b18caf1be4b8dc9b8b3104898170104ed87ec3274f77acc6c28e3" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-42ec6bf06ed94ff99a760d944e82f618583fa6576f80d1ca4c40cfe5949e2775.img.xz", - "hash": "e24e0a66f4de2b3bc23adc7995b78b3d547b881ede9456b2abfdcd77657bd71e", - "hash_raw": "42ec6bf06ed94ff99a760d944e82f618583fa6576f80d1ca4c40cfe5949e2775", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-ec37fcfb7d707d26d5fbc64994e20cfdbb73a27eeedfe37778559824a2032a27.img.xz", + "hash": "de475b604b63fbeb1841c6564fb8eb496da46c9a9564ec73e5d7c8045fc88ebc", + "hash_raw": "ec37fcfb7d707d26d5fbc64994e20cfdbb73a27eeedfe37778559824a2032a27", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "c3644225eb21fba5ff65971954b1d17986ca1b6bb44cae6729dab7d1181904bc" + "ondevice_hash": "03f6cbddc3bfbd2d0cd316d87d488434a03095c12870c8c6fe3bc4a2946ff0ef" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-8cb7da131c91753c7e53206f1ad55f0a1d9857dfb779f87e274992ba40c627eb.img.xz", - "hash": "e6990b2dfbaca7f45ab336079bcfc9d6bf198c5b05aa692277507f50f041cae8", - "hash_raw": "8cb7da131c91753c7e53206f1ad55f0a1d9857dfb779f87e274992ba40c627eb", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-3501f34c28f0e5ffe224f192b4a3a35a00a039980ca29a5c35d31449f3e918d6.img.xz", + "hash": "5bda2cb099b14f4944b476995d84dcb943af1858a57fdd62d5920b6e7b74fb80", + "hash_raw": "3501f34c28f0e5ffe224f192b4a3a35a00a039980ca29a5c35d31449f3e918d6", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "3301335a96fcce7adbc56e85ef0f8b526480604444f2d33106cdc7c0ffe8a055" + "ondevice_hash": "d1da6f8d928093dec15590b5c1740c0062031d0068a11962bdb28dca2104d8c6" } ] \ No newline at end of file From 3d08a5048b39092ac8e25fe569d37cc04473f344 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 13 Nov 2025 06:22:14 +0800 Subject: [PATCH 337/341] replay: Only send bookmarkButton message when --all flag is set (#36612) Only send BookmarkButton message when --all flag is set --- tools/replay/main.cc | 2 +- tools/replay/replay.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/replay/main.cc b/tools/replay/main.cc index f950985075..4609012194 100644 --- a/tools/replay/main.cc +++ b/tools/replay/main.cc @@ -30,7 +30,7 @@ Options: --qcam Load qcamera --no-hw-decoder Disable HW video decoding --no-vipc Do not output video - --all Output all messages including uiDebug, userBookmark + --all Output all messages including bookmarkButton, uiDebug, userBookmark -h, --help Show this help message )"; diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index 7ecad82873..c9ab7e7e2b 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -20,7 +20,7 @@ Replay::Replay(const std::string &route, std::vector allow, std::ve std::signal(SIGUSR1, interrupt_sleep_handler); if (!(flags_ & REPLAY_FLAG_ALL_SERVICES)) { - block.insert(block.end(), {"uiDebug", "userBookmark"}); + block.insert(block.end(), {"bookmarkButton", "uiDebug", "userBookmark"}); } setupServices(allow, block); setupSegmentManager(!allow.empty() || !block.empty()); From f93b3f51c9c9d37c7d50b656ed4651028d663f12 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:04:20 -1000 Subject: [PATCH 338/341] fix: install missing x deps for building raylib from src (#36614) * fix: install missing x deps for building raylib from src * move here * cleaner --- third_party/raylib/build.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/third_party/raylib/build.sh b/third_party/raylib/build.sh index ab6bbb977b..368e27b449 100755 --- a/third_party/raylib/build.sh +++ b/third_party/raylib/build.sh @@ -1,6 +1,17 @@ #!/usr/bin/env bash set -e +SUDO="" + +# Use sudo if not root +if [[ ! $(id -u) -eq 0 ]]; then + if [[ -z $(which sudo) ]]; then + echo "Please install sudo or run as root" + exit 1 + fi + SUDO="sudo" +fi + DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" cd $DIR @@ -10,6 +21,13 @@ ARCHNAME=$(uname -m) if [ -f /TICI ]; then ARCHNAME="larch64" RAYLIB_PLATFORM="PLATFORM_COMMA" +elif [[ "$OSTYPE" == "linux"* ]]; then + # required dependencies on Linux PC + $SUDO apt install \ + libxcursor-dev \ + libxi-dev \ + libxinerama-dev \ + libxrandr-dev fi if [[ "$OSTYPE" == "darwin"* ]]; then From d72a01d7390efae4dc4b4dea30ccc921766a0b68 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 13 Nov 2025 14:58:16 -0800 Subject: [PATCH 339/341] raylib: fix texture wrapping filtering artifacts (#36618) fix wrapping artifacts --- system/ui/lib/application.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index af254f4b57..352a8cfa9a 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -312,6 +312,8 @@ class GuiApplication: texture = rl.load_texture_from_image(image) # Set texture filtering to smooth the result rl.set_texture_filter(texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) + # prevent artifacts from wrapping coordinates + rl.set_texture_wrap(texture, rl.TextureWrap.TEXTURE_WRAP_CLAMP) rl.unload_image(image) return texture From fc253fe1eec32f161bee57bd4a849fa3f5c602e2 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 13 Nov 2025 14:59:34 -0800 Subject: [PATCH 340/341] Don't resize images that are the same size --- system/ui/lib/application.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 352a8cfa9a..e9f5484a17 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -287,22 +287,25 @@ class GuiApplication: rl.image_alpha_premultiply(image) if width is not None and height is not None: + same_dimensions = image.width == width and image.height == height + # Resize with aspect ratio preservation if requested - if keep_aspect_ratio: - orig_width = image.width - orig_height = image.height + if not same_dimensions: + if keep_aspect_ratio: + orig_width = image.width + orig_height = image.height - scale_width = width / orig_width - scale_height = height / orig_height + scale_width = width / orig_width + scale_height = height / orig_height - # Calculate new dimensions - scale = min(scale_width, scale_height) - new_width = int(orig_width * scale) - new_height = int(orig_height * scale) + # Calculate new dimensions + scale = min(scale_width, scale_height) + new_width = int(orig_width * scale) + new_height = int(orig_height * scale) - rl.image_resize(image, new_width, new_height) - else: - rl.image_resize(image, width, height) + rl.image_resize(image, new_width, new_height) + else: + rl.image_resize(image, width, height) else: assert keep_aspect_ratio, "Cannot resize without specifying width and height" return image From 9c19ec84096dba2ef8b12f52e6fb3d0b7a44ccd3 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 13 Nov 2025 15:09:33 -0800 Subject: [PATCH 341/341] bump raylib --- third_party/raylib/build.sh | 2 +- third_party/raylib/larch64/libraylib.a | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/third_party/raylib/build.sh b/third_party/raylib/build.sh index 368e27b449..7f2ce5951f 100755 --- a/third_party/raylib/build.sh +++ b/third_party/raylib/build.sh @@ -48,7 +48,7 @@ fi cd raylib_repo -COMMIT=${1:-97dc6a9f1da2b5bbca6fee86b28ac79f7b28b573} +COMMIT=${1:-3425bd9d1fb292ede4d80f97a1f4f258f614cffc} git fetch origin $COMMIT git reset --hard $COMMIT git clean -xdff . diff --git a/third_party/raylib/larch64/libraylib.a b/third_party/raylib/larch64/libraylib.a index a0f7734d27..4e810c8b7b 100644 --- a/third_party/raylib/larch64/libraylib.a +++ b/third_party/raylib/larch64/libraylib.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f97b6b4ca09ccf8a3e2cca1c8dadffadf12ffad6e25a4c91898e5bb34c8c243f -size 2001812 +oid sha256:91e9a07513e84f7b553da01b34b24e12fe7130131ef73ebdb3dac3b838db815b +size 2001860