This commit is contained in:
firestar5683
2026-08-31 14:10:18 -05:00
parent 9f2f79a652
commit 6f49c1cfc9
10 changed files with 38 additions and 17 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ rerun with `--allow-runtime-changes` only after Firestar approves it.
The default resources checkout is `~/StarPilot-Resources` on branch `Models`.
It must be clean and synchronized before starting. The default Hugging Face
bucket is `firestar4430/StarPilot-Resources`; authenticate with `hf auth login`
bucket is `StarPilot-Driving/StarPilot-Resources`; authenticate with `hf auth login`
before use. Use `--dry-run` to test parsing and the runtime scan without
touching the device or either resource store.
+1 -1
View File
@@ -29,7 +29,7 @@ from pathlib import Path
OPENPILOT_REPO = "commaai/openpilot"
RESOURCES_REPO = os.environ.get("STARPILOT_RESOURCES_REPO", "firestar5683/StarPilot-Resources")
HF_BUCKET = os.environ.get("STARPILOT_HF_BUCKET", "firestar4430/StarPilot-Resources")
HF_BUCKET = os.environ.get("STARPILOT_HF_BUCKET", "StarPilot-Driving/StarPilot-Resources")
RESOURCE_BRANCH = "Models"
MANIFEST_VERSION = "v24"
DEFAULT_BEHAVIOR_VERSION = "v16"
@@ -7,7 +7,7 @@ from datetime import datetime, timezone
from openpilot.starpilot.common.starpilot_utilities import delete_file, is_url_pingable
from openpilot.starpilot.common.starpilot_variables import RESOURCES_REPO
HF_BUCKET = os.getenv("STARPILOT_HF_BUCKET", "firestar4430/StarPilot-Resources")
HF_BUCKET = os.getenv("STARPILOT_HF_BUCKET", "StarPilot-Driving/StarPilot-Resources")
HF_BUCKET_URL = f"https://huggingface.co/buckets/{HF_BUCKET}/resolve"
GITHUB_URL = f"https://raw.githubusercontent.com/{RESOURCES_REPO}"
+2 -2
View File
@@ -99,7 +99,8 @@ class BluetoothController:
prompt["name"] = device["name"] if device else self._pairing_address
except Exception as error:
result["error"] = str(error)
self._reset_client()
if not self._pairing_address:
self._reset_client()
return result
def _require_offroad(self, command: str) -> None:
@@ -166,7 +167,6 @@ class BluetoothController:
finally:
self._reset_client()
self._radio.stop()
self.params.remove("BluetoothAudioAddress")
self.params.put_bool("BluetoothEnabled", False)
self._scan_deadline = 0.0
elif command == "start_scan":
+2 -3
View File
@@ -17,7 +17,7 @@ A2DP_SINK_UUID = "0000110b-0000-1000-8000-00805f9b34fb"
HID_UUID = "00001124-0000-1000-8000-00805f9b34fb"
HOG_UUID = "00001812-0000-1000-8000-00805f9b34fb"
COMMAND_TIMEOUTS = {
"set_power": 55.0,
"set_power": 90.0,
"start_scan": 20.0,
"stop_scan": 20.0,
"connect": 35.0,
@@ -151,7 +151,6 @@ class _DesktopFakeBluetooth:
self._enabled = bool(payload.get("enabled", False))
self._discovering = False
if not self._enabled:
self._selected_audio = ""
self._devices = tuple(replace(device, connected=False) for device in self._devices)
elif command == "start_scan":
self._require_enabled()
@@ -265,7 +264,7 @@ class BluetoothClient:
bootstrap = enabled and not os.path.exists(self.socket_path)
if bootstrap:
params.put_bool("BluetoothEnabled", True)
deadline = time.monotonic() + max(self.timeout, 10.0)
deadline = time.monotonic() + max(self.timeout, 45.0)
while not os.path.exists(self.socket_path):
if time.monotonic() >= deadline:
params.put_bool("BluetoothEnabled", False)
@@ -201,10 +201,12 @@ def test_desktop_fake_bluetooth_is_stateful_and_interactive(monkeypatch, tmp_pat
client.set_power(False)
disabled = client.status()
assert not disabled.enabled and not disabled.powered and not disabled.discovering
assert disabled.selected_audio == speaker.address
with pytest.raises(RuntimeError, match="Enable Bluetooth"):
client.start_scan()
client.set_power(True)
assert client.status().enabled
enabled = client.status()
assert enabled.enabled and enabled.selected_audio == speaker.address
def test_desktop_fake_bluetooth_cannot_activate_on_device(monkeypatch, tmp_path):
@@ -265,6 +267,16 @@ def test_power_pair_audio_and_offroad_enforcement():
assert not params.get_bool("BluetoothEnabled") and radio.stops == 1 and clients[0].closed
def test_power_off_preserves_saved_audio_selection():
params = FakeParams(IsOffroad=True, BluetoothEnabled=False, BluetoothAudioAddress="00:11:22:33:44:55")
controller = BluetoothController(params, FakeBlueZ, FakeRadio())
controller.handle({"command": "set_power", "enabled": True})
controller.handle({"command": "set_power", "enabled": False})
assert params.get("BluetoothAudioAddress") == "00:11:22:33:44:55"
def test_status_does_not_restart_radio_during_disable():
params = FakeParams(IsOffroad=True, BluetoothEnabled=True)
radio = BlockingStopRadio()
@@ -2,7 +2,7 @@ import { html, reactive } from "/assets/vendor/arrow-core.js"
import { createBrowserHistory, createRouter } from "/assets/vendor/remix-router-1.3.1.js"
import { hideSidebar } from "/assets/js/utils.js"
import { DeviceSettings } from "/assets/components/tools/device_settings.js?v=favorite-c4-hint-1"
import { Bluetooth } from "/assets/components/tools/bluetooth.js?v=bluetooth-5"
import { Bluetooth } from "/assets/components/tools/bluetooth.js?v=bluetooth-6"
import { WheelControls } from "/assets/components/tools/wheel_controls.js?v=controllers-2"
import { ErrorLogs } from "/assets/components/tools/error_logs.js"
import { VehicleFeatures } from "/assets/components/tools/vehicle_features.js"
@@ -4,6 +4,7 @@ import { galaxyPath } from "/assets/js/utils.js"
const state = reactive({
loading: true,
busy: "",
powerTarget: null,
available: false,
enabled: false,
powered: false,
@@ -38,7 +39,8 @@ function pollDelay() {
function schedulePoll(delay = pollDelay()) {
if (pollTimer !== null) clearTimeout(pollTimer)
pollTimer = setTimeout(async () => {
if (bluetoothPageActive()) await refresh()
pollTimer = null
if (bluetoothPageActive() && state.busy !== "power") await refresh()
schedulePoll()
}, delay)
}
@@ -69,6 +71,7 @@ function startAudioTestCountdown(address, delayMs, requestStartedAt) {
async function request(operation, body = {}) {
const requestStartedAt = performance.now()
state.busy = operation
if (operation === "power") state.powerTarget = !!body.enabled
schedulePoll(250)
try {
const response = await fetch(galaxyPath(`/api/bluetooth/${operation}`), {
@@ -88,6 +91,7 @@ async function request(operation, body = {}) {
state.error = error?.message || "Bluetooth operation failed"
} finally {
state.busy = ""
if (operation === "power") state.powerTarget = null
schedulePoll(250)
}
}
@@ -155,7 +159,8 @@ async function refresh() {
}
function initialize() {
if (!initialized) initialized = true
if (initialized) return
initialized = true
schedulePoll(0)
}
@@ -203,7 +208,7 @@ function deviceActions(device) {
const pairing = () => isPairing(device)
return html`
<div class="bluetoothActions">
${!device.paired ? html`
${!device.paired && !device.connected ? html`
<button disabled="${() => !state.offroad || !!state.busy || pairing()}" @click="${() => request("pair", { address: device.address })}">
${() => pairing() ? "Pairing…" : "Pair"}
</button>
@@ -222,10 +227,10 @@ function deviceActions(device) {
</button>
` : ""}
` : ""}
<button class="bluetoothIconButton bluetoothForgetButton" title="Forget device" aria-label="Forget ${device.name}"
${device.paired ? html`<button class="bluetoothIconButton bluetoothForgetButton" title="Forget device" aria-label="Forget ${device.name}"
disabled="${() => !state.offroad || !!state.busy}" @click="${() => {
if (window.confirm(`Forget ${device.name}?`)) request("forget", { address: device.address })
}}"><i class="bi bi-trash3" aria-hidden="true"></i></button>
}}"><i class="bi bi-trash3" aria-hidden="true"></i></button>` : ""}
`}
</div>
`
@@ -279,7 +284,9 @@ export function Bluetooth() {
<label class="bluetoothSwitch">
<input type="checkbox" checked="${() => state.enabled}" disabled="${() => !state.available || !state.offroad || !!state.busy}"
@change="${(event) => request("power", { enabled: event.target.checked })}" />
<span>${() => state.enabled ? "On" : "Off"}</span>
<span>${() => state.busy === "power"
? `Turning Bluetooth ${state.powerTarget ? "on" : "off"}`
: state.enabled ? "On" : "Off"}</span>
</label>
</div>
@@ -42,6 +42,9 @@ def test_bluetooth_actions_use_reactive_disabled_bindings():
assert "bluetoothForgetButton" in source
assert "bi-trash3" in source
assert "state.pairingAddress" in source
assert "state.busy !== \"power\"" in source
assert "Turning Bluetooth" in source
assert "!device.paired && !device.connected" in source
assert 'galaxyPath("/bluetooth")' in source
assert 'window.location.pathname === "/bluetooth"' not in source
assert "schedulePoll(250)" in source
+1 -1
View File
@@ -4969,7 +4969,7 @@ def setup(app):
@app.route("/api/bluetooth/status", methods=["GET"])
def bluetooth_status():
try:
status = BluetoothClient(timeout=3.0).status()
status = BluetoothClient(timeout=10.0).status()
return jsonify(BluetoothClient.serialize_status(status)), 200
except Exception as error:
return jsonify({