diff --git a/common/api/sunnylink.py b/common/api/sunnylink.py index e2761cc29f..4318a27c9a 100644 --- a/common/api/sunnylink.py +++ b/common/api/sunnylink.py @@ -82,15 +82,17 @@ class SunnylinkApi(BaseApi): privkey_path = Path(Paths.persist_root()+"/comma/id_rsa") pubkey_path = Path(Paths.persist_root()+"/comma/id_rsa.pub") + start_time = time.monotonic() + successful_registration = False if not pubkey_path.is_file(): sunnylink_dongle_id = UNREGISTERED_SUNNYLINK_DONGLE_ID self._status_update("Public key not found, setting dongle ID to unregistered.") else: + Params().put("LastSunnylinkPingTime", "0") # Reset the last ping time to 0 if we are trying to register with pubkey_path.open() as f1, privkey_path.open() as f2: public_key = f1.read() private_key = f2.read() - start_time = time.monotonic() backoff = 1 while True: register_token = jwt.encode({'register': True, 'exp': datetime.utcnow() + timedelta(hours=1)}, private_key, algorithm='RS256') @@ -102,17 +104,29 @@ class SunnylinkApi(BaseApi): resp = self.api_get("v2/pilotauth/", method='POST', timeout=15, imei=imei1, imei2=imei2, serial=serial, comma_dongle_id=comma_dongle_id, public_key=public_key, register_token=register_token) + if resp.status_code in (409, 412): + timeout = time.monotonic() - start_time # Don't retry if the public key is already in use + key_in_use = "Public key is already in use, is your key unique? Contact your vendor for a new key." + unsafe_key = "Public key is known to not be unique and it's unsafe. Contact your vendor for a new key." + error_message = key_in_use if resp.status_code == 409 else unsafe_key + raise Exception(error_message) + if resp.status_code != 200: - raise Exception(f"Failed to register with sunnylink. Status code: {resp.status_code}") - else: - dongleauth = json.loads(resp.text) - sunnylink_dongle_id = dongleauth["device_id"] - if sunnylink_dongle_id: - self._status_update("Device registered successfully.") - break + raise Exception(f"Failed to register with sunnylink. Status code: {resp.status_code}\nData\n:{resp.text}") + + dongleauth = json.loads(resp.text) + sunnylink_dongle_id = dongleauth["device_id"] + if sunnylink_dongle_id: + self._status_update("Device registered successfully.") + successful_registration = True + break except Exception as e: if verbose: self._status_update(f"Waiting {backoff}s before retry, Exception occurred during registration: [{str(e)}]") + + with open('/data/community/crashes/error.txt', 'a') as f: + f.write(f"[{datetime.now()}] sunnylink: {str(e)}\n") + backoff = min(backoff * 2, 60) time.sleep(backoff) @@ -123,5 +137,13 @@ class SunnylinkApi(BaseApi): self.params.put("SunnylinkDongleId", sunnylink_dongle_id or UNREGISTERED_SUNNYLINK_DONGLE_ID) + # Set the last ping time to the current time since we were just talking to the API + last_ping = int(time.monotonic() * 1e9) if successful_registration else start_time + Params().put("LastSunnylinkPingTime", str(last_ping)) + + # Disable sunnylink if registration was not successful + if not successful_registration: + Params().put_bool("SunnylinkEnabled", False) + self.spinner = None return sunnylink_dongle_id diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 9463843ab2..8d45e62ce4 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -158,23 +158,22 @@ void Sidebar::updateState(const UIState &s) { auto sl_dongle_id = getSunnylinkDongleId(); auto last_sunnylink_ping_str = params.get("LastSunnylinkPingTime"); auto last_sunnylink_ping = std::stoull(last_sunnylink_ping_str.empty() ? "0" : last_sunnylink_ping_str); - auto current_nanos = nanos_since_boot(); - auto elapsed_sunnylink_ping = current_nanos - last_sunnylink_ping; + auto elapsed_sunnylink_ping = nanos_since_boot() - last_sunnylink_ping; auto sunnylink_enabled = params.getBool("SunnylinkEnabled"); - if (!sunnylink_enabled) { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("DISABLED")}, disabled_color}; - } else if(!sl_dongle_id.has_value()) { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("REGIST...")}, progress_color}; - } else if (last_sunnylink_ping == 0) { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("OFFLINE")}, warning_color}; - } else { - if (elapsed_sunnylink_ping < 80000000000ULL) { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("ONLINE")}, good_color}; - } - else { - sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), tr("ERROR")}, danger_color}; - } + + QString status = tr("DISABLED"); + QColor color = disabled_color; + + if (sunnylink_enabled && last_sunnylink_ping == 0) { + // If sunnylink is enabled, but we don't have a dongle id, and we haven't received a ping yet, we are registering + status = sl_dongle_id.has_value() ? tr("OFFLINE") : tr("REGIST..."); + color = sl_dongle_id.has_value() ? warning_color : progress_color; + } else if (sunnylink_enabled) { + // If sunnylink is enabled, we are considered online if we have received a ping in the last 80 seconds, else error. + status = elapsed_sunnylink_ping < 80000000000ULL ? tr("ONLINE") : tr("ERROR"); + color = elapsed_sunnylink_ping < 80000000000ULL ? good_color : danger_color; } + sunnylinkStatus = ItemStatus{{tr("SUNNYLINK"), status}, color }; setProperty("sunnylinkStatus", QVariant::fromValue(sunnylinkStatus)); } diff --git a/system/manager/sunnylink.py b/system/manager/sunnylink.py index 1a70532e82..84161fb817 100755 --- a/system/manager/sunnylink.py +++ b/system/manager/sunnylink.py @@ -50,10 +50,6 @@ def register_sunnylink(): sunnylink_id = SunnylinkApi(None).register_device(None, **extra_args) print(f"SunnyLinkId: {sunnylink_id}") - # Set the last ping time to the current time since we just registered - last_ping = int(time.monotonic() * 1e9) - Params().put("LastSunnylinkPingTime", str(last_ping)) - def main(): """The main method is expected to be called by the manager when the device boots up."""