Compare commits

..

9 Commits

Author SHA1 Message Date
Jason Wen 58d57f7e1a max global brightness 2026-01-11 01:10:57 -05:00
Jason Wen 9cbc91d129 read 2026-01-11 00:57:47 -05:00
Jason Wen 764ff0db6a update immediately 2026-01-11 00:55:08 -05:00
Jason Wen 8faedaa062 lint 2026-01-11 00:32:22 -05:00
Jason Wen 4cc43ae6f5 override properly and clip to 30% minimum 2026-01-11 00:10:30 -05:00
Jason Wen 578e80855a do not touch light sensor logic 2026-01-10 23:49:17 -05:00
DevTekVE 66c335113e Reapply "ui: Global Brightness Override (#1579)"
This reverts commit a0c10be1ff.
2026-01-10 21:25:33 +01:00
DevTekVE a0c10be1ff Revert "ui: Global Brightness Override (#1579)"
This reverts commit 1eb82fcc
2026-01-10 21:24:44 +01:00
Nayan a58db66a98 sunnylink: add units to param metadata (#1643)
add units
2026-01-09 18:40:09 -05:00
10 changed files with 82 additions and 443 deletions
+19
View File
@@ -88,3 +88,22 @@ class DeviceSP:
def _set_awake(self, on: bool):
if on and self._params.get("DeviceBootMode", return_default=True) == 1:
self._params.put_bool("OffroadMode", True)
@staticmethod
def update_max_global_brightness(brightness_override: int) -> float:
"""
Updates the max global brightness by constraining the value to a predefined range.
The method takes an integer `brightness` value, adjusts it to ensure it is within the
range of 30 to 100, inclusive, and returns the adjusted value as a float.
This method only runs if 0 (Auto) is not selected.
:param brightness_override: The desired brightness level. It is constrained between
a minimum of 30 and a maximum of 100.
:type brightness_override: int
:return: The brightness value adjusted to fit within the allowable range,
converted to a float.
:rtype: float
"""
return float(min(max(brightness_override, 30), 100))
+3 -8
View File
@@ -258,17 +258,12 @@ class Device(DeviceSP):
else:
clipped_brightness = ((clipped_brightness + 16.0) / 116.0) ** 3.0
if gui_app.sunnypilot_ui():
if ui_state.global_brightness_override <= 0:
min_global_brightness = 1 if ui_state.global_brightness_override < 0 else 30
clipped_brightness = float(np.interp(clipped_brightness, [0, 1], [min_global_brightness, 100]))
else:
clipped_brightness = float(np.interp(clipped_brightness, [0, 1], [30, 100]))
clipped_brightness = float(np.interp(clipped_brightness, [0, 1], [30, 100]))
brightness = round(self._brightness_filter.update(clipped_brightness))
if gui_app.sunnypilot_ui() and ui_state.global_brightness_override > 0:
brightness = ui_state.global_brightness_override
if gui_app.sunnypilot_ui() and ui_state.global_brightness_override != 0:
brightness = DeviceSP.update_max_global_brightness(ui_state.global_brightness_override)
if not self._awake:
brightness = 0
-129
View File
@@ -1,129 +0,0 @@
<center>
# Comparative Analysis of Parameter Getter Methods: Params vs. ParamWatcher
James (sunnypilot Developer) <br>
December 13, 2025
</center>
## <br><br> Abstract
This research report examines the inefficiencies in standard parameter access methods within sunnypilot and proposes an optimized alternative, ParamWatcher. The standard `Params::get()` method incurs significant CPU and memory overhead due to repeated file I/O operations (sunnypilot, 2025). ParamWatcher utilizes OS-level file system events (inotify on Linux, FSEvents on macOS) to maintain an in-memory cache, reducing I/O to near zero (Linux man-pages, 2025-c; Apple Inc., n.d.-a). Empirical benchmarks with ~10 million parameter accesses demonstrate a 14.5x CPU speedup and flat memory usage (514.7 KB vs. 497.7 KB for base Params, with only 17 KB overhead). The implementation employs a process-local singleton pattern for efficiency in multi-process architectures (Gamma et al., 1994). Results indicate ParamWatcher eliminates UI stutters and GC pauses, enhancing system responsiveness without compromising data freshness.
**Keywords:** parameter access, file I/O optimization, event-driven caching, autonomous driving systems, performance benchmarking
## Introduction
In sunnypilot, efficient parameter management is important for real-time system access. The standard `Params::get()` method, implemented in C++ and wrapped in Python, performs full file I/O cycles for each access, leading to high CPU overhead and memory churn (sunnypilot, 2025). This is particularly problematic in UI loops where parameters are queried frequently (e.g., 50 toggles at 20 FPS equates to ~1,000 reads/second).
This inefficiency stems from architectural mismatches: C++ streams are designed for throughput, not latency (cppreference.com, n.d.-a). Each call triggers kernel mode switches, heap allocations, and garbage collection in Python, causing UI stutters (Linux man-pages, 2025-a; Linux man-pages, 2025-b).
### Inefficiencies in Standard Parameter Access
The standard `Params::get()` method executes a full file I/O lifecycle—opening, allocating, reading, and closing—for every function call. This results in significant CPU overhead and memory churn due to the frequency of these operations in the user interface loop.
#### System Overhead Analysis
- **System Call Overhead**: Every read operation requires context switches into kernel mode. The `Params::get` function calls `util::read_file` (sunnypilot, 2025), which subsequently invokes `std::ifstream` (sunnypilot, 2025).
- **Impact**: Frequent context switching degrades performance (Linux man-pages, 2025-a; Linux man-pages, 2025-b).
- **C++ Stream Overhead**: The use of `std::ifstream` introduces additional overhead for maintaining stream state and buffering compared to raw file descriptors (cppreference.com, n.d.-a; Codezup, 2025).
- **Memory Churn**: The instantiation of `std::string result(size, '\0');` forces heap allocation and deallocation during every call (sunnypilot, 2025). This stresses the memory allocator and can lead to fragmentation (cppreference.com, n.d.-b).
This report introduces ParamWatcher, an event-driven caching solution using OS file system events. It shifts from polling to notifications, caching converted values in static RAM. I propose that ParamWatcher achieves minimum 10x+ CPU gains with bounded non increasing memory, improving sunnypilot's performance both on latency and responsiveness.
## Method
### Materials
- **System:** sunnypilot, running on macOS, Ubuntu/Linux, comma 3x, and comma four.
- **Parameters:** 231 defined keys in `param_keys.h`.
- **Tools:** Python 3, tracemalloc for memory profiling, time.perf_counter for CPU timing, ctypes for OS integration (Python Software Foundation, 2025).
### Implementation Details
ParamWatcher provides cross-platform file system monitoring using ctypes for direct OS integration (Python Software Foundation, 2025).
#### Linux Implementation
On Linux, ParamWatcher uses the inotify subsystem for efficient file change detection (Linux man-pages, 2025-c). It loads `libc.so.6` to access system calls, initializes an inotify instance, and watches the parameters directory for events like `IN_MODIFY` and `IN_CLOSE_WRITE` (Linux Kernel Organization, 2005). Events are polled with `select.epoll()` and parsed using `struct.unpack_from()` to avoid ctypes overhead. Filenames are extracted and passed to cache invalidation, ensuring real-time updates without polling (Codezup, 2025).
- **Library Loading**: `libc = ctypes.CDLL('libc.so.6')` loads the standard C library to access system calls.
- **Initialization**: `inotify_init()` is called to create a new inotify instance, returning a file descriptor.
- **Watch Setup**: `inotify_add_watch(fd, path, mask)` registers the parameters directory. The mask includes `IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVED_TO | IN_CLOSE_WRITE` (Linux Kernel Organization, 2005) to capture all relevant file changes.
- **Event Loop**:
- **Polling**: `select.epoll()` is used to efficiently wait for activity on the file descriptor without busy-waiting.
- **Reading**: When events occur, `os.read(fd, 2048)` retrieves the raw binary event data.
- **Parsing**: The code uses Python's `struct` module (`struct.unpack_from("iIII", ...)`) to parse the C-style `inotify_event` structures directly from the buffer, avoiding the overhead of defining `ctypes` structures.
- **Handling**: Extracted filenames are passed to `_trigger_callbacks`, which invalidates the specific cache entry (`self._cache.pop(path, None)`), forcing a fresh read on the next access.
#### macOS Implementation
On macOS, ParamWatcher leverages FSEvents from CoreServices for directory monitoring (Apple Inc., n.d.-a). It defines a C-compatible callback using `CFUNCTYPE`, creates an `FSEventStream` with `kFSEventStreamCreateFlagFileEvents`, and schedules it on the run loop (Apple Inc., n.d.-b). Events are filtered for modifications, creations, and renames (Apple Inc., n.d.-c), triggering cache invalidation for affected parameters.
- **Framework Loading**: `ctypes.cdll.LoadLibrary` loads `CoreServices` and `CoreFoundation`.
- **Callback Definition**: `CFUNCTYPE` is used to define a C-compatible callback function. This function is invoked by the OS whenever a change occurs in the watched directory.
- **Stream Creation**: `FSEventStreamCreate` creates a stream for the target directory. The `kFSEventStreamCreateFlagFileEvents` flag is used to request file-level granularity where available.
- **Event Filtering**: The callback filters events using flags such as `kFSEventStreamEventFlagItemCreated` and `kFSEventStreamEventFlagItemModified` to ensure only relevant file changes trigger updates (Apple Inc., n.d.-c).
- **Scheduling**: `FSEventStreamScheduleWithRunLoop` attaches the stream to the current thread's run loop (Apple Inc., n.d.-b).
- **Execution**: `CFRunLoopRun()` starts the event loop. This passes control to the OS, which wakes the thread only when necessary.
- **Handling**: Inside the callback, the code iterates through the changed paths provided by the OS. It extracts the filename and calls `_trigger_callbacks` to invalidate the cache for that specific parameter.
### Procedure
Benchmarks simulated heavy load:
- **Memory Test:** ~10 million gets (43,290 loops over 231 keys), measured with tracemalloc.
- **CPU Test:** Same load, timed with perf_counter.
- Comparisons: Base Params vs. ParamWatcher.
## Results
### Memory Usage
Using tracemalloc for peak memory measurement during ~10 million parameter accesses (43,290 loops over 231 keys), base Params peaked at 497.7 KB, while ParamWatcher peaked at 514.7 KB (17 KB overhead). ParamWatcher's memory remained flat post-initialization, preventing churn.
| Condition | Memory (KB) | Overhead |
|-----------|-------------|----------|
| Base Params | 497.7 | - |
| ParamWatcher | 514.7 | 17 KB |
### CPU Performance
ParamWatcher was 14.5x faster: 4.52s vs. 65.43s to complete ~10 million param gets.
| Condition | Time (s) | Speedup |
|-----------|----------|---------|
| Base Params | 65.43 | 1x |
| ParamWatcher | 4.52 | 14.5x |
### Scalability
No degradation at scale; cache invalidation maintained freshness.
## Discussion
ParamWatcher successfully optimizes parameter access, delivering substantial CPU gains with minimal memory overhead. The event-driven approach eliminates I/O bottlenecks, reducing GC pressure and UI stutters (cppreference.com, n.d.-b). The 17 KB memory overhead is negligible compared to the megabytes of churn from base Params, ensuring bounded usage in multi-process environments via the singleton pattern (Gamma et al., 1994).
Results demonstrate scalability without degradation, with cache invalidation maintaining data freshness. This optimization enhances system responsiveness.
Limitations include potential event latency in high-load scenarios (<10 ms, imperceptible for UI) and increased complexity from background threads.
Trade-offs: Static RAM (~17 KB) vs. dynamic churn; benefits outweigh costs for param-heavy workloads.
## <br>References
Apple Inc. (n.d.-a). *File System Events*. Retrieved from https://developer.apple.com/documentation/coreservices/file_system_events
Apple Inc. (n.d.-b). *CFRunLoop*. Retrieved from https://developer.apple.com/documentation/corefoundation/cfrunloop
Apple Inc. (n.d.-c). *FSEventStreamEventFlags*. Retrieved from https://developer.apple.com/documentation/coreservices/1455361-fseventstreameventflags
Codezup. (2025). *Efficient File I/O in C++*. Retrieved from https://codezup.com/efficient-file-io-cpp-best-practices/
cppreference.com. (n.d.-a). *std::basic_ifstream*. Retrieved from https://en.cppreference.com/w/cpp/io/basic_ifstream
cppreference.com. (n.d.-b). *std::basic_string*. Retrieved from https://en.cppreference.com/w/cpp/string/basic_string/basic_string
Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley.
Linux Kernel Organization. (2005). *include/uapi/linux/inotify.h*. Retrieved from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/inotify.h
Linux man-pages. (2025-a). *open(2)*. Retrieved from https://man7.org/linux/man-pages/man2/open.2.html
Linux man-pages. (2025-b). *read(2)*. Retrieved from https://man7.org/linux/man-pages/man2/read.2.html
Linux man-pages. (2025-c). *inotify(7)*. Retrieved from https://man7.org/linux/man-pages/man7/inotify.7.html
Python Software Foundation. (2025). *ctypes — A foreign function library for Python*. Retrieved from https://docs.python.org/3/library/ctypes.html
sunnypilot. (2025). *common/params.cc* [Source code]. GitHub. https://github.com/sunnypilot/sunnypilot/blob/master/common/params.cc#L180C1-L206C2
sunnypilot. (2025). *common/util.cc* [Source code]. GitHub. https://github.com/sunnypilot/sunnypilot/blob/master/common/util.cc#L79C1-L117C2
View File
-193
View File
@@ -1,193 +0,0 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import os
import platform
import struct
import select
import threading
import time
import ctypes
import ctypes.util
import traceback
from ctypes import c_void_p, c_size_t, POINTER, c_uint32, c_uint64
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware.hw import Paths
IN_MODIFY = 0x00000002
IN_CREATE = 0x00000100
IN_DELETE = 0x00000200
IN_MOVED_TO = 0x00000080
IN_CLOSE_WRITE = 0x00000008
class ParamWatcher(Params):
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if self._initialized:
return
super().__init__()
cloudlog.warning("ParamWatcher initialized")
self._cache = {}
self._last_trigger = {}
self._version = {}
self._lock = threading.Lock()
self._callbacks = []
self.last_accessed_param = None
self._initialized = True
self.start()
def start(self):
if getattr(self, '_thread', None) and self._thread.is_alive():
return
self._thread = threading.Thread(target=self._run_watcher, daemon=True)
self._thread.start()
def is_watching(self):
return getattr(self, '_thread', None) and self._thread.is_alive()
def add_watcher(self, callback):
if callback not in self._callbacks:
self._callbacks.append(callback)
def _trigger_callbacks(self, path):
with self._lock:
if (now := time.monotonic()) - self._last_trigger.get(path, 0) < 0.1:
return
self._last_trigger[path] = now
self._version[path] = self._version.get(path, 0) + 1
self._cache.pop(path, None)
for callback in self._callbacks:
try:
callback(path)
except Exception:
cloudlog.exception("Param watcher callback failed")
def _get_cached(self, key, getter, sig):
k = str(key)
with self._lock:
bucket = self._cache.get(k)
if bucket and sig in bucket:
if bucket[sig][0] == self._version.get(k, 0):
return bucket[sig][1]
start_ver = self._version.get(k, 0)
val = getter()
with self._lock:
if self._version.get(k, 0) != start_ver:
val = getter()
self._cache.setdefault(k, {})[sig] = (self._version.get(k, 0), val)
return val
def get(self, key, block=False, return_default=False):
self.last_accessed_param = key
if block:
return super().get(key, block, return_default)
fetcher = super().get
return self._get_cached(key, lambda: fetcher(key, block, return_default), (block, return_default))
def get_bool(self, key, block=False):
self.last_accessed_param = key
if block:
return super().get_bool(key, block)
fetcher = super().get_bool
return self._get_cached(key, lambda: fetcher(key, block), ("bool", block))
def _run_watcher(self):
system = platform.system()
while True:
try:
if system == "Linux":
self._run_linux()
elif system == "Darwin":
self._run_darwin()
except Exception:
cloudlog.exception("Param watcher crashed")
time.sleep(2)
def _run_linux(self):
path = Paths.params_root()
libc = ctypes.CDLL('libc.so.6')
fd = libc.inotify_init()
libc.inotify_add_watch(fd, path.encode(), IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVED_TO | IN_CLOSE_WRITE)
try:
poll = select.epoll()
poll.register(fd, select.EPOLLIN)
while True:
for fileno, _ in poll.poll():
if fileno == fd:
buffer = os.read(fd, 2048)
i = 0
while i + 16 <= len(buffer):
_, mask, _, name_len = struct.unpack_from("iIII", buffer, i)
if mask & (IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVED_TO | IN_CLOSE_WRITE):
name = buffer[i+16:i+16+name_len].rstrip(b"\0").decode()
if not name.startswith("."):
self._trigger_callbacks(name)
i += 16 + name_len
finally:
if 'poll' in locals():
poll.unregister(fd)
poll.close()
os.close(fd)
def _run_darwin(self):
CS = ctypes.cdll.LoadLibrary(ctypes.util.find_library("CoreServices"))
CF = ctypes.cdll.LoadLibrary(ctypes.util.find_library("CoreFoundation"))
kCFAllocatorDefault = c_void_p(0)
kCFStringEncodingUTF8 = 0x08000100
kFSEventStreamCreateFlagFileEvents = 0x00000010
kFSEventStreamEventFlagItemCreated = 0x00000100
kFSEventStreamEventFlagItemRemoved = 0x00000200
kFSEventStreamEventFlagItemRenamed = 0x00000800
kFSEventStreamEventFlagItemModified = 0x00001000
CF.CFStringCreateWithCString.restype = c_void_p
CF.CFStringCreateWithCString.argtypes = [c_void_p, ctypes.c_char_p, c_uint32]
CF.CFArrayCreate.restype = c_void_p
CF.CFArrayCreate.argtypes = [c_void_p, POINTER(c_void_p), c_size_t, c_void_p]
CS.FSEventStreamCreate.restype = c_void_p
CS.FSEventStreamCreate.argtypes = [c_void_p, c_void_p, c_void_p, c_void_p, c_uint64, ctypes.c_double, c_uint32]
CS.FSEventStreamScheduleWithRunLoop.argtypes = [c_void_p, c_void_p, c_void_p]
CS.FSEventStreamStart.argtypes = [c_void_p]
CF.CFRunLoopGetCurrent.restype = c_void_p
def _cb(stream, ctx, num, paths, flags, ids):
try:
paths_arr = ctypes.cast(paths, POINTER(c_void_p))
flags_arr = ctypes.cast(flags, POINTER(c_uint32))
for i in range(num):
path = ctypes.cast(paths_arr[i], ctypes.c_char_p).value
if path and (flags_arr[i] & (kFSEventStreamEventFlagItemCreated | kFSEventStreamEventFlagItemRemoved |
kFSEventStreamEventFlagItemRenamed | kFSEventStreamEventFlagItemModified)):
self._trigger_callbacks(os.path.basename(path.decode('utf-8').rstrip('/')))
except Exception:
traceback.print_exc()
self._darwin_cb = ctypes.CFUNCTYPE(None, c_void_p, c_void_p, c_size_t, c_void_p, POINTER(c_uint32), POINTER(c_uint64))(_cb)
path_str = Paths.params_root().encode('utf-8')
cf_path = CF.CFStringCreateWithCString(kCFAllocatorDefault, path_str, kCFStringEncodingUTF8)
cf_paths = CF.CFArrayCreate(kCFAllocatorDefault, (c_void_p * 1)(cf_path), 1, None)
stream = CS.FSEventStreamCreate(kCFAllocatorDefault, self._darwin_cb, None, cf_paths, -1, 0.05, kFSEventStreamCreateFlagFileEvents)
run_loop = CF.CFRunLoopGetCurrent()
kDefaultMode = CF.CFStringCreateWithCString(kCFAllocatorDefault, b"kCFRunLoopDefaultMode", kCFStringEncodingUTF8)
CS.FSEventStreamScheduleWithRunLoop(stream, run_loop, kDefaultMode)
CS.FSEventStreamStart(stream)
CF.CFRunLoopRun()
-4
View File
@@ -1,4 +0,0 @@
from openpilot.common.params_pyx import ParamKeyFlag, ParamKeyType, UnknownKeyName
from openpilot.sunnypilot.common.param_watcher import ParamWatcher as Params
__all__ = ["Params", "ParamKeyFlag", "ParamKeyType", "UnknownKeyName"]
View File
@@ -1,94 +0,0 @@
import time
import pytest
import threading
import tracemalloc
from openpilot.common.params import Params
from openpilot.common.params_pyx import UnknownKeyName
from openpilot.sunnypilot.common.param_watcher import ParamWatcher
class TestParamWatcher:
BYTES_KEYS = ["LocationFilterInitialState", "UpdaterCurrentReleaseNotes", "UpdaterNewReleaseNotes"]
BOOL_KEYS = [
"IsMetric", "AdbEnabled", "AlwaysOnDM", "ExperimentalMode",
"ExperimentalModeConfirmed", "DisengageOnAccelerator",
"OpenpilotEnabledToggle", "RecordAudio", "RecordFront"
]
_key_counter = 0
@pytest.fixture(autouse=True)
def setup_method(self):
self.params = Params()
self.key_index = TestParamWatcher._key_counter
TestParamWatcher._key_counter += 1
self.bytes_key = self.BYTES_KEYS[self.key_index % len(self.BYTES_KEYS)]
self.bool_key = self.BOOL_KEYS[self.key_index % len(self.BOOL_KEYS)]
@pytest.fixture
def param_watcher(self):
ParamWatcher._instance = None
param_watch = ParamWatcher()
param_watch.start()
assert param_watch.is_watching(), "ParamWatcher thread died"
return param_watch
def teardown_method(self):
for key in (self.bytes_key, self.bool_key):
try:
self.params.remove(key)
except UnknownKeyName:
pass
def test_watcher_detects_change(self, param_watcher):
val = b"123"
self.params.put(self.bytes_key, val)
assert param_watcher.get(self.bytes_key) == val
def test_watcher_get_bool(self, param_watcher):
self.params.put_bool(self.bool_key, True)
assert param_watcher.get_bool(self.bool_key) is True # First read should populate internal cache
def test_performance_comparison(self, param_watcher):
plain_params = self.params
for key in self.BYTES_KEYS:
plain_params.put(key, b"x" * 10000)
param_watcher.get(key)
for key in self.BOOL_KEYS:
plain_params.put_bool(key, True)
param_watcher.get_bool(key)
def bench(get_bytes, get_bool):
tracemalloc.start()
start_time = time.process_time()
for _ in range(1000):
for key in self.BYTES_KEYS:
get_bytes(key)
for key in self.BOOL_KEYS:
get_bool(key)
duration = time.process_time() - start_time
_, memory = tracemalloc.get_traced_memory()
tracemalloc.stop()
return duration, memory
plain_cpu, plain_memory = bench(plain_params.get, plain_params.get_bool)
watcher_cpu, watcher_memory = bench(param_watcher.get, param_watcher.get_bool)
# ParamWatcher *should* be significantly faster and use less memory than Params()
assert watcher_cpu < plain_cpu * 0.6, f"PW CPU ({watcher_cpu:.4f}s) should be < 60% of Param call ({plain_cpu:.4f}s)"
assert watcher_memory < plain_memory * 0.5, f"PW Memory ({watcher_memory}B) should be < 50% of Param call ({plain_memory}B)"
def test_cache_invalidation_simulation(self, param_watcher):
self.params.put(self.bytes_key, b"old")
assert param_watcher.get(self.bytes_key) == b"old"
time.sleep(0.2)
event = threading.Event()
param_watcher.add_watcher(lambda key: event.set())
param_watcher._trigger_callbacks(self.bytes_key)
assert event.wait(timeout=2), "Callback not triggered"
self.params.put(self.bytes_key, b"new")
assert param_watcher.get(self.bytes_key) == b"new"
+60 -8
View File
@@ -126,11 +126,13 @@
"description": "Virtually shift camera's perspective to move model's center to Left(+ values) or Right (- values)",
"min": -0.35,
"max": 0.35,
"step": 0.01
"step": 0.01,
"unit": "meters"
},
"CarBatteryCapacity": {
"title": "Car Battery Capacity",
"description": ""
"description": "Battery Size",
"unit": "kWh"
},
"CarList": {
"title": "Supported Car List",
@@ -396,7 +398,8 @@
},
"InteractivityTimeout": {
"title": "Interactivity Timeout",
"description": ""
"description": "",
"unit": "seconds"
},
"IsDevelopmentBranch": {
"title": "Is Development Branch",
@@ -630,7 +633,8 @@
},
"MaxTimeOffroad": {
"title": "Max Time Offroad",
"description": ""
"description": "",
"unit": "seconds"
},
"ModelManager_ActiveBundle": {
"title": "Model Manager Active Bundle",
@@ -784,9 +788,56 @@
"OnroadScreenOffTimer": {
"title": "Onroad Brightness Delay",
"description": "",
"min": 0,
"max": 60,
"step": 1
"options": [
{
"value": 15,
"label": "15s"
},
{
"value": 30,
"label": "30s"
},
{
"value": 60,
"label": "1m"
},
{
"value": 120,
"label": "2m"
},
{
"value": 180,
"label": "3m"
},
{
"value": 240,
"label": "4m"
},
{
"value": 300,
"label": "5m"
},
{
"value": 360,
"label": "6m"
},
{
"value": 420,
"label": "7m"
},
{
"value": 480,
"label": "8m"
},
{
"value": 540,
"label": "9m"
},
{
"value": 600,
"label": "10m"
}
]
},
"OnroadUploads": {
"title": "Onroad Uploads",
@@ -1060,7 +1111,8 @@
"description": "",
"min": 0.1,
"max": 5.0,
"step": 0.1
"step": 0.1,
"unit": "m/s²"
},
"ToyotaEnforceStockLongitudinal": {
"title": "Toyota: Enforce Factory Longitudinal Control",
-7
View File
@@ -95,10 +95,3 @@ class Paths:
return str(Path(Paths.comma_home()) / "media" / "0" / "osm")
else:
return "/data/media/0/osm"
@staticmethod
def params_root() -> str:
if PC:
return str(Path(Paths.comma_home()) / "params" / "d")
else:
return "/data/params/d"