This commit is contained in:
firestar5683
2026-03-30 01:36:05 -05:00
parent bccffecef3
commit 675227a8eb
3 changed files with 173 additions and 11 deletions
@@ -13,7 +13,7 @@
}
.download-speed-limits-button + .download-speed-limits-button {
margin-top: var(--padding-sm);
margin-top: 0;
}
.download-speed-limits-button:hover {
@@ -22,25 +22,39 @@
transform: var(--hover-scale-sm);
}
.download-speed-limits-button-wrapper {
height: calc(100% + 20px);
position: relative;
align-items: stretch;
display: flex;
flex-direction: column;
gap: var(--padding-sm);
width: 100%;
}
.download-speed-limits-link {
color: var(--text-color);
font-size: var(--font-size-sm);
left: 50%;
margin-top: var(--border-radius-sm);
position: absolute;
text-align: center;
text-decoration: underline;
top: 100%;
transform: translateX(-50%);
white-space: nowrap;
}
.download-speed-limits-note {
color: var(--secondary-fg);
font-size: var(--font-size-sm);
margin: 0;
text-align: center;
}
.download-speed-limits-status {
color: var(--text-color);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-demi-bold);
margin: 0;
text-align: center;
}
.download-speed-limits-text {
color: var(--text-color);
margin-bottom: 0;
text-align: center;
}
@@ -1,4 +1,58 @@
import { html } from "/assets/vendor/arrow-core.js"
import { html, reactive } from "/assets/vendor/arrow-core.js"
const state = reactive({
canProcessNow: false,
fetched: false,
loading: true,
processing: false,
reason: "",
status: "Checking...",
submitting: false,
})
let pollTimer = null
async function fetchStatus() {
try {
const response = await fetch("/api/speed_limits/status")
const result = await response.json()
state.canProcessNow = Boolean(result.canProcessNow)
state.processing = Boolean(result.processing)
state.reason = result.reason || ""
state.status = result.status || "Idle"
} catch (error) {
state.canProcessNow = false
state.processing = false
state.reason = "Failed to load processor status."
state.status = "Unavailable"
}
state.loading = false
}
async function handleProcessNow() {
if (state.submitting || state.processing || !state.canProcessNow) {
return
}
state.submitting = true
try {
const response = await fetch("/api/speed_limits/process", { method: "POST" })
const result = await response.json()
if (response.ok) {
showSnackbar(result.message || "Speed limit processing started.")
} else {
showSnackbar(result.error || "Failed to start speed limit processing.", "error")
}
} catch (error) {
showSnackbar("Failed to start speed limit processing.", "error")
}
state.submitting = false
fetchStatus()
}
export function SpeedLimits() {
function handleDownload() {
@@ -10,16 +64,38 @@ export function SpeedLimits() {
showSnackbar("Download started...")
}
if (!state.fetched) {
state.fetched = true
fetchStatus()
if (pollTimer === null) {
pollTimer = setInterval(fetchStatus, 3000)
}
}
return html`
<div class="download-speed-limits-wrapper">
<section class="download-speed-limits-widget">
<div class="download-speed-limits-title">Download Speed Limits</div>
<p class="download-speed-limits-text">
Download speed limit data collected using "Speed Limit Filler".
Enable "Speed Limit Filler" on the device, drive to collect data, then process and download it here when parked.
</p>
<p class="download-speed-limits-status">
${() => state.loading ? "Checking processor status..." : `Processor Status: ${state.status}`}
</p>
${() => !state.loading && state.reason && state.reason !== state.status ? html`
<p class="download-speed-limits-note">${state.reason}</p>
` : ""}
<div class="download-speed-limits-button-wrapper">
<button class="download-speed-limits-button" @click="${handleDownload}">Download</button>
<a class="download-speed-limits-link" href="https://SpeedLimitFiller.starpilot.download" target="_blank">
<button
class="download-speed-limits-button"
@click="${handleProcessNow}"
disabled="${() => state.submitting || state.processing || !state.canProcessNow}"
>
${() => state.processing || state.submitting ? "Processing..." : "Process Now"}
</button>
<a class="download-speed-limits-link" href="https://nerf.077769.xyz/" target="_blank" rel="noopener noreferrer">
Submit speed limits here
</a>
</div>
+72
View File
@@ -3839,6 +3839,78 @@ def setup(app):
buffer.seek(0)
return send_file(buffer, as_attachment=True, download_name="speed_limits.json", mimetype="application/json")
def _speed_limits_status_payload():
status = params_memory.get("UpdateSpeedLimitsStatus", encoding="utf-8") or ""
processing = bool(status and status != "Completed!")
enabled = params.get_bool("SpeedLimitFiller")
is_onroad = params.get_bool("IsOnroad")
time_valid = system_time_valid()
network_connected = True
try:
sm = messaging.SubMaster(["deviceState"], poll="deviceState")
sm.update(0)
network_connected = sm["deviceState"].networkType != log.DeviceState.NetworkType.none
except Exception:
pass
overpass_requests = {}
try:
overpass_requests = json.loads(params.get("OverpassRequests", encoding="utf-8") or "{}")
except Exception:
pass
current_day = datetime.now(timezone.utc).day
saved_day = int(overpass_requests.get("day", current_day) or current_day)
total_requests = int(overpass_requests.get("total_requests", 0) or 0)
max_requests = int(overpass_requests.get("max_requests", 10000) or 10000)
if saved_day != current_day:
total_requests = 0
api_limit_hit = total_requests >= max_requests
reason = ""
if not enabled:
reason = "Enable Speed Limit Filler on the device first."
elif processing:
reason = status
elif is_onroad:
reason = "Processing is only available while parked."
elif not time_valid:
reason = "System time is not valid yet."
elif not network_connected:
reason = "Connect the device to the internet first."
elif api_limit_hit:
reason = "Today's Overpass API request limit has been reached."
return {
"apiLimitHit": api_limit_hit,
"canProcessNow": enabled and not processing and not is_onroad and time_valid and network_connected and not api_limit_hit,
"enabled": enabled,
"isOnroad": is_onroad,
"networkConnected": network_connected,
"processing": processing,
"reason": reason,
"status": status or "Idle",
"timeValid": time_valid,
"totalRequests": total_requests,
"maxRequests": max_requests,
}
@app.route("/api/speed_limits/status", methods=["GET"])
def speed_limits_status():
return jsonify(_speed_limits_status_payload()), 200
@app.route("/api/speed_limits/process", methods=["POST"])
def process_speed_limits():
payload = _speed_limits_status_payload()
if not payload["canProcessNow"]:
return jsonify({"error": payload["reason"] or "Speed limit processing is unavailable right now."}), 409
params_memory.put("UpdateSpeedLimitsStatus", "Calculating...")
params_memory.put_bool("UpdateSpeedLimits", True)
return jsonify({"message": "Speed limit processing started.", "status": "Calculating..."}), 202
@app.route("/api/stats", methods=["GET"])
def get_stats():
build_metadata = get_build_metadata()