Automatic updates

This commit is contained in:
James
2026-01-03 11:53:11 -07:00
parent 5cdc5b7991
commit 48e1f14229
7 changed files with 88 additions and 25 deletions
+39
View File
@@ -71,3 +71,42 @@ def update_boot_logo(frogpilot=False, stock=False):
run_cmd(["sudo", "mount", "-o", "remount,rw", "/"], "Successfully remounted / as read-write", "Failed to remount /")
run_cmd(["sudo", "cp", target_logo, boot_logo_location], "Successfully replaced boot logo", "Failed to replace boot logo")
run_cmd(["sudo", "mount", "-o", f"remount,{mount_options}", "/"], "Successfully restored / mount options", "Failed to restore / mount options")
def update_openpilot(thread_manager, params):
def update_available():
run_cmd(["pkill", "-SIGUSR1", "-f", "system.updated.updated"], "Checking for updates...", "Failed to check for update...", report=False)
while params.get("UpdaterState") != "checking...":
time.sleep(1)
while params.get("UpdaterState") == "checking...":
time.sleep(1)
if not params.get_bool("UpdaterFetchAvailable"):
return False
while params.get_bool("IsOnroad") or thread_manager.is_thread_alive("lock_doors"):
time.sleep(60)
run_cmd(["pkill", "-SIGHUP", "-f", "system.updated.updated"], "Update available, downloading...", "Failed to download update...", report=False)
while not params.get_bool("UpdateAvailable"):
time.sleep(60)
return True
if params.get("UpdaterState") != "idle":
return
while params.get_bool("IsOnroad") or thread_manager.is_thread_alive("lock_doors"):
time.sleep(60)
if not update_available():
return
while True:
if not update_available():
break
HARDWARE.reboot()
+5
View File
@@ -9,6 +9,7 @@ from openpilot.common.realtime import DT_MDL, Priority, Ratekeeper, config_realt
from openpilot.common.time_helpers import system_time_valid
from openpilot.frogpilot.common.frogpilot_backups import backup_toggles
from openpilot.frogpilot.common.frogpilot_functions import update_openpilot
from openpilot.frogpilot.common.frogpilot_utilities import ThreadManager, is_url_pingable
from openpilot.frogpilot.common.frogpilot_variables import FrogPilotVariables
from openpilot.frogpilot.controls.frogpilot_planner import FrogPilotPlanner
@@ -31,6 +32,9 @@ def update_checks(now, thread_manager, params, params_memory, frogpilot_toggles,
while not (is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com")):
time.sleep(60)
if frogpilot_toggles.automatic_updates:
thread_manager.run_with_lock(update_openpilot, (thread_manager, params))
time.sleep(1)
def update_toggles(frogpilot_variables, started, thread_manager, time_validated, params):
@@ -102,6 +106,7 @@ def frogpilot_thread():
if params_memory.get_bool("FrogPilotTogglesUpdated"):
frogpilot_toggles = update_toggles(frogpilot_variables, started, thread_manager, time_validated, params)
run_update_checks |= params_memory.get_bool("ManualUpdateInitiated")
run_update_checks |= now.second == 0 and (now.minute % 60 == 0)
run_update_checks &= time_validated
+1
View File
@@ -7,6 +7,7 @@
struct FrogPilotUIScene {
bool always_on_lateral_active;
bool downloading_update;
bool enabled;
bool frogpilot_panel_active;
bool online;
+21 -5
View File
@@ -21,7 +21,7 @@ void SoftwarePanel::checkForUpdates() {
}
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off."));
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off or in park."));
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
addItem(onroadLbl);
@@ -29,6 +29,12 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
versionLbl = new LabelControl(tr("Current Version"), "");
addItem(versionLbl);
// automatic updates toggle
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
tr("Automatically update FrogPilot when the vehicle is parked with an active internet connection."), "");
automaticUpdatesToggle->setVisible(params.getBool("IsReleaseBranch"));
addItem(automaticUpdatesToggle);
// download update btn
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
connect(downloadBtn, &ButtonControl::clicked, [=]() {
@@ -38,6 +44,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
} else {
std::system("pkill -SIGHUP -f system.updated.updated");
}
frogpilotUIState()->params_memory.putBool("ManualUpdateInitiated", true);
});
addItem(downloadBtn);
@@ -110,6 +117,10 @@ void SoftwarePanel::showEvent(QShowEvent *event) {
// FrogPilot variables
FrogPilotUIState &fs = *frogpilotUIState();
FrogPilotUIScene &frogpilot_scene = fs.frogpilot_scene;
if (frogpilot_scene.online && params.get("UpdaterState") == "idle") {
checkForUpdates();
}
}
void SoftwarePanel::updateLabels() {
@@ -117,6 +128,8 @@ void SoftwarePanel::updateLabels() {
FrogPilotUIState &fs = *frogpilotUIState();
FrogPilotUIScene &frogpilot_scene = fs.frogpilot_scene;
bool parked = frogpilot_scene.parked;
// add these back in case the files got removed
fs_watch->addParam("LastUpdateTime");
fs_watch->addParam("UpdateFailedCount");
@@ -125,12 +138,13 @@ void SoftwarePanel::updateLabels() {
if (!isVisible()) {
// FrogPilot variables
frogpilot_scene.downloading_update = false;
return;
}
// updater only runs offroad
onroadLbl->setVisible(is_onroad);
downloadBtn->setVisible(!is_onroad);
// updater only runs offroad or when parked
onroadLbl->setVisible(is_onroad && !parked);
downloadBtn->setVisible(!is_onroad || parked);
// download update
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
@@ -140,6 +154,7 @@ void SoftwarePanel::updateLabels() {
downloadBtn->setValue(updater_state);
// FrogPilot variables
frogpilot_scene.downloading_update = true;
} else {
if (failed) {
downloadBtn->setText(tr("CHECK"));
@@ -159,6 +174,7 @@ void SoftwarePanel::updateLabels() {
downloadBtn->setEnabled(true);
// FrogPilot variables
frogpilot_scene.downloading_update = false;
}
targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch")));
@@ -166,7 +182,7 @@ void SoftwarePanel::updateLabels() {
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable"));
installBtn->setVisible((!is_onroad || parked) && params.getBool("UpdateAvailable"));
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
+1 -1
View File
@@ -154,7 +154,7 @@ void UIState::update() {
FrogPilotUIScene &frogpilot_scene = fs->frogpilot_scene;
QJsonObject &frogpilot_toggles = frogpilot_scene.frogpilot_toggles;
if (frogpilot_scene.frogpilot_panel_active) {
if (frogpilot_scene.downloading_update || frogpilot_scene.frogpilot_panel_active) {
device()->resetInteractiveTimeout();
}
+1 -1
View File
@@ -106,7 +106,7 @@ procs = [
PythonProcess("radard", "selfdrive.controls.radard", only_onroad),
PythonProcess("hardwared", "system.hardware.hardwared", always_run),
PythonProcess("tombstoned", "system.tombstoned", always_run, enabled=not PC),
PythonProcess("updated", "system.updated.updated", only_offroad, enabled=not PC),
PythonProcess("updated", "system.updated.updated", always_run, enabled=not PC),
PythonProcess("uploader", "system.loggerd.uploader", always_run),
PythonProcess("statsd", "system.statsd", always_run),
PythonProcess("feedbackd", "selfdrive.ui.feedback.feedbackd", only_onroad),
+20 -18
View File
@@ -422,10 +422,6 @@ class Updater:
def main() -> None:
params = Params()
if params.get_bool("DisableUpdates"):
cloudlog.warning("updates are disabled by the DisableUpdates param")
exit(0)
with open(LOCK_FILE, 'w') as ov_lock_fd:
try:
fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
@@ -467,6 +463,9 @@ def main() -> None:
# FrogPilot variables
frogpilot_toggles = get_frogpilot_toggles()
manual_update_requested = params_memory.get_bool("ManualUpdateInitiated")
params_memory.remove("ManualUpdateInitiated")
# Attempt an update
exception = None
try:
@@ -483,21 +482,24 @@ def main() -> None:
update_failed_count += 1
# check for update
params.put("UpdaterState", "checking...")
updater.check_for_update()
if manual_update_requested or params.get_bool("IsOffroad"):
# check for update
params.put("UpdaterState", "checking...")
updater.check_for_update()
# download update
last_fetch = params.get("UpdaterLastFetchTime")
timed_out = last_fetch is None or (datetime.datetime.now(datetime.UTC).replace(tzinfo=None) - last_fetch > datetime.timedelta(days=3))
user_requested_fetch = wait_helper.user_request == UserRequest.FETCH
if params.get_bool("NetworkMetered") and not timed_out and not user_requested_fetch:
cloudlog.info("skipping fetch, connection metered")
elif wait_helper.user_request == UserRequest.CHECK:
cloudlog.info("skipping fetch, only checking")
# download update
last_fetch = params.get("UpdaterLastFetchTime")
timed_out = last_fetch is None or (datetime.datetime.now(datetime.UTC).replace(tzinfo=None) - last_fetch > datetime.timedelta(days=3))
user_requested_fetch = wait_helper.user_request == UserRequest.FETCH
if params.get_bool("NetworkMetered") and not timed_out and not user_requested_fetch:
cloudlog.info("skipping fetch, connection metered")
elif wait_helper.user_request == UserRequest.CHECK:
cloudlog.info("skipping fetch, only checking")
else:
updater.fetch_update()
write_time_to_param(params, "UpdaterLastFetchTime")
else:
updater.fetch_update()
write_time_to_param(params, "UpdaterLastFetchTime")
cloudlog.info("skipping fetch, vehicle is onroad")
update_failed_count = 0
except subprocess.CalledProcessError as e:
cloudlog.event(
@@ -522,7 +524,7 @@ def main() -> None:
# infrequent attempts if we successfully updated recently
wait_helper.user_request = UserRequest.NONE
wait_helper.sleep(5*60 if update_failed_count > 0 else 1.5*60*60)
wait_helper.sleep(60*60*24*365*100)
if __name__ == "__main__":