|
|
|
@ -1,5 +1,6 @@ |
|
|
|
|
import os |
|
|
|
|
from openpilot.common.params import Params |
|
|
|
|
from openpilot.selfdrive.ui.ui_state import ui_state |
|
|
|
|
from openpilot.system.ui.lib.application import gui_app |
|
|
|
|
from openpilot.system.ui.widgets import Widget, DialogResult |
|
|
|
|
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog |
|
|
|
@ -12,77 +13,89 @@ class SoftwareLayout(Widget): |
|
|
|
|
super().__init__() |
|
|
|
|
|
|
|
|
|
self._params = Params() |
|
|
|
|
self._download_btn = None |
|
|
|
|
self._install_btn = None |
|
|
|
|
self._version_item = None |
|
|
|
|
items = self._init_items() |
|
|
|
|
self._scroller = Scroller(items, line_separator=True, spacing=0) |
|
|
|
|
|
|
|
|
|
def _init_items(self): |
|
|
|
|
self._version_item = text_item("Current Version", "") |
|
|
|
|
self._download_btn = button_item("Download", "CHECK", callback=self._on_download_update) |
|
|
|
|
self._install_btn = button_item("Install Update", "INSTALL", callback=self._on_install_update) |
|
|
|
|
self._current_version_item = text_item("Current Version", lambda: self._params.get("UpdaterCurrentDescription") or "") |
|
|
|
|
|
|
|
|
|
self._download_item = button_item( |
|
|
|
|
"Download", |
|
|
|
|
self._download_button_text, |
|
|
|
|
description=self._download_button_description, |
|
|
|
|
callback=self._on_download_button_clicked, |
|
|
|
|
enabled=self._download_button_enabled, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
self._install_item = button_item("Install Update", "INSTALL", callback=self._on_install_update) |
|
|
|
|
|
|
|
|
|
items = [ |
|
|
|
|
self._version_item, |
|
|
|
|
self._download_btn, |
|
|
|
|
self._install_btn, |
|
|
|
|
self._current_version_item, |
|
|
|
|
self._download_item, |
|
|
|
|
self._install_item, |
|
|
|
|
button_item("Target Branch", "SELECT", callback=self._on_select_branch), |
|
|
|
|
button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall), |
|
|
|
|
] |
|
|
|
|
return items |
|
|
|
|
|
|
|
|
|
def _render(self, rect): |
|
|
|
|
self._update_labels() |
|
|
|
|
self._scroller.render(rect) |
|
|
|
|
|
|
|
|
|
def _update_labels(self): |
|
|
|
|
# Update current version |
|
|
|
|
current_desc = self._params.get("UpdaterCurrentDescription", "Unknown") |
|
|
|
|
self._version_item.set_description(current_desc) |
|
|
|
|
# --- Download/Check logic (mirrors Qt SoftwarePanel::checkForUpdates + updateLabels) --- |
|
|
|
|
def _download_button_enabled(self) -> bool: |
|
|
|
|
if not ui_state.is_offroad(): |
|
|
|
|
return False |
|
|
|
|
updater_state = (self._params.get("UpdaterState") or "").strip() |
|
|
|
|
return updater_state == "idle" |
|
|
|
|
|
|
|
|
|
# Update download button state |
|
|
|
|
updater_state = self._params.get("UpdaterState", "idle") |
|
|
|
|
failed_count = int(self._params.get("UpdateFailedCount", "0")) |
|
|
|
|
fetch_available = self._params.get_bool("UpdaterFetchAvailable") |
|
|
|
|
update_available = self._params.get_bool("UpdateAvailable") |
|
|
|
|
def _download_button_text(self) -> str: |
|
|
|
|
updater_state = (self._params.get("UpdaterState") or "").strip() |
|
|
|
|
if updater_state != "idle": |
|
|
|
|
# Button text stays as last actionable state; show state in description |
|
|
|
|
# Default to CHECK when idle |
|
|
|
|
fetch_available = self._params.get_bool("UpdaterFetchAvailable") |
|
|
|
|
return "DOWNLOAD" if fetch_available else "CHECK" |
|
|
|
|
# idle: decide between CHECK or DOWNLOAD |
|
|
|
|
return "DOWNLOAD" if self._params.get_bool("UpdaterFetchAvailable") else "CHECK" |
|
|
|
|
|
|
|
|
|
def _download_button_description(self) -> str: |
|
|
|
|
updater_state = (self._params.get("UpdaterState") or "").strip() |
|
|
|
|
if updater_state != "idle": |
|
|
|
|
self._download_btn.set_enabled(False) |
|
|
|
|
self._download_btn.set_description(updater_state) |
|
|
|
|
else: |
|
|
|
|
if failed_count > 0: |
|
|
|
|
self._download_btn.set_description("failed to check for update") |
|
|
|
|
self._download_btn.set_text("CHECK") |
|
|
|
|
elif fetch_available: |
|
|
|
|
self._download_btn.set_description("update available") |
|
|
|
|
self._download_btn.set_text("DOWNLOAD") |
|
|
|
|
else: |
|
|
|
|
last_update = self._params.get("LastUpdateTime", "") |
|
|
|
|
if last_update: |
|
|
|
|
# TODO: Format time ago |
|
|
|
|
self._download_btn.set_description(f"up to date, last checked {last_update}") |
|
|
|
|
else: |
|
|
|
|
self._download_btn.set_description("up to date, last checked never") |
|
|
|
|
self._download_btn.set_text("CHECK") |
|
|
|
|
self._download_btn.set_enabled(True) |
|
|
|
|
|
|
|
|
|
# Update install button |
|
|
|
|
if update_available: |
|
|
|
|
new_desc = self._params.get("UpdaterNewDescription", "") |
|
|
|
|
self._install_btn.set_description(new_desc) |
|
|
|
|
self._install_btn.set_visible(True) |
|
|
|
|
else: |
|
|
|
|
self._install_btn.set_visible(False) |
|
|
|
|
|
|
|
|
|
def _on_download_update(self): |
|
|
|
|
# Check if we should start checking or stop downloading |
|
|
|
|
if self._download_btn.get_text() == "CHECK": |
|
|
|
|
# Start checking for updates |
|
|
|
|
os.system("pkill -SIGUSR1 -f system.updated.updated") |
|
|
|
|
else: |
|
|
|
|
# Stop downloading |
|
|
|
|
os.system("pkill -SIGHUP -f system.updated.updated") |
|
|
|
|
return updater_state |
|
|
|
|
|
|
|
|
|
failed = False |
|
|
|
|
try: |
|
|
|
|
failed = int(self._params.get("UpdateFailedCount") or "0") > 0 |
|
|
|
|
except Exception: |
|
|
|
|
failed = False |
|
|
|
|
|
|
|
|
|
if failed: |
|
|
|
|
return "failed to check for update" |
|
|
|
|
|
|
|
|
|
if self._params.get_bool("UpdaterFetchAvailable"): |
|
|
|
|
return "update available" |
|
|
|
|
|
|
|
|
|
last = self._params.get("LastUpdateTime") or "" |
|
|
|
|
# Keep it simple; Qt shows relative time, we can show the raw timestamp |
|
|
|
|
return f"up to date, last checked {last or 'never'}" |
|
|
|
|
|
|
|
|
|
def _on_download_button_clicked(self): |
|
|
|
|
text = self._download_button_text() |
|
|
|
|
try: |
|
|
|
|
if text == "CHECK": |
|
|
|
|
os.system("pkill -SIGUSR1 -f system.updated.updated") |
|
|
|
|
elif text == "DOWNLOAD": |
|
|
|
|
os.system("pkill -SIGHUP -f system.updated.updated") |
|
|
|
|
except Exception: |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
# --- |
|
|
|
|
|
|
|
|
|
def _on_install_update(self): |
|
|
|
|
# Mirrors Qt: set DoReboot to start install flow handled by updater |
|
|
|
|
self._params.put_bool("DoReboot", True) |
|
|
|
|
def _on_install_update(self): pass |
|
|
|
|
def _on_select_branch(self): pass |
|
|
|
|
|
|
|
|
|
def _on_uninstall(self): |
|
|
|
|
def handle_uninstall_confirmation(result): |
|
|
|
@ -91,10 +104,3 @@ class SoftwareLayout(Widget): |
|
|
|
|
|
|
|
|
|
dialog = ConfirmDialog("Are you sure you want to uninstall?", "Uninstall") |
|
|
|
|
gui_app.set_modal_overlay(dialog, callback=handle_uninstall_confirmation) |
|
|
|
|
|
|
|
|
|
def _on_install_update(self): |
|
|
|
|
# Trigger reboot to install update |
|
|
|
|
self._params.put_bool("DoReboot", True) |
|
|
|
|
|
|
|
|
|
def _on_select_branch(self): |
|
|
|
|
pass |
|
|
|
|