test_failed_start_rolls_back (test_install.InstallerTests.test_failed_start_rolls_back) ... ok
test_firmware_rejected_before_stop (test_install.InstallerTests.test_firmware_rejected_before_stop) ... usage: install_webpanel.py [-h]
                           [--check | --uninstall | --reset-password | --tailscale-only]
                           [--with-tailscale]
install_webpanel.py: error: Load application firmware 0x08B5 before installing this panel.
ok
test_remote_failure_keeps_installed_local_panel (test_install.InstallerTests.test_remote_failure_keeps_installed_local_panel) ... ok
test_remote_only_does_not_reinstall_or_change_accounts (test_install.InstallerTests.test_remote_only_does_not_reinstall_or_change_accounts) ... ok
test_reset_password_preserves_accounts_without_install (test_install.InstallerTests.test_reset_password_preserves_accounts_without_install) ... ok
test_uninstall_retains_config_and_ups (test_install.InstallerTests.test_uninstall_retains_config_and_ups) ... ok
test_upgrade_retains_accounts_and_daemon (test_install.InstallerTests.test_upgrade_retains_accounts_and_daemon) ... ok
test_account_revocation (test_panel.ApiTests.test_account_revocation) ... ok
test_anonymous (test_panel.ApiTests.test_anonymous) ... ok
test_confirm_and_one_time_token (test_panel.ApiTests.test_confirm_and_one_time_token) ... ok
test_cookie_and_headers (test_panel.ApiTests.test_cookie_and_headers) ... ok
test_csrf_missing_default_and_cross_origin (test_panel.ApiTests.test_csrf_missing_default_and_cross_origin) ... ok
test_demo_no_writes (test_panel.ApiTests.test_demo_no_writes) ... ok
test_error_consumes_token (test_panel.ApiTests.test_error_consumes_token) ... ok
test_host_header_validation (test_panel.ApiTests.test_host_header_validation) ... ok
test_html_assets_and_dom_contract (test_panel.ApiTests.test_html_assets_and_dom_contract) ... /workspace/scratch/fe7b6d5ab273/work_webpanel/M2_UPS_PMH_WebPanel_V02/tests/test_panel.py:89: ResourceWarning: unclosed file <_io.BufferedReader name='/workspace/scratch/fe7b6d5ab273/work_webpanel/M2_UPS_PMH_WebPanel_V02/app/static/panel.css'>
  self.assertEqual(self.c.get('/static/panel.css').status_code,200)
ResourceWarning: Enable tracemalloc to get the object allocation traceback
/workspace/scratch/fe7b6d5ab273/work_webpanel/M2_UPS_PMH_WebPanel_V02/tests/test_panel.py:94: ResourceWarning: unclosed file <_io.BufferedReader name='/workspace/scratch/fe7b6d5ab273/work_webpanel/M2_UPS_PMH_WebPanel_V02/app/static/panel.css'>
  self.assertIn('@media(max-width:760px)',self.c.get('/static/panel.css').data.decode())
ResourceWarning: Enable tracemalloc to get the object allocation traceback
/opt/codex/runtimes/codex-primary-runtime/dependencies/python/lib/python3.12/unittest/case.py:589: ResourceWarning: unclosed file <_io.BufferedReader name='/workspace/scratch/fe7b6d5ab273/work_webpanel/M2_UPS_PMH_WebPanel_V02/app/static/panel.js'>
  if method() is not None:
ResourceWarning: Enable tracemalloc to get the object allocation traceback
ok
test_invalid_json (test_panel.ApiTests.test_invalid_json) ... ok
test_login_throttle (test_panel.ApiTests.test_login_throttle) ... ok
test_password_change_invalidates_session (test_panel.ApiTests.test_password_change_invalidates_session) ... ok
test_token_bound_to_session (test_panel.ApiTests.test_token_bound_to_session) ... ok
test_token_expires (test_panel.ApiTests.test_token_expires) ... ok
test_unknown_bool_and_large_request (test_panel.ApiTests.test_unknown_bool_and_large_request) ... ok
test_viewer_cannot_preview_or_apply (test_panel.ApiTests.test_viewer_cannot_preview_or_apply) ... ok
test_cable_idle_advanced_guards (test_panel.BoardTests.test_cable_idle_advanced_guards) ... ok
test_firmware_mismatch_stops_decoding (test_panel.BoardTests.test_firmware_mismatch_stops_decoding) ... ok
test_no_arbitrary_registers (test_panel.BoardTests.test_no_arbitrary_registers) ... ok
test_preview_is_read_only (test_panel.BoardTests.test_preview_is_read_only) ... ok
test_range_type_and_firmware_guards (test_panel.BoardTests.test_range_type_and_firmware_guards) ... ok
test_snapshot_read_only_with_sensors (test_panel.BoardTests.test_snapshot_read_only_with_sensors) ... ok
test_stale_and_recheck (test_panel.BoardTests.test_stale_and_recheck) ... ok
test_uncertain_write_not_retried (test_panel.BoardTests.test_uncertain_write_not_retried) ... ok
test_verified_write_and_mode_side_effect (test_panel.BoardTests.test_verified_write_and_mode_side_effect) ... ok
test_no_poll_before_browser_and_pause_after_idle (test_panel.PollTests.test_no_poll_before_browser_and_pause_after_idle) ... ok
test_password_verifier (test_panel.PollTests.test_password_verifier) ... ok
test_absent_check_does_not_install (test_remote.RemoteTests.test_absent_check_does_not_install) ... ok
test_connected_numeric_url (test_remote.RemoteTests.test_connected_numeric_url) ... ok
test_existing_connected_no_reauth (test_remote.RemoteTests.test_existing_connected_no_reauth) ... ok
test_existing_repo_not_overwritten (test_remote.RemoteTests.test_existing_repo_not_overwritten) ... ok
test_login_pending_preserves_local (test_remote.RemoteTests.test_login_pending_preserves_local) ... The existing heartbeat/shutdown daemon remains running. No firmware or battery-setting writes are performed.
Existing accounts retained. To set a new admin password, run this installer with --reset-password.
The existing heartbeat/shutdown daemon remains running. No firmware or battery-setting writes are performed.
Existing accounts retained. To set a new admin password, run this installer with --reset-password.

Web Panel V02 installed. Account: admin (or your retained accounts).
Open http://192.168.1.7:8080 on your computer or phone.
Check: sudo python3 install_webpanel.py --check
Set administrator password: admin (visible entry; minimum 8 characters).
Administrator password updated. Sign in as admin with the new password. No service restart is needed.
Web panel removed. UPS daemon, accounts, audit log and backups retained. Tailscale is retained separately.
The existing heartbeat/shutdown daemon remains running. No firmware or battery-setting writes are performed.
Existing accounts retained. To set a new admin password, run this installer with --reset-password.

Web Panel V02 installed. Account: admin (or your retained accounts).
Open http://192.168.1.7:8080 on your computer or phone.
Check: sudo python3 install_webpanel.py --check
Optional Internet access: sudo python3 install_webpanel.py --tailscale-only
Tailscale is not installed. Use --tailscale-only to configure remote access.
Authorize this Raspberry Pi using the Tailscale link printed below. The panel password is separate.
ok
test_unsupported_os_no_repository_writes (test_remote.RemoteTests.test_unsupported_os_no_repository_writes) ... ok
test_wrong_download_origin_rejected (test_remote.RemoteTests.test_wrong_download_origin_rejected) ... ok

----------------------------------------------------------------------
Ran 41 tests in 8.637s

OK
Remote login is pending or requires attention. Local panel remains available. Run sudo tailscale up to finish.

JavaScript: node --check passed. Python syntax: all files parse.
V02 live VPN pairing/phone test pending; see VALIDATION.txt.
