Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025

# Conflicts:
#	opendbc_repo
This commit is contained in:
Jason Wen
2026-01-26 11:45:29 -05:00
173 changed files with 5029 additions and 3155 deletions
+3 -3
View File
@@ -316,7 +316,7 @@ def upload_handler(end_event: threading.Event) -> None:
cloudlog.exception("athena.upload_handler.exception")
def _do_upload(upload_item: UploadItem, callback: Callable = None) -> requests.Response:
def _do_upload(upload_item: UploadItem, callback: Callable | None = None) -> requests.Response:
path = upload_item.path
compress = False
@@ -369,7 +369,7 @@ def getVersion() -> dict[str, str]:
@dispatcher.add_method
def setNavDestination(latitude: int = 0, longitude: int = 0, place_name: str = None, place_details: str = None) -> dict[str, int]:
def setNavDestination(latitude: int = 0, longitude: int = 0, place_name: str | None = None, place_details: str | None = None) -> dict[str, int]:
destination = {
"latitude": latitude,
"longitude": longitude,
@@ -901,7 +901,7 @@ def backoff(retries: int) -> int:
return random.randrange(0, min(128, int(2 ** retries)))
def main(exit_event: threading.Event = None):
def main(exit_event: threading.Event | None = None):
try:
set_core_affinity([0, 1, 2, 3])
except Exception:
+1 -1
View File
@@ -97,7 +97,7 @@ class TestAthenadMethods:
break
@staticmethod
def _create_file(file: str, parent: str = None, data: bytes = b'') -> str:
def _create_file(file: str, parent: str | None = None, data: bytes = b'') -> str:
fn = os.path.join(Paths.log_root() if parent is None else parent, file)
os.makedirs(os.path.dirname(fn), exist_ok=True)
with open(fn, 'wb') as f:
+1 -5
View File
@@ -26,11 +26,7 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, SpectraCamera *
LOGD("allocated %d CL buffers", frame_buf_count);
}
// the encoder HW tells us the size it wants after setting it up.
// TODO: VENUS_BUFFER_SIZE should give the size, but it's too small. dependent on encoder settings?
size_t nv12_size = (out_img_width <= 1344 ? 2900 : 2346)*cam->stride;
vipc_server->create_buffers_with_sizes(stream_type, VIPC_BUFFER_COUNT, out_img_width, out_img_height, nv12_size, cam->stride, cam->uv_offset);
vipc_server->create_buffers_with_sizes(stream_type, VIPC_BUFFER_COUNT, out_img_width, out_img_height, cam->yuv_size, cam->stride, cam->uv_offset);
LOGD("created %d YUV vipc buffers with size %dx%d", VIPC_BUFFER_COUNT, cam->stride, cam->y_height);
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <cassert>
#include <cstdint>
#include <tuple>
#include "third_party/linux/include/msm_media_info.h"
// Returns NV12 aligned (stride, y_height, uv_height, buffer_size) for the given frame dimensions.
inline std::tuple<uint32_t, uint32_t, uint32_t, uint32_t> get_nv12_info(int width, int height) {
const uint32_t stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, width);
const uint32_t y_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, height);
const uint32_t uv_height = VENUS_UV_SCANLINES(COLOR_FMT_NV12, height);
const uint32_t size = VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height);
// Sanity checks for NV12 format assumptions
assert(stride == VENUS_UV_STRIDE(COLOR_FMT_NV12, width));
assert(y_height / 2 == uv_height);
assert((stride * y_height) % 0x1000 == 0); // uv_offset must be page-aligned
return {stride, y_height, uv_height, size};
}
+21
View File
@@ -0,0 +1,21 @@
# Python version of system/camerad/cameras/nv12_info.h
# Calculations from third_party/linux/include/msm_media_info.h (VENUS_BUFFER_SIZE)
def align(val: int, alignment: int) -> int:
return ((val + alignment - 1) // alignment) * alignment
def get_nv12_info(width: int, height: int) -> tuple[int, int, int, int]:
"""Returns (stride, y_height, uv_height, buffer_size) for NV12 frame dimensions."""
stride = align(width, 128)
y_height = align(height, 32)
uv_height = align(height // 2, 16)
# VENUS_BUFFER_SIZE for NV12
y_plane = stride * y_height
uv_plane = stride * uv_height + 4096
size = y_plane + uv_plane + max(16 * 1024, 8 * stride)
size = align(size, 4096)
size += align(width, 512) * 512 # kernel padding for non-aligned frames
size = align(size, 4096)
return stride, y_height, uv_height, size
+3 -12
View File
@@ -12,11 +12,11 @@
#include "media/cam_isp_ife.h"
#include "media/cam_sensor_cmn_header.h"
#include "media/cam_sync.h"
#include "third_party/linux/include/msm_media_info.h"
#include "common/util.h"
#include "common/swaglog.h"
#include "system/camerad/cameras/ife.h"
#include "system/camerad/cameras/nv12_info.h"
#include "system/camerad/cameras/spectra.h"
#include "system/camerad/cameras/bps_blobs.h"
@@ -286,17 +286,8 @@ void SpectraCamera::camera_open(VisionIpcServer *v, cl_device_id device_id, cl_c
// size is driven by all the HW that handles frames,
// the video encoder has certain alignment requirements in this case
stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, buf.out_img_width);
y_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, buf.out_img_height);
uv_height = VENUS_UV_SCANLINES(COLOR_FMT_NV12, buf.out_img_height);
uv_offset = stride*y_height;
yuv_size = uv_offset + stride*uv_height;
if (cc.output_type != ISP_RAW_OUTPUT) {
uv_offset = ALIGNED_SIZE(uv_offset, 0x1000);
yuv_size = uv_offset + ALIGNED_SIZE(stride*uv_height, 0x1000);
}
assert(stride == VENUS_UV_STRIDE(COLOR_FMT_NV12, buf.out_img_width));
assert(y_height/2 == uv_height);
std::tie(stride, y_height, uv_height, yuv_size) = get_nv12_info(buf.out_img_width, buf.out_img_height);
uv_offset = stride * y_height;
open = true;
configISP();
+8 -2
View File
@@ -43,9 +43,15 @@ def yuv_to_rgb(y, u, v):
def extract_image(buf):
# NV12 format: Y plane followed by interleaved UV plane
# UV plane size is stride * uv_height, where uv_height = align(height/2, 16)
uv_height = ((buf.height // 2) + 15) // 16 * 16
uv_plane_size = buf.stride * uv_height
y = np.array(buf.data[:buf.uv_offset], dtype=np.uint8).reshape((-1, buf.stride))[:buf.height, :buf.width]
u = np.array(buf.data[buf.uv_offset::2], dtype=np.uint8).reshape((-1, buf.stride//2))[:buf.height//2, :buf.width//2]
v = np.array(buf.data[buf.uv_offset+1::2], dtype=np.uint8).reshape((-1, buf.stride//2))[:buf.height//2, :buf.width//2]
uv_data = buf.data[buf.uv_offset:buf.uv_offset + uv_plane_size]
u = np.array(uv_data[::2], dtype=np.uint8).reshape((-1, buf.stride//2))[:buf.height//2, :buf.width//2]
v = np.array(uv_data[1::2], dtype=np.uint8).reshape((-1, buf.stride//2))[:buf.height//2, :buf.width//2]
return yuv_to_rgb(y, u, v)
-1
View File
@@ -1 +0,0 @@
eon/rat
+24 -46
View File
@@ -5,6 +5,7 @@ from dataclasses import dataclass, fields
from cereal import log
NetworkType = log.DeviceState.NetworkType
NetworkStrength = log.DeviceState.NetworkStrength
class LPAError(RuntimeError):
pass
@@ -65,10 +66,6 @@ class ThermalConfig:
return ret
class LPABase(ABC):
@abstractmethod
def bootstrap(self) -> None:
pass
@abstractmethod
def list_profiles(self) -> list[Profile]:
pass
@@ -114,68 +111,57 @@ class HardwareBase(ABC):
def booted(self) -> bool:
return True
@abstractmethod
def reboot(self, reason=None):
pass
print("REBOOT!")
@abstractmethod
def uninstall(self):
pass
print("uninstall")
@abstractmethod
def get_os_version(self):
pass
return None
@abstractmethod
def get_device_type(self):
pass
@abstractmethod
def get_imei(self, slot) -> str:
pass
return ""
@abstractmethod
def get_serial(self):
pass
return ""
@abstractmethod
def get_network_info(self):
pass
return None
@abstractmethod
def get_network_type(self):
pass
return NetworkType.none
@abstractmethod
def get_sim_info(self):
pass
return {
'sim_id': '',
'mcc_mnc': None,
'network_type': ["Unknown"],
'sim_state': ["ABSENT"],
'data_connected': False
}
@abstractmethod
def get_sim_lpa(self) -> LPABase:
pass
raise NotImplementedError("SIM LPA not available")
@abstractmethod
def get_network_strength(self, network_type):
pass
return NetworkStrength.unknown
def get_network_metered(self, network_type) -> bool:
return network_type not in (NetworkType.none, NetworkType.wifi, NetworkType.ethernet)
@staticmethod
def set_bandwidth_limit(upload_speed_kbps: int, download_speed_kbps: int) -> None:
pass
@abstractmethod
def get_current_power_draw(self):
pass
return 0
@abstractmethod
def get_som_power_draw(self):
pass
return 0
@abstractmethod
def shutdown(self):
pass
print("SHUTDOWN!")
def get_thermal_config(self):
return ThermalConfig()
@@ -183,31 +169,24 @@ class HardwareBase(ABC):
def set_display_power(self, on: bool):
pass
@abstractmethod
def set_screen_brightness(self, percentage):
pass
@abstractmethod
def get_screen_brightness(self):
pass
return 0
@abstractmethod
def set_power_save(self, powersave_enabled):
pass
@abstractmethod
def get_gpu_usage_percent(self):
pass
return 0
def get_modem_version(self):
return None
@abstractmethod
def get_modem_temperatures(self):
pass
return []
@abstractmethod
def initialize_hardware(self):
pass
@@ -217,9 +196,8 @@ class HardwareBase(ABC):
def reboot_modem(self):
pass
@abstractmethod
def get_networks(self):
pass
return None
def has_internal_panda(self) -> bool:
return False
+1 -26
View File
@@ -3,32 +3,10 @@
import argparse
import time
from openpilot.system.hardware import HARDWARE
from openpilot.system.hardware.base import LPABase
def bootstrap(lpa: LPABase) -> None:
print('┌──────────────────────────────────────────────────────────────────────────────┐')
print('│ WARNING, PLEASE READ BEFORE PROCEEDING │')
print('│ │')
print('│ this is an irreversible operation that will remove the comma-provisioned │')
print('│ profile. │')
print('│ │')
print('│ after this operation, you must purchase a new eSIM from comma in order to │')
print('│ use the comma prime subscription again. │')
print('└──────────────────────────────────────────────────────────────────────────────┘')
print()
for severity in ('sure', '100% sure'):
print(f'are you {severity} you want to proceed? (y/N) ', end='')
confirm = input()
if confirm != 'y':
print('aborting')
exit(0)
lpa.bootstrap()
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='esim.py', description='manage eSIM profiles on your comma device', epilog='comma.ai')
parser.add_argument('--bootstrap', action='store_true', help='bootstrap the eUICC (required before downloading profiles)')
parser.add_argument('--backend', choices=['qmi', 'at'], default='qmi', help='use the specified backend, defaults to qmi')
parser.add_argument('--switch', metavar='iccid', help='switch to profile')
parser.add_argument('--delete', metavar='iccid', help='delete profile (warning: this cannot be undone)')
@@ -38,10 +16,7 @@ if __name__ == '__main__':
mutated = False
lpa = HARDWARE.get_sim_lpa()
if args.bootstrap:
bootstrap(lpa)
mutated = True
elif args.switch:
if args.switch:
lpa.switch_profile(args.switch)
mutated = True
elif args.delete:
+7 -22
View File
@@ -1,24 +1,13 @@
#!/usr/bin/env python3
import numpy as np
from abc import ABC, abstractmethod
from openpilot.common.realtime import DT_HW
from openpilot.common.swaglog import cloudlog
from openpilot.common.pid import PIDController
class BaseFanController(ABC):
@abstractmethod
def update(self, cur_temp: float, ignition: bool) -> int:
pass
class TiciFanController(BaseFanController):
def __init__(self) -> None:
super().__init__()
cloudlog.info("Setting up TICI fan handler")
class FanController:
def __init__(self, rate: int) -> None:
self.last_ignition = False
self.controller = PIDController(k_p=0, k_i=4e-3, rate=(1 / DT_HW))
self.controller = PIDController(k_p=0, k_i=4e-3, rate=rate)
def update(self, cur_temp: float, ignition: bool) -> int:
self.controller.pos_limit = 100 if ignition else 30
@@ -26,13 +15,9 @@ class TiciFanController(BaseFanController):
if ignition != self.last_ignition:
self.controller.reset()
error = cur_temp - 75
fan_pwr_out = int(self.controller.update(
error=error,
feedforward=np.interp(cur_temp, [60.0, 100.0], [0, 100])
))
self.last_ignition = ignition
return fan_pwr_out
return int(self.controller.update(
error=(cur_temp - 75), # temperature setpoint in C
feedforward=np.interp(cur_temp, [60.0, 100.0], [0, 100])
))
+3 -10
View File
@@ -22,7 +22,7 @@ from openpilot.system.loggerd.config import get_available_percent
from openpilot.system.statsd import statlog
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware.power_monitoring import PowerMonitoring
from openpilot.system.hardware.fan_controller import TiciFanController
from openpilot.system.hardware.fan_controller import FanController
from openpilot.system.version import terms_version, training_version, get_build_metadata, terms_version_sp
ThermalStatus = log.DeviceState.ThermalStatus
@@ -209,14 +209,13 @@ def hardware_thread(end_event, hw_queue) -> None:
HARDWARE.initialize_hardware()
thermal_config = HARDWARE.get_thermal_config()
fan_controller = None
fan_controller = FanController(int(1./DT_HW))
while not end_event.is_set():
sm.update(PANDA_STATES_TIMEOUT)
pandaStates = sm['pandaStates']
peripheralState = sm['peripheralState']
peripheral_panda_present = peripheralState.pandaType != log.PandaState.PandaType.unknown
# handle requests to cycle system started state
if params.get_bool("OnroadCycleRequested"):
@@ -233,11 +232,6 @@ def hardware_thread(end_event, hw_queue) -> None:
in_car = pandaState.harnessStatus != log.PandaState.HarnessStatus.notConnected
# Setup fan handler on first connect to panda
if fan_controller is None and peripheral_panda_present:
if TICI:
fan_controller = TiciFanController()
elif (time.monotonic() - sm.recv_time['pandaStates']) > DISCONNECT_TIMEOUT:
if onroad_conditions["ignition"]:
onroad_conditions["ignition"] = False
@@ -288,8 +282,7 @@ def hardware_thread(end_event, hw_queue) -> None:
all_comp_temp = all_temp_filter.update(max(temp_sources))
msg.deviceState.maxTempC = all_comp_temp
if fan_controller is not None:
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"])
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"])
is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (time.monotonic() - off_ts > 60 * 5))
if is_offroad_for_5_min and offroad_comp_temp > OFFROAD_DANGER_TEMP:
-1
View File
@@ -6,7 +6,6 @@
class HardwarePC : public HardwareNone {
public:
static std::string get_os_version() { return "openpilot for PC"; }
static std::string get_name() { return "pc"; }
static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::PC; }
static bool PC() { return true; }
+2 -68
View File
@@ -1,78 +1,12 @@
import random
from cereal import log
from openpilot.system.hardware.base import HardwareBase, LPABase
from openpilot.system.hardware.base import HardwareBase
NetworkType = log.DeviceState.NetworkType
NetworkStrength = log.DeviceState.NetworkStrength
class Pc(HardwareBase):
def get_os_version(self):
return None
def get_device_type(self):
return "pc"
def reboot(self, reason=None):
print("REBOOT!")
def uninstall(self):
print("uninstall")
def get_imei(self, slot):
return f"{random.randint(0, 1 << 32):015d}"
def get_serial(self):
return "cccccccc"
def get_network_info(self):
return None
def get_network_type(self):
return NetworkType.wifi
def get_sim_info(self):
return {
'sim_id': '',
'mcc_mnc': None,
'network_type': ["Unknown"],
'sim_state': ["ABSENT"],
'data_connected': False
}
def get_sim_lpa(self) -> LPABase:
raise NotImplementedError("SIM LPA not implemented for PC")
def get_network_strength(self, network_type):
return NetworkStrength.unknown
def get_current_power_draw(self):
return 0
def get_som_power_draw(self):
return 0
def shutdown(self):
print("SHUTDOWN!")
def set_screen_brightness(self, percentage):
pass
def get_screen_brightness(self):
return 0
def set_power_save(self, powersave_enabled):
pass
def get_gpu_usage_percent(self):
return 0
def get_modem_temperatures(self):
return []
def initialize_hardware(self):
pass
def get_networks(self):
return None
+3 -3
View File
@@ -1,12 +1,12 @@
import pytest
from openpilot.system.hardware.fan_controller import TiciFanController
from openpilot.system.hardware.fan_controller import FanController
ALL_CONTROLLERS = [TiciFanController]
ALL_CONTROLLERS = [FanController]
def patched_controller(mocker, controller_class):
mocker.patch("os.system", new=mocker.Mock())
return controller_class()
return controller_class(2)
class TestFanController:
def wind_up(self, controller, ignition=True):
+10 -10
View File
@@ -56,28 +56,28 @@
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c.img.xz",
"hash": "90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c",
"hash_raw": "90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c",
"url": "https://commadist.azureedge.net/agnosupdate/boot-a0185fa5ffc860de2179e4d0fec703fef6d560eacd730f79f60891ca79c72756.img.xz",
"hash": "a0185fa5ffc860de2179e4d0fec703fef6d560eacd730f79f60891ca79c72756",
"hash_raw": "a0185fa5ffc860de2179e4d0fec703fef6d560eacd730f79f60891ca79c72756",
"size": 17496064,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "35014c39b55010ac955c10f808b088e74259147c7a8cbf989b3dff7d95a1e8ae"
"ondevice_hash": "0ee1ab104bb46d0f72e7d0b7d3e94629a7644a368896c6d4c558554fb955a08a"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-d9d476b466186014e7ae4b8232bc6fc5e79b122421bdc12ff4eb02d1c3f37818.img.xz",
"hash": "a068d4d692ec770884f0a15e1a6d7aba52385ecae138f6d43fb0a9b1643ed5cd",
"hash_raw": "d9d476b466186014e7ae4b8232bc6fc5e79b122421bdc12ff4eb02d1c3f37818",
"url": "https://commadist.azureedge.net/agnosupdate/system-0cf8cb01e40d05d6d325afe68b934a6c0dda3a56703b2ef3e3de637d754ae5dd.img.xz",
"hash": "7c58308be461126677ba02e9c9739556520ee02958934733867d86ecfe2e58e9",
"hash_raw": "0cf8cb01e40d05d6d325afe68b934a6c0dda3a56703b2ef3e3de637d754ae5dd",
"size": 4718592000,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "6ffa02f7113badc122742f33efebc5d17f1cd61dd6358f3e130c162707dbfaf4",
"ondevice_hash": "826790516410c325aa30265846946d06a556f0a7b23c957f65fd11c055a663da",
"alt": {
"hash": "d9d476b466186014e7ae4b8232bc6fc5e79b122421bdc12ff4eb02d1c3f37818",
"url": "https://commadist.azureedge.net/agnosupdate/system-d9d476b466186014e7ae4b8232bc6fc5e79b122421bdc12ff4eb02d1c3f37818.img",
"hash": "0cf8cb01e40d05d6d325afe68b934a6c0dda3a56703b2ef3e3de637d754ae5dd",
"url": "https://commadist.azureedge.net/agnosupdate/system-0cf8cb01e40d05d6d325afe68b934a6c0dda3a56703b2ef3e3de637d754ae5dd.img",
"size": 4718592000
}
}
+22 -22
View File
@@ -339,62 +339,62 @@
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c.img.xz",
"hash": "90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c",
"hash_raw": "90bd687e9e407834d4ee1b07f3d05527dfae0ff09c0cacd64cfd6097f6b10e2c",
"url": "https://commadist.azureedge.net/agnosupdate/boot-a0185fa5ffc860de2179e4d0fec703fef6d560eacd730f79f60891ca79c72756.img.xz",
"hash": "a0185fa5ffc860de2179e4d0fec703fef6d560eacd730f79f60891ca79c72756",
"hash_raw": "a0185fa5ffc860de2179e4d0fec703fef6d560eacd730f79f60891ca79c72756",
"size": 17496064,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "35014c39b55010ac955c10f808b088e74259147c7a8cbf989b3dff7d95a1e8ae"
"ondevice_hash": "0ee1ab104bb46d0f72e7d0b7d3e94629a7644a368896c6d4c558554fb955a08a"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-d9d476b466186014e7ae4b8232bc6fc5e79b122421bdc12ff4eb02d1c3f37818.img.xz",
"hash": "a068d4d692ec770884f0a15e1a6d7aba52385ecae138f6d43fb0a9b1643ed5cd",
"hash_raw": "d9d476b466186014e7ae4b8232bc6fc5e79b122421bdc12ff4eb02d1c3f37818",
"url": "https://commadist.azureedge.net/agnosupdate/system-0cf8cb01e40d05d6d325afe68b934a6c0dda3a56703b2ef3e3de637d754ae5dd.img.xz",
"hash": "7c58308be461126677ba02e9c9739556520ee02958934733867d86ecfe2e58e9",
"hash_raw": "0cf8cb01e40d05d6d325afe68b934a6c0dda3a56703b2ef3e3de637d754ae5dd",
"size": 4718592000,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "6ffa02f7113badc122742f33efebc5d17f1cd61dd6358f3e130c162707dbfaf4",
"ondevice_hash": "826790516410c325aa30265846946d06a556f0a7b23c957f65fd11c055a663da",
"alt": {
"hash": "d9d476b466186014e7ae4b8232bc6fc5e79b122421bdc12ff4eb02d1c3f37818",
"url": "https://commadist.azureedge.net/agnosupdate/system-d9d476b466186014e7ae4b8232bc6fc5e79b122421bdc12ff4eb02d1c3f37818.img",
"hash": "0cf8cb01e40d05d6d325afe68b934a6c0dda3a56703b2ef3e3de637d754ae5dd",
"url": "https://commadist.azureedge.net/agnosupdate/system-0cf8cb01e40d05d6d325afe68b934a6c0dda3a56703b2ef3e3de637d754ae5dd.img",
"size": 4718592000
}
},
{
"name": "userdata_90",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-f9ea618ac97a86da49733ce66cd5e3aa19aa917666ee90de301cd746664e4d22.img.xz",
"hash": "dfc6812e76bd1583ed77a86eedf48cafdc306037d2a85c5d0aa7cdb23033b736",
"hash_raw": "f9ea618ac97a86da49733ce66cd5e3aa19aa917666ee90de301cd746664e4d22",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-ec31b8116125a95755adb32853c401c462a14a74f538535532bf2c34d72c60eb.img.xz",
"hash": "aa0f0fe32187493e6135aee9e984d3f9705fc58560d537b34687bb6b51a38428",
"hash_raw": "ec31b8116125a95755adb32853c401c462a14a74f538535532bf2c34d72c60eb",
"size": 96636764160,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "ff95f994e9ed6504632f4b7c6daecef582f0a4e5261b8240d4474f16059faef4"
"ondevice_hash": "9c916b7d05543d4608b0401bc867639f44ce9671639a1a6da83b6d58b4eaa1b4"
},
{
"name": "userdata_89",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-393956e255c277b895bdb98bf65cfa3907e4b57822740ff82f857ac4e1a2f11e.img.xz",
"hash": "b5e2f05d31fc18fff18e82dcebfc2bf04de624baeca0511b93e50b3198b8a9ab",
"hash_raw": "393956e255c277b895bdb98bf65cfa3907e4b57822740ff82f857ac4e1a2f11e",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-7f092cc841124c10300e43574e90e3367e983bfbe4faa0969024e79e5ce90b11.img.xz",
"hash": "fa83d4b7096857136820b0b0a8785c90677256b054c5c14039cd7b9b1065a90b",
"hash_raw": "7f092cc841124c10300e43574e90e3367e983bfbe4faa0969024e79e5ce90b11",
"size": 95563022336,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "db64c6abc72bfcddc1682c73cc73c7230ed2f6e835d292fd38d054a9d242b8fc"
"ondevice_hash": "1699e38de769eb32c21dfa6a5ac21eb3ad620a362c7b8abf1a2c0afe0f717530"
},
{
"name": "userdata_30",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-a4b3e2a2fc3612a37322b7b1a4c5737765841dc3b8d6d3bb58b1e5a271023068.img.xz",
"hash": "ecec713cf7d8f1f616f122a16b138931f818290447e36a5925da6a4fc0fc7bf3",
"hash_raw": "a4b3e2a2fc3612a37322b7b1a4c5737765841dc3b8d6d3bb58b1e5a271023068",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-3df2dcd5e1f426c90b090fdbcd1a95b035d96a4bdaf88d5517245db5ee84f5ed.img.xz",
"hash": "890910f20b1ad88a728ee822a47b1234eb3d70cab28ca8a935679c8c2d33cbe9",
"hash_raw": "3df2dcd5e1f426c90b090fdbcd1a95b035d96a4bdaf88d5517245db5ee84f5ed",
"size": 32212254720,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "48fefa5a1880a4fd3dd50e1f9ddee297122053556816baca310d495129bc8893"
"ondevice_hash": "8e7cb392dd6e49c7d59fa850be7d1f44901314c86ba9c88be5bb27a0cd1123c9"
}
]
+27 -51
View File
@@ -6,23 +6,8 @@ from collections import namedtuple
# https://datasheets.maximintegrated.com/en/ds/MAX98089.pdf
AmpConfig = namedtuple('AmpConfig', ['name', 'value', 'register', 'offset', 'mask'])
EQParams = namedtuple('EQParams', ['K', 'k1', 'k2', 'c1', 'c2'])
def configs_from_eq_params(base, eq_params):
return [
AmpConfig("K (high)", (eq_params.K >> 8), base, 0, 0xFF),
AmpConfig("K (low)", (eq_params.K & 0xFF), base + 1, 0, 0xFF),
AmpConfig("k1 (high)", (eq_params.k1 >> 8), base + 2, 0, 0xFF),
AmpConfig("k1 (low)", (eq_params.k1 & 0xFF), base + 3, 0, 0xFF),
AmpConfig("k2 (high)", (eq_params.k2 >> 8), base + 4, 0, 0xFF),
AmpConfig("k2 (low)", (eq_params.k2 & 0xFF), base + 5, 0, 0xFF),
AmpConfig("c1 (high)", (eq_params.c1 >> 8), base + 6, 0, 0xFF),
AmpConfig("c1 (low)", (eq_params.c1 & 0xFF), base + 7, 0, 0xFF),
AmpConfig("c2 (high)", (eq_params.c2 >> 8), base + 8, 0, 0xFF),
AmpConfig("c2 (low)", (eq_params.c2 & 0xFF), base + 9, 0, 0xFF),
]
BASE_CONFIG = [
CONFIG = [
AmpConfig("MCLK prescaler", 0b01, 0x10, 4, 0b00110000),
AmpConfig("PM: enable speakers", 0b11, 0x4D, 4, 0b00110000),
AmpConfig("PM: enable DACs", 0b11, 0x4D, 0, 0b00000011),
@@ -58,35 +43,31 @@ BASE_CONFIG = [
AmpConfig("Enhanced volume smoothing disabled", 0b0, 0x49, 7, 0b10000000),
AmpConfig("Volume adjustment smoothing disabled", 0b0, 0x49, 6, 0b01000000),
AmpConfig("Zero-crossing detection disabled", 0b0, 0x49, 5, 0b00100000),
AmpConfig("Left speaker output from left DAC", 0b1, 0x2B, 0, 0b11111111),
AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111),
AmpConfig("Left Speaker Mixer Gain", 0b00, 0x2D, 0, 0b00000011),
AmpConfig("Right Speaker Mixer Gain", 0b00, 0x2D, 2, 0b00001100),
AmpConfig("Left speaker output volume", 0x17, 0x3D, 0, 0b00011111),
AmpConfig("Right speaker output volume", 0x17, 0x3E, 0, 0b00011111),
AmpConfig("DAI2 EQ enable", 0b0, 0x49, 1, 0b00000010),
AmpConfig("DAI2: DC blocking", 0b0, 0x20, 0, 0b00000001),
AmpConfig("ALC enable", 0b0, 0x43, 7, 0b10000000),
AmpConfig("DAI2 EQ attenuation", 0x2, 0x32, 0, 0b00001111),
AmpConfig("Excursion limiter upper corner freq", 0b001, 0x41, 4, 0b01110000),
AmpConfig("Excursion limiter threshold", 0b100, 0x42, 0, 0b00001111),
AmpConfig("Distortion limit (THDCLP)", 0x0, 0x46, 4, 0b11110000),
AmpConfig("Distortion limiter release time constant", 0b1, 0x46, 0, 0b00000001),
AmpConfig("Left DAC input mixer: DAI1 left", 0b0, 0x22, 7, 0b10000000),
AmpConfig("Left DAC input mixer: DAI1 right", 0b0, 0x22, 6, 0b01000000),
AmpConfig("Left DAC input mixer: DAI2 left", 0b1, 0x22, 5, 0b00100000),
AmpConfig("Left DAC input mixer: DAI2 right", 0b0, 0x22, 4, 0b00010000),
AmpConfig("Right DAC input mixer: DAI2 left", 0b0, 0x22, 1, 0b00000010),
AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001),
AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000),
]
CONFIGS = {
"tizi": [
AmpConfig("Left speaker output from left DAC", 0b1, 0x2B, 0, 0b11111111),
AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111),
AmpConfig("Left Speaker Mixer Gain", 0b00, 0x2D, 0, 0b00000011),
AmpConfig("Right Speaker Mixer Gain", 0b00, 0x2D, 2, 0b00001100),
AmpConfig("Left speaker output volume", 0x17, 0x3D, 0, 0b00011111),
AmpConfig("Right speaker output volume", 0x17, 0x3E, 0, 0b00011111),
AmpConfig("DAI2 EQ enable", 0b0, 0x49, 1, 0b00000010),
AmpConfig("DAI2: DC blocking", 0b0, 0x20, 0, 0b00000001),
AmpConfig("ALC enable", 0b0, 0x43, 7, 0b10000000),
AmpConfig("DAI2 EQ attenuation", 0x2, 0x32, 0, 0b00001111),
AmpConfig("Excursion limiter upper corner freq", 0b001, 0x41, 4, 0b01110000),
AmpConfig("Excursion limiter threshold", 0b100, 0x42, 0, 0b00001111),
AmpConfig("Distortion limit (THDCLP)", 0x0, 0x46, 4, 0b11110000),
AmpConfig("Distortion limiter release time constant", 0b1, 0x46, 0, 0b00000001),
AmpConfig("Left DAC input mixer: DAI1 left", 0b0, 0x22, 7, 0b10000000),
AmpConfig("Left DAC input mixer: DAI1 right", 0b0, 0x22, 6, 0b01000000),
AmpConfig("Left DAC input mixer: DAI2 left", 0b1, 0x22, 5, 0b00100000),
AmpConfig("Left DAC input mixer: DAI2 right", 0b0, 0x22, 4, 0b00010000),
AmpConfig("Right DAC input mixer: DAI2 left", 0b0, 0x22, 1, 0b00000010),
AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001),
AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000),
],
}
class Amplifier:
AMP_I2C_BUS = 0
AMP_ADDRESS = 0x10
@@ -127,20 +108,15 @@ class Amplifier:
def set_global_shutdown(self, amp_disabled: bool) -> bool:
return self.set_configs([self._get_shutdown_config(amp_disabled), ])
def initialize_configuration(self, model: str) -> bool:
def initialize_configuration(self) -> bool:
cfgs = [
self._get_shutdown_config(True),
*BASE_CONFIG,
*CONFIGS[model],
*CONFIG,
self._get_shutdown_config(False),
]
return self.set_configs(cfgs)
if __name__ == "__main__":
with open("/sys/firmware/devicetree/base/model") as f:
model = f.read().strip('\x00')
model = model.split('comma ')[-1]
amp = Amplifier()
amp.initialize_configuration(model)
amp.initialize_configuration()
-29
View File
@@ -40,7 +40,6 @@ class TiciLPA(LPABase):
self._process_notifications()
def download_profile(self, qr: str, nickname: str | None = None) -> None:
self._check_bootstrapped()
msgs = self._invoke('profile', 'download', '-a', qr)
self._validate_successful(msgs)
new_profile = next((m for m in msgs if m['payload']['message'] == 'es8p_meatadata_parse'), None)
@@ -55,7 +54,6 @@ class TiciLPA(LPABase):
self._validate_successful(self._invoke('profile', 'nickname', iccid, nickname))
def switch_profile(self, iccid: str) -> None:
self._check_bootstrapped()
self._validate_profile_exists(iccid)
latest = self.get_active_profile()
if latest and latest.iccid == iccid:
@@ -63,33 +61,6 @@ class TiciLPA(LPABase):
self._validate_successful(self._invoke('profile', 'enable', iccid))
self._process_notifications()
def bootstrap(self) -> None:
"""
find all comma-provisioned profiles and delete them. they conflict with user-provisioned profiles
and must be deleted.
**note**: this is a **very** destructive operation. you **must** purchase a new comma SIM in order
to use comma prime again.
"""
if self._is_bootstrapped():
return
for p in self.list_profiles():
if self.is_comma_profile(p.iccid):
self._disable_profile(p.iccid)
self.delete_profile(p.iccid)
def _disable_profile(self, iccid: str) -> None:
self._validate_successful(self._invoke('profile', 'disable', iccid))
self._process_notifications()
def _check_bootstrapped(self) -> None:
assert self._is_bootstrapped(), 'eUICC is not bootstrapped, please bootstrap before performing this operation'
def _is_bootstrapped(self) -> bool:
""" check if any comma provisioned profiles are on the eUICC """
return not any(self.is_comma_profile(iccid) for iccid in (p.iccid for p in self.list_profiles()))
def _invoke(self, *cmd: str):
proc = subprocess.Popen(['sudo', '-E', 'lpac'] + list(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.env)
try:
+4 -4
View File
@@ -8,7 +8,7 @@ from functools import cached_property, lru_cache
from pathlib import Path
from cereal import log
from openpilot.common.util import sudo_read, sudo_write
from openpilot.common.utils import sudo_read, sudo_write
from openpilot.common.gpio import gpio_set, gpio_init, get_irqs_for_action
from openpilot.system.hardware.base import HardwareBase, LPABase, ThermalConfig, ThermalZone
from openpilot.system.hardware.tici import iwlist
@@ -125,7 +125,7 @@ class Tici(HardwareBase):
return int(f.read())
def set_ir_power(self, percent: int):
if self.get_device_type() in ("tici", "tizi"):
if self.get_device_type() == "tizi":
return
value = int((percent / 100) * 300)
@@ -369,7 +369,7 @@ class Tici(HardwareBase):
if self.amplifier is not None:
self.amplifier.set_global_shutdown(amp_disabled=powersave_enabled)
if not powersave_enabled:
self.amplifier.initialize_configuration(self.get_device_type())
self.amplifier.initialize_configuration()
# *** CPU config ***
@@ -404,7 +404,7 @@ class Tici(HardwareBase):
def initialize_hardware(self):
if self.amplifier is not None:
self.amplifier.initialize_configuration(self.get_device_type())
self.amplifier.initialize_configuration()
# Allow hardwared to write engagement status to kmsg
os.system("sudo chmod a+w /dev/kmsg")
+2 -3
View File
@@ -5,7 +5,6 @@ import subprocess
from panda import Panda
from openpilot.system.hardware import TICI, HARDWARE
from openpilot.system.hardware.tici.hardware import Tici
from openpilot.system.hardware.tici.amplifier import Amplifier
@@ -39,7 +38,7 @@ class TestAmplifier:
def test_init(self):
amp = Amplifier(debug=True)
r = amp.initialize_configuration(Tici().get_device_type())
r = amp.initialize_configuration()
assert r
assert self._check_for_i2c_errors(False)
@@ -61,7 +60,7 @@ class TestAmplifier:
time.sleep(random.randint(0, 5))
amp = Amplifier(debug=True)
r = amp.initialize_configuration(Tici().get_device_type())
r = amp.initialize_configuration()
assert r
if self._check_for_i2c_errors(True):
+2 -2
View File
@@ -10,7 +10,7 @@ from openpilot.system.hardware.hw import Paths
from openpilot.system.loggerd.xattr_cache import setxattr
def create_random_file(file_path: Path, size_mb: float, lock: bool = False, upload_xattr: bytes = None) -> None:
def create_random_file(file_path: Path, size_mb: float, lock: bool = False, upload_xattr: bytes | None = None) -> None:
file_path.parent.mkdir(parents=True, exist_ok=True)
if lock:
@@ -80,7 +80,7 @@ class UploaderTestCase:
self.params.put("DongleId", "0000000000000000")
def make_file_with_data(self, f_dir: str, fn: str, size_mb: float = .1, lock: bool = False,
upload_xattr: bytes = None, preserve_xattr: bytes = None) -> Path:
upload_xattr: bytes | None = None, preserve_xattr: bytes | None = None) -> Path:
file_path = Path(Paths.log_root()) / f_dir / fn
create_random_file(file_path, size_mb, lock, upload_xattr)
+1 -1
View File
@@ -50,7 +50,7 @@ class TestUploader(UploaderTestCase):
self.end_event.set()
self.up_thread.join()
def gen_files(self, lock=False, xattr: bytes = None, boot=True) -> list[Path]:
def gen_files(self, lock=False, xattr: bytes | None = None, boot=True) -> list[Path]:
f_paths = []
for t in ["qlog", "rlog", "dcamera.hevc", "fcamera.hevc"]:
f_paths.append(self.make_file_with_data(self.seg_dir, t, 1, lock=lock, upload_xattr=xattr))
+1 -1
View File
@@ -226,7 +226,7 @@ class Uploader:
return self.upload(name, key, fn, network_type, metered)
def main(exit_event: threading.Event = None) -> None:
def main(exit_event: threading.Event | None = None) -> None:
if exit_event is None:
exit_event = threading.Event()
+1 -1
View File
@@ -81,7 +81,7 @@ class ManagerProcess(ABC):
self.stop(sig=signal.SIGKILL)
self.start()
def stop(self, retry: bool = True, block: bool = True, sig: signal.Signals = None) -> int | None:
def stop(self, retry: bool = True, block: bool = True, sig: signal.Signals | None = None) -> int | None:
if self.proc is None:
return None
+2 -2
View File
@@ -107,11 +107,11 @@ def process_nmea_port_messages(device:str="/dev/ttyUSB1") -> NoReturn:
match fields[0]:
case "$GNCLK":
# fields at end are reserved (not used)
gnss_clock = GnssClockNmeaPort(*fields[1:10]) # type: ignore[arg-type]
gnss_clock = GnssClockNmeaPort(*fields[1:10])
print(gnss_clock)
case "$GNMEAS":
# fields at end are reserved (not used)
gnss_meas = GnssMeasNmeaPort(*fields[1:14]) # type: ignore[arg-type]
gnss_meas = GnssMeasNmeaPort(*fields[1:14])
print(gnss_meas)
except Exception as e:
print(e)
+1 -1
View File
@@ -7,7 +7,7 @@ import threading
import cereal.messaging as messaging
from cereal.services import SERVICE_LIST
from openpilot.common.util import sudo_write
from openpilot.common.utils import sudo_write
from openpilot.common.realtime import config_realtime_process, Ratekeeper
from openpilot.common.swaglog import cloudlog
from openpilot.common.gpio import gpiochip_get_ro_value_fd, gpioevent_data
+2 -8
View File
@@ -32,23 +32,17 @@ SENSOR_CONFIGURATIONS: list[set] = {
Sensor = log.SensorEventData.SensorSource
SensorConfig = namedtuple('SensorConfig', ['type', 'sanity_min', 'sanity_max'])
ALL_SENSORS = {
Sensor.lsm6ds3: {
SensorConfig("acceleration", 5, 15),
SensorConfig("gyroUncalibrated", 0, .2),
SensorConfig("temperature", 0, 60),
},
Sensor.lsm6ds3trc: {
SensorConfig("acceleration", 5, 15),
SensorConfig("gyroUncalibrated", 0, .2),
SensorConfig("temperature", 0, 60),
SensorConfig("temperature", 10, 40), # set for max range of our office
},
Sensor.mmc5603nj: {
SensorConfig("magneticUncalibrated", 0, 300),
}
}
ALL_SENSORS[Sensor.lsm6ds3] = ALL_SENSORS[Sensor.lsm6ds3trc]
def get_irq_count(irq: int):
with open(f"/sys/kernel/irq/{irq}/per_cpu_count") as f:
+7 -5
View File
@@ -187,7 +187,8 @@ class MouseState:
time.monotonic(),
)
# Only add changes
if self._prev_mouse_event[slot] is None or ev[:-1] != self._prev_mouse_event[slot][:-1]:
prev = self._prev_mouse_event[slot]
if prev is None or ev[:-1] != prev[:-1]:
with self._lock:
self._events.append(ev)
self._prev_mouse_event[slot] = ev
@@ -195,6 +196,8 @@ class MouseState:
class GuiApplication(GuiApplicationExt):
def __init__(self, width: int | None = None, height: int | None = None):
self._set_log_callback()
self._fonts: dict[FontWeight, rl.Font] = {}
self._width = width if width is not None else GuiApplication._default_width()
self._height = height if height is not None else GuiApplication._default_height()
@@ -218,7 +221,6 @@ class GuiApplication(GuiApplicationExt):
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._modal_overlay_tick: Callable[[], None] | None = None
@@ -265,9 +267,6 @@ class GuiApplication(GuiApplicationExt):
signal.signal(signal.SIGINT, _close)
atexit.register(self.close)
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
@@ -648,6 +647,9 @@ class GuiApplication(GuiApplicationExt):
else:
cloudlog.error(f"raylib: Unknown level {log_level}: {text_str}")
# ensure we get all the logs forwarded to us
rl.set_trace_log_level(rl.TraceLogLevel.LOG_DEBUG)
# Store callback reference
self._trace_log_callback = trace_log_callback
rl.set_trace_log_callback(self._trace_log_callback)
+1 -1
View File
@@ -48,7 +48,7 @@ class Multilang:
translation = gettext.GNUTranslations(fh)
translation.install()
self._translation = translation
cloudlog.warning(f"Loaded translations for language: {self._language}")
cloudlog.debug(f"Loaded translations for language: {self._language}")
except FileNotFoundError:
cloudlog.error(f"No translation file found for language: {self._language}, using default.")
gettext.install('app')
+1 -1
View File
@@ -631,7 +631,7 @@ class WifiManager:
known_connections = self._get_connections()
networks = [Network.from_dbus(ssid, ap_list, ssid in known_connections) for ssid, ap_list in aps.items()]
# sort with quantized strength to reduce jumping
networks.sort(key=lambda n: (-n.is_connected, -round(n.strength / 100 * 4), n.ssid.lower()))
networks.sort(key=lambda n: (-n.is_connected, -round(n.strength / 100 * 2), n.ssid.lower()))
self._networks = networks
self._update_ipv4_address()
+1 -1
View File
@@ -132,7 +132,7 @@ class Reset(Widget):
if self._reset_state == ResetState.FAILED:
return "Reset failed. Reboot to try again."
if self._mode == ResetMode.RECOVER:
return "Unable to mount data partition. Partition may be corrupted."
return "Unable to mount data partition. It may be corrupted."
return "All content and settings will be erased."
+9 -8
View File
@@ -102,14 +102,15 @@ class Updater(Widget):
self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True, bufsize=1, universal_newlines=True)
for line in self.process.stdout:
parts = line.strip().split(":")
if len(parts) == 2:
self.progress_text = parts[0].lower()
try:
self.progress_value = int(float(parts[1]))
except ValueError:
pass
if self.process.stdout is not None:
for line in self.process.stdout:
parts = line.strip().split(":")
if len(parts) == 2:
self.progress_text = parts[0].lower()
try:
self.progress_value = int(float(parts[1]))
except ValueError:
pass
exit_code = self.process.wait()
if exit_code == 0:
+17 -7
View File
@@ -55,7 +55,7 @@ class ButtonSP(Button):
class SimpleButtonActionSP(ItemAction):
def __init__(self, button_text: str | Callable[[], str], callback: Callable = None,
def __init__(self, button_text: str | Callable[[], str], callback: Callable | None = None,
enabled: bool | Callable[[], bool] = True, button_width: int = style.SIMPLE_BUTTON_WIDTH):
super().__init__(width=button_width, enabled=enabled)
self.button_action = ButtonSP(button_text, click_callback=callback, button_style=ButtonStyle.NORMAL,
@@ -99,8 +99,8 @@ class ButtonActionSP(ButtonAction):
class DualButtonActionSP(DualButtonAction):
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, border_radius: int = 15):
def __init__(self, left_text: str | Callable[[], str], right_text: str | Callable[[], str], left_callback: Callable | None = None,
right_callback: Callable | None = None, enabled: bool | Callable[[], bool] = True, border_radius: int = 15):
DualButtonAction.__init__(self, left_text, right_text, left_callback, right_callback, enabled)
self.left_button._border_radius = self.right_button._border_radius = border_radius
@@ -126,7 +126,7 @@ class DualButtonActionSP(DualButtonAction):
class MultipleButtonActionSP(MultipleButtonAction):
def __init__(self, buttons: list[str | Callable[[], 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 = None,
param: str | None = None):
MultipleButtonAction.__init__(self, buttons, button_width, selected_index, callback)
self.param_key = param
@@ -192,6 +192,9 @@ class ListItemSP(ListItem):
self._right_value_font = gui_app.font(FontWeight.NORMAL)
self._right_value_color: rl.Color = style.ITEM_TEXT_VALUE_COLOR
def set_title(self, title: str | Callable[[], str] = ""):
self._title = title
def set_right_value(self, value: str | Callable[[], str], color: rl.Color = style.ITEM_TEXT_VALUE_COLOR):
self._right_value_source = value
self._right_value_color = color
@@ -202,6 +205,13 @@ class ListItemSP(ListItem):
return ""
return str(_resolve_value(self._right_value_source, ""))
def _update_state(self):
prev_desc = self._prev_description
super()._update_state()
if self.description_visible and self._prev_description != prev_desc:
content_width = int(self._rect.width - style.ITEM_PADDING * 2)
self._rect.height = self.get_item_height(self._font, content_width)
def get_item_height(self, font: rl.Font, max_width: int) -> float:
height = super().get_item_height(font, max_width)
@@ -326,7 +336,7 @@ def toggle_item_sp(title: str | Callable[[], str], description: str | Callable[[
def multiple_button_item_sp(title: str | Callable[[], str], description: str | Callable[[], str], buttons: list[str | Callable[[], str]],
selected_index: int = 0, button_width: int = style.BUTTON_ACTION_WIDTH, callback: Callable = None,
selected_index: int = 0, button_width: int = style.BUTTON_ACTION_WIDTH, callback: Callable | None = None,
icon: str = "", param: str | None = None, inline: bool = False) -> ListItemSP:
action = MultipleButtonActionSP(buttons, button_width, selected_index, callback=callback, param=param)
return ListItemSP(title=title, description=description, icon=icon, action_item=action, inline=inline)
@@ -351,8 +361,8 @@ def button_item_sp(title: str | Callable[[], str], button_text: str | Callable[[
return ListItemSP(title=title, description=description, action_item=action, callback=callback)
def dual_button_item_sp(left_text: str | Callable[[], str], right_text: str | Callable[[], str], left_callback: Callable = None,
right_callback: Callable = None, description: str | Callable[[], str] | None = None,
def dual_button_item_sp(left_text: str | Callable[[], str], right_text: str | Callable[[], str], left_callback: Callable | None = None,
right_callback: Callable | None = None, description: str | Callable[[], str] | None = None,
enabled: bool | Callable[[], bool] = True, border_radius: int = 15) -> ListItemSP:
action = DualButtonActionSP(left_text, right_text, left_callback, right_callback, enabled, border_radius)
return ListItemSP(title="", description=description, action_item=action)
+9 -8
View File
@@ -71,14 +71,15 @@ class Updater(Widget):
self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True, bufsize=1, universal_newlines=True)
for line in self.process.stdout:
parts = line.strip().split(":")
if len(parts) == 2:
self.progress_text = parts[0]
try:
self.progress_value = int(float(parts[1]))
except ValueError:
pass
if self.process.stdout is not None:
for line in self.process.stdout:
parts = line.strip().split(":")
if len(parts) == 2:
self.progress_text = parts[0]
try:
self.progress_value = int(float(parts[1]))
except ValueError:
pass
exit_code = self.process.wait()
if exit_code == 0:
+8 -8
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import abc
import pyray as rl
from enum import IntEnum
@@ -10,7 +12,7 @@ try:
except ImportError:
class Device:
awake = True
device = Device() # type: ignore
device = Device()
class DialogResult(IntEnum):
@@ -91,7 +93,7 @@ class Widget(abc.ABC):
return self._rect
return rl.get_collision_rec(self._rect, self._parent_rect)
def render(self, rect: rl.Rectangle = None) -> bool | int | None:
def render(self, rect: rl.Rectangle | None = None) -> bool | int | None:
if rect is not None:
self.set_rect(rect)
@@ -165,15 +167,13 @@ 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:
def _handle_mouse_press(self, mouse_pos: MousePos) -> None:
"""Optionally handle mouse press events."""
return False
def _handle_mouse_release(self, mouse_pos: MousePos) -> bool:
def _handle_mouse_release(self, mouse_pos: MousePos) -> None:
"""Optionally handle mouse release events."""
if self._click_callback:
self._click_callback()
return False
def _handle_mouse_event(self, mouse_event: MouseEvent) -> None:
"""Optionally handle mouse events. This is called before rendering."""
@@ -299,7 +299,7 @@ class NavWidget(Widget, abc.ABC):
# block horizontal swiping if now swiping away
if self._can_swipe_away:
if mouse_event.pos.y - self._back_button_start_pos.y > START_DISMISSING_THRESHOLD: # type: ignore
if mouse_event.pos.y - self._back_button_start_pos.y > START_DISMISSING_THRESHOLD:
self._swiping_away = True
elif mouse_event.left_released:
@@ -361,7 +361,7 @@ class NavWidget(Widget, abc.ABC):
self.set_position(self._rect.x, new_y)
def render(self, rect: rl.Rectangle = None) -> bool | int | None:
def render(self, rect: rl.Rectangle | None = None) -> bool | int | None:
ret = super().render(rect)
if self.back_enabled:
+2 -2
View File
@@ -31,13 +31,13 @@ class MiciLabel(Widget):
def __init__(self,
text: str,
font_size: int = DEFAULT_TEXT_SIZE,
width: int = None,
width: int | None = None,
color: rl.Color = DEFAULT_TEXT_COLOR,
font_weight: FontWeight = FontWeight.NORMAL,
alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_LEFT,
alignment_vertical: int = rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP,
spacing: int = 0,
line_height: int = None,
line_height: int | None = None,
elide_right: bool = True,
wrap_text: bool = False,
scroll: bool = False):
+6 -5
View File
@@ -174,8 +174,8 @@ class TextAction(ItemAction):
class DualButtonAction(ItemAction):
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):
def __init__(self, left_text: str | Callable[[], str], right_text: str | Callable[[], str], left_callback: Callable | None = None,
right_callback: Callable | None = None, enabled: bool | Callable[[], bool] = True):
super().__init__(width=0, enabled=enabled) # Width 0 means use full width
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)
@@ -207,7 +207,7 @@ class DualButtonAction(ItemAction):
class MultipleButtonAction(ItemAction):
def __init__(self, buttons: list[str | Callable[[], 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 = None):
super().__init__(width=len(buttons) * button_width + (len(buttons) - 1) * RIGHT_ITEM_PADDING, enabled=True)
self.buttons = buttons
self.button_width = button_width
@@ -454,13 +454,14 @@ def text_item(title: str | Callable[[], str], value: str | Callable[[], str], de
return ListItem(title=title, description=description, action_item=action, callback=callback)
def dual_button_item(left_text: str | Callable[[], str], right_text: str | Callable[[], 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 = None, right_callback: Callable | None = 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 | Callable[[], str], description: str | Callable[[], str], buttons: list[str | Callable[[], str]], selected_index: int,
button_width: int = BUTTON_WIDTH, callback: Callable = None, icon: str = ""):
button_width: int = BUTTON_WIDTH, callback: Callable | None = None, icon: str = ""):
action = MultipleButtonAction(buttons, button_width, selected_index, callback=callback)
return ListItem(title=title, description=description, icon=icon, action_item=action)
+2 -2
View File
@@ -28,8 +28,8 @@ try:
from openpilot.selfdrive.ui.lib.prime_state import PrimeType
except Exception:
Params = None
ui_state = None # type: ignore
PrimeType = None # type: ignore
ui_state = None
PrimeType = None
NM_DEVICE_STATE_NEED_AUTH = 60
MIN_PASSWORD_LENGTH = 8
+2 -2
View File
@@ -168,7 +168,7 @@ def build_chunk_dict(chunks: list[Chunk]) -> ChunkDict:
def extract(target: list[Chunk],
sources: list[tuple[str, ChunkReader, ChunkDict]],
out_path: str,
progress: Callable[[int], None] = None):
progress: Callable[[int], None] | None = None):
stats: dict[str, int] = defaultdict(int)
mode = 'rb+' if os.path.exists(out_path) else 'wb'
@@ -208,7 +208,7 @@ def extract_directory(target: list[Chunk],
sources: list[tuple[str, ChunkReader, ChunkDict]],
out_path: str,
tmp_file: str,
progress: Callable[[int], None] = None):
progress: Callable[[int], None] | None = None):
"""extract a directory stored as a casync tar archive"""
stats = extract(target, sources, tmp_file, progress)
+1 -1
View File
@@ -67,7 +67,7 @@ def write_time_to_param(params, param) -> None:
t = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
params.put(param, t)
def run(cmd: list[str], cwd: str = None) -> str:
def run(cmd: list[str], cwd: str | None = None) -> str:
return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT, encoding='utf8')
+2 -2
View File
@@ -16,7 +16,7 @@ class AudioInputStreamTrack(aiortc.mediastreams.AudioStreamTrack):
pyaudio.paFloat32: 'flt',
}
def __init__(self, audio_format: int = pyaudio.paInt16, rate: int = 16000, channels: int = 1, packet_time: float = 0.020, device_index: int = None):
def __init__(self, audio_format: int = pyaudio.paInt16, rate: int = 16000, channels: int = 1, packet_time: float = 0.020, device_index: int | None = None):
super().__init__()
self.p = pyaudio.PyAudio()
@@ -48,7 +48,7 @@ class AudioInputStreamTrack(aiortc.mediastreams.AudioStreamTrack):
class AudioOutputSpeaker:
def __init__(self, audio_format: int = pyaudio.paInt16, rate: int = 48000, channels: int = 2, packet_time: float = 0.2, device_index: int = None):
def __init__(self, audio_format: int = pyaudio.paInt16, rate: int = 48000, channels: int = 2, packet_time: float = 0.2, device_index: int | None = None):
chunk_size = int(packet_time * rate)
self.p = pyaudio.PyAudio()