|
| 1 | +from enum import IntEnum |
| 2 | +import os |
| 3 | +import threading |
| 4 | +import time |
| 5 | + |
| 6 | +from openpilot.common.api import Api, api_get |
| 7 | +from openpilot.common.params import Params |
| 8 | +from openpilot.common.swaglog import cloudlog |
| 9 | + |
| 10 | + |
| 11 | +class PrimeType(IntEnum): |
| 12 | + UNKNOWN = -2, |
| 13 | + UNPAIRED = -1, |
| 14 | + NONE = 0, |
| 15 | + MAGENTA = 1, |
| 16 | + LITE = 2, |
| 17 | + BLUE = 3, |
| 18 | + MAGENTA_NEW = 4, |
| 19 | + PURPLE = 5, |
| 20 | + |
| 21 | + |
| 22 | +class PrimeState: |
| 23 | + FETCH_INTERVAL = 5.0 # seconds between API calls |
| 24 | + API_TIMEOUT = 10.0 # seconds for API requests |
| 25 | + SLEEP_INTERVAL = 0.5 # seconds to sleep between checks in the worker thread |
| 26 | + |
| 27 | + def __init__(self): |
| 28 | + self._params = Params() |
| 29 | + self._lock = threading.Lock() |
| 30 | + self.prime_type: PrimeType = self._load_initial_state() |
| 31 | + |
| 32 | + self._running = False |
| 33 | + self._thread = None |
| 34 | + self.start() |
| 35 | + |
| 36 | + def _load_initial_state(self) -> PrimeType: |
| 37 | + prime_type_str = os.getenv("PRIME_TYPE") or self._params.get("PrimeType", encoding='utf8') |
| 38 | + try: |
| 39 | + if prime_type_str is not None: |
| 40 | + return PrimeType(int(prime_type_str)) |
| 41 | + except (ValueError, TypeError): |
| 42 | + pass |
| 43 | + return PrimeType.UNKNOWN |
| 44 | + |
| 45 | + def _fetch_prime_status(self) -> None: |
| 46 | + dongle_id = self._params.get("DongleId", encoding='utf8') |
| 47 | + if not dongle_id: |
| 48 | + return |
| 49 | + |
| 50 | + try: |
| 51 | + identity_token = Api(dongle_id).get_token() |
| 52 | + response = api_get(f"v1.1/devices/{dongle_id}", timeout=self.API_TIMEOUT, access_token=identity_token) |
| 53 | + if response.status_code == 200: |
| 54 | + data = response.json() |
| 55 | + is_paired = data.get("is_paired", False) |
| 56 | + prime_type = data.get("prime_type", 0) |
| 57 | + self.set_type(PrimeType(prime_type) if is_paired else PrimeType.UNPAIRED) |
| 58 | + except Exception as e: |
| 59 | + print(f"Failed to fetch prime status: {e}") |
| 60 | + |
| 61 | + def set_type(self, prime_type: PrimeType) -> None: |
| 62 | + with self._lock: |
| 63 | + if prime_type != self.prime_type: |
| 64 | + self.prime_type = prime_type |
| 65 | + self._params.put("PrimeType", str(int(prime_type))) |
| 66 | + cloudlog.info(f"Prime type updated to {prime_type}") |
| 67 | + |
| 68 | + def _worker_thread(self) -> None: |
| 69 | + while self._running: |
| 70 | + self._fetch_prime_status() |
| 71 | + |
| 72 | + for _ in range(int(self.FETCH_INTERVAL / self.SLEEP_INTERVAL)): |
| 73 | + if not self._running: |
| 74 | + break |
| 75 | + time.sleep(self.SLEEP_INTERVAL) |
| 76 | + |
| 77 | + def start(self) -> None: |
| 78 | + if self._thread and self._thread.is_alive(): |
| 79 | + return |
| 80 | + self._running = True |
| 81 | + self._thread = threading.Thread(target=self._worker_thread, daemon=True) |
| 82 | + self._thread.start() |
| 83 | + |
| 84 | + def stop(self) -> None: |
| 85 | + self._running = False |
| 86 | + if self._thread and self._thread.is_alive(): |
| 87 | + self._thread.join(timeout=1.0) |
| 88 | + |
| 89 | + def get_type(self) -> PrimeType: |
| 90 | + with self._lock: |
| 91 | + return self.prime_type |
| 92 | + |
| 93 | + def is_prime(self) -> bool: |
| 94 | + with self._lock: |
| 95 | + return self.prime_type > PrimeType.NONE |
| 96 | + |
| 97 | + def __del__(self): |
| 98 | + self.stop() |
0 commit comments