Part of #201.
Problem
bluepilot/backend/bp_portal.py:3912 calls check_and_restore_power_save() with no arguments. The signature is check_and_restore_power_save(is_onroad_func) (utils/power.py:57). It became a required parameter in d929702 (2026-01-19, "Route Viewer and TICI menu (#47)") and this call site was not updated.
Every run of monitor_status() therefore raises TypeError on its first statement. The enclosing except Exception at line 3925 logs Error in status monitor: … without a traceback and carries on. That is why this has gone unnoticed.
Impact
- Power save is never restored.
enable_performance_mode() brings CPU cores 4–7 online for remux and export (bp_portal.py:682,3760, video/export.py:301,439). The only code that takes them offline again is the function that never runs. After the first remux, the big cores stay online for as long as the portal process lives, including while the car is parked.
STATUS_CHANGED WebSocket events are never sent. The broadcast (lines 3915–3922) comes after the failing call inside the same try, so clients never see onroad and offroad transitions.
Found by reading the code, not reproduced on a device. The TypeError follows directly from the signature.
Proposed change
Pass the existing is_onroad function: check_and_restore_power_save(is_onroad). One line.
Out of scope
- Isolating the power-save call from the status broadcast, so that one failing can't mask the other.
- Converting line 3926 to
logger.exception (tracked in the tracebacks spec).
Tests
New bluepilot/backend/tests/test_power.py:
- With
last_activity_time set in the past and is_onroad_func returning False, restore_power_save is called (patch it, no sysfs writes).
- With
is_onroad_func returning True, it is not called, and last_activity_time resets.
- Signature guard: call it exactly as
bp_portal does and assert that no exception is raised.
Verification
pytest -c /dev/null --rootdir=bluepilot --confcutdir=bluepilot bluepilot/backend runs as plain Python with no build. The extra flags stop pytest loading the root conftest.py, which needs compiled openpilot modules. bluepilot/ is not in pyproject.toml testpaths yet; default collection is left to the CI work.
Part of #201.
Problem
bluepilot/backend/bp_portal.py:3912callscheck_and_restore_power_save()with no arguments. The signature ischeck_and_restore_power_save(is_onroad_func)(utils/power.py:57). It became a required parameter in d929702 (2026-01-19, "Route Viewer and TICI menu (#47)") and this call site was not updated.Every run of
monitor_status()therefore raisesTypeErroron its first statement. The enclosingexcept Exceptionat line 3925 logsError in status monitor: …without a traceback and carries on. That is why this has gone unnoticed.Impact
enable_performance_mode()brings CPU cores 4–7 online for remux and export (bp_portal.py:682,3760,video/export.py:301,439). The only code that takes them offline again is the function that never runs. After the first remux, the big cores stay online for as long as the portal process lives, including while the car is parked.STATUS_CHANGEDWebSocket events are never sent. The broadcast (lines 3915–3922) comes after the failing call inside the sametry, so clients never see onroad and offroad transitions.Found by reading the code, not reproduced on a device. The
TypeErrorfollows directly from the signature.Proposed change
Pass the existing
is_onroadfunction:check_and_restore_power_save(is_onroad). One line.Out of scope
logger.exception(tracked in the tracebacks spec).Tests
New
bluepilot/backend/tests/test_power.py:last_activity_timeset in the past andis_onroad_funcreturningFalse,restore_power_saveis called (patch it, no sysfs writes).is_onroad_funcreturningTrue, it is not called, andlast_activity_timeresets.bp_portaldoes and assert that no exception is raised.Verification
pytest -c /dev/null --rootdir=bluepilot --confcutdir=bluepilot bluepilot/backendruns as plain Python with no build. The extra flags stop pytest loading the rootconftest.py, which needs compiled openpilot modules.bluepilot/is not inpyproject.tomltestpathsyet; default collection is left to the CI work.