mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-07-24 07:42:05 +08:00
Merge branch 'master-priv' into disable-on-screen-recorder
This commit is contained in:
+8
-8
@@ -5,17 +5,17 @@ sunnypilot - 0.9.7.0 (2024-xx-xx)
|
||||
************************
|
||||
* UPDATED: Synced with commaai's openpilot
|
||||
* master commit 56e343b (February 27, 2024)
|
||||
* NEW❗: Config Backup (Alpha early access)
|
||||
* Remotely back up and restore sunnypilot settings easily
|
||||
* Device registration with sunnylink ensures a secure, integrated experience across services
|
||||
* AES encryption derived from the device's RSA private key is used for utmost security
|
||||
* Settings are encrypted on-device, transmitted securely via HTTPS, and stored encrypted on sunnylink
|
||||
* Prevents loss of settings after device resets, offering peace of mind through end-to-end encryption
|
||||
* Early alpha access to all current and previous GitHub Sponsors and Patreon supporters
|
||||
* NEW❗: sunnylink (Alpha early access)
|
||||
* NEW❗: Config Backup
|
||||
* Remotely back up and restore sunnypilot settings easily
|
||||
* Device registration with sunnylink ensures a secure, integrated experience across services
|
||||
* AES encryption derived from the device's RSA private key is used for utmost security
|
||||
* Settings are encrypted on-device, transmitted securely via HTTPS, and stored encrypted on sunnylink
|
||||
* Prevents loss of settings after device resets, offering peace of mind through end-to-end encryption
|
||||
* Early alpha access to all current and previous GitHub Sponsors and Patreon supporters
|
||||
* GitHub account pairing from device settings scanning QR code
|
||||
* Pairing your account will allow you to access features via our API (still WIP but accessible if you dig a little on our code 😉)
|
||||
* Allow inheritance of your sponsorship status, allowing you to get extra features and early access whenever applicable
|
||||
* Immediate sponsor recognition works only for new sponsors. If you're an earlier sponsor, we've got you — just reach out to a moderator on Discord (https://discord.gg/sunnypilot) to get sorted!
|
||||
* RE-ENABLED: Map-based Turn Speed Control (M-TSC) for supported platforms
|
||||
* openpilot Longitudianl Control available cars
|
||||
* Custom Stock Longitudinal Control available cars
|
||||
|
||||
@@ -28,6 +28,10 @@ class SunnylinkApi(BaseApi):
|
||||
|
||||
return super().api_get(endpoint, method, timeout, **kwargs)
|
||||
|
||||
def resume_queued(self, timeout=10, **kwargs):
|
||||
sunnylinkId, commaId = self._resolve_dongle_ids()
|
||||
return self.api_get(f"ws/{sunnylinkId}/resume_queued", "POST", timeout, access_token=self.get_token(), **kwargs)
|
||||
|
||||
def get_token(self, expiry_hours=1):
|
||||
# Add your additional data here
|
||||
additional_data = {}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from libcpp cimport bool
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.map cimport map
|
||||
|
||||
cdef extern from "common/params.h":
|
||||
cpdef enum ParamKeyType:
|
||||
@@ -26,6 +27,7 @@ cdef extern from "common/params.h":
|
||||
string getParamPath(string) nogil
|
||||
void clearAll(ParamKeyType)
|
||||
vector[string] allKeys()
|
||||
map[string, string] readAll()
|
||||
|
||||
|
||||
def ensure_bytes(v):
|
||||
@@ -116,3 +118,6 @@ cdef class Params:
|
||||
|
||||
def all_keys(self):
|
||||
return self.p.allKeys()
|
||||
|
||||
def read_all(self):
|
||||
return self.p.readAll()
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import gzip
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
|
||||
from openpilot.selfdrive.athena.athenad import ws_send, jsonrpc_handler, \
|
||||
recv_queue, RECONNECT_TIMEOUT_S, UploadQueueCache, upload_queue, cur_upload_items, backoff, ws_manage
|
||||
from jsonrpc import dispatcher
|
||||
from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutException,
|
||||
create_connection)
|
||||
|
||||
@@ -21,7 +25,8 @@ SUNNYLINK_ATHENA_HOST = os.getenv('SUNNYLINK_ATHENA_HOST', 'wss://ws.stg.api.sun
|
||||
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
|
||||
LOCAL_PORT_WHITELIST = {8022}
|
||||
|
||||
|
||||
params = Params()
|
||||
sunnylink_api = SunnylinkApi(params.get("SunnylinkDongleId", encoding='utf-8'))
|
||||
def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None:
|
||||
end_event = threading.Event()
|
||||
|
||||
@@ -30,6 +35,7 @@ def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None:
|
||||
threading.Thread(target=ws_recv, args=(ws, end_event), name='ws_recv'),
|
||||
threading.Thread(target=ws_send, args=(ws, end_event), name='ws_send'),
|
||||
threading.Thread(target=ws_ping, args=(ws, end_event), name='ws_ping'),
|
||||
threading.Thread(target=ws_queue, args=(end_event,), name='ws_queue'),
|
||||
# threading.Thread(target=upload_handler, args=(end_event,), name='upload_handler'),
|
||||
# threading.Thread(target=log_handler, args=(end_event,), name='log_handler'),
|
||||
# threading.Thread(target=stat_handler, args=(end_event,), name='stat_handler'),
|
||||
@@ -62,7 +68,9 @@ def ws_recv(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
if opcode == ABNF.OPCODE_TEXT:
|
||||
data = data.decode("utf-8")
|
||||
recv_queue.put_nowait(data)
|
||||
cloudlog.debug(f"sunnylinkd.ws_recv.recv {data}")
|
||||
elif opcode in (ABNF.OPCODE_PING, ABNF.OPCODE_PONG):
|
||||
cloudlog.debug(f"sunnylinkd.ws_recv.pong {opcode}")
|
||||
last_ping = int(time.monotonic() * 1e9)
|
||||
Params().put("LastSunnylinkPingTime", str(last_ping))
|
||||
except WebSocketTimeoutException:
|
||||
@@ -80,11 +88,67 @@ def ws_ping(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
ws.ping()
|
||||
cloudlog.debug(f"sunnylinkd.ws_recv.ws_ping: Pinging")
|
||||
except Exception:
|
||||
cloudlog.exception("sunnylinkd.ws_ping.exception")
|
||||
end_event.set()
|
||||
time.sleep(RECONNECT_TIMEOUT_S * 0.8) # Sleep about 80% before a timeout
|
||||
|
||||
def ws_queue(end_event: threading.Event) -> None:
|
||||
resume_requested = False
|
||||
tries = 0
|
||||
|
||||
while not end_event.is_set() and not resume_requested:
|
||||
try:
|
||||
if not resume_requested:
|
||||
cloudlog.debug(f"sunnylinkd.ws_queue.resume_queued")
|
||||
sunnylink_api.resume_queued(timeout=29)
|
||||
resume_requested = True
|
||||
tries = 0
|
||||
except Exception:
|
||||
cloudlog.exception("sunnylinkd.ws_queue.resume_queued.exception")
|
||||
resume_requested = False
|
||||
tries += 1
|
||||
time.sleep(backoff(tries)) # Wait for the backoff time before the next attempt
|
||||
cloudlog.debug("Resume requested or end_event is set, exiting ws_queue thread")
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getParamsAllKeys() -> list[str]:
|
||||
keys: list[str] = [k.decode('utf-8') for k in Params().all_keys()]
|
||||
return keys
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getParams(params_keys: list[str], compression: bool = False) -> str | dict[str, str]:
|
||||
try:
|
||||
params = Params()
|
||||
params_dict: dict[str, bytes] = {key: params.get(key) or b'' for key in params_keys}
|
||||
|
||||
# Compress the values before encoding to base64 as output from params.get is bytes and same for compression
|
||||
if compression:
|
||||
params_dict = {key: gzip.compress(value) for key, value in params_dict.items()}
|
||||
|
||||
# Last step is to encode the values to base64 and decode to utf-8 for JSON serialization
|
||||
return {key: base64.b64encode(value).decode('utf-8') for key, value in params_dict.items()}
|
||||
|
||||
except Exception as e:
|
||||
return cloudlog.exception("sunnylinkd.getParams.exception", e)
|
||||
|
||||
@dispatcher.add_method
|
||||
def saveParams(params_to_update: dict[str, str], compression: bool = False) -> None:
|
||||
params = Params()
|
||||
try:
|
||||
params_dict = {key: base64.b64decode(value) for key, value in params_to_update.items()}
|
||||
|
||||
if compression:
|
||||
params_dict = {key: gzip.decompress(value) for key, value in params_dict.items()}
|
||||
|
||||
for key, value in params_dict.items():
|
||||
params.put(key, value)
|
||||
except Exception as e:
|
||||
return cloudlog.exception("sunnylinkd.saveParams.exception", e)
|
||||
|
||||
|
||||
def main(exit_event: threading.Event = None):
|
||||
try:
|
||||
@@ -92,12 +156,9 @@ def main(exit_event: threading.Event = None):
|
||||
except Exception:
|
||||
cloudlog.exception("failed to set core affinity")
|
||||
|
||||
params = Params()
|
||||
dongle_id = params.get("SunnylinkDongleId", encoding='utf-8')
|
||||
UploadQueueCache.initialize(upload_queue)
|
||||
|
||||
ws_uri = SUNNYLINK_ATHENA_HOST
|
||||
api = SunnylinkApi(dongle_id)
|
||||
conn_start = None
|
||||
conn_retries = 0
|
||||
while exit_event is None or not exit_event.is_set():
|
||||
@@ -107,7 +168,7 @@ def main(exit_event: threading.Event = None):
|
||||
|
||||
cloudlog.event("sunnylinkd.main.connecting_ws", ws_uri=ws_uri, retries=conn_retries)
|
||||
ws = create_connection(ws_uri,
|
||||
cookie="jwt=" + api.get_token(),
|
||||
cookie="jwt=" + sunnylink_api.get_token(),
|
||||
enable_multithread=True,
|
||||
timeout=30.0)
|
||||
cloudlog.event("sunnylinkd.main.connected_ws", ws_uri=ws_uri, retries=conn_retries,
|
||||
|
||||
@@ -200,6 +200,11 @@ void SunnylinkPanel::updateLabels() {
|
||||
bool is_paired = uiState()->isSunnylinkPaired();
|
||||
auto paired_users = uiState()->sunnylinkDeviceUsers();
|
||||
|
||||
//little easter egg for Panda :D
|
||||
if(sunnylinkDongleId == "d689627422cefcbc") {
|
||||
role_name = "Panda 🐼";
|
||||
}
|
||||
|
||||
sunnylinkEnabledBtn->setEnabled(!is_onroad);
|
||||
sunnylinkEnabledBtn->setValue(tr("Device ID ")+ sunnylinkDongleId);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user