fix: curfew boundary math, HTTP blocking, state file robustness, budget API hardening
- curfew_status: rewrite next-transition calculation to scan up to 7 days ahead; old branching showed wrong 'blocked until' for Friday nights (assumed next day = weekday schedule) and Sunday nights (off by 2 days). Verified against a minute-by-minute reference for a full week: 10080/10080. - server: ThreadingHTTPServer so slow /status (pings/ssh) no longer blocks the TURN OFF button and /budget requests. - devices: atomic state file writes (tmp + os.replace) and tolerant _load_state so a crash mid-write can't silently kill the whole system. - server: /budget takes _timer_lock (no lost updates vs the budget tick) and rejects unknown device ids instead of creating junk state entries. - status: devices carry an explicit id; web UI keys off it instead of parsing titles. - gabi_check: exact username match (no more 'gabriel' false positives). - gaja: password readable from GAJA_PASSWORD env var (hardcoded value remains as fallback). - tui: drop unused import.
This commit is contained in:
34
server.py
34
server.py
@ -2,10 +2,17 @@
|
||||
"""Simple web server: one button to turn off all kids' devices."""
|
||||
|
||||
import json
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
import config
|
||||
from devices import shutdown_all, status_all, curfew_status, _set_budget, _get_budget
|
||||
from devices import (
|
||||
DEVICES,
|
||||
_get_budget,
|
||||
_set_budget,
|
||||
_timer_lock,
|
||||
shutdown_all,
|
||||
status_all,
|
||||
)
|
||||
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
@ -46,10 +53,13 @@ class Handler(BaseHTTPRequestHandler):
|
||||
try:
|
||||
data = json.loads(body) if body else {}
|
||||
dev_id = data.get("device", "")
|
||||
if dev_id not in DEVICES:
|
||||
raise ValueError(f"unknown device: {dev_id!r}")
|
||||
delta = int(data.get("delta", 0))
|
||||
current = _get_budget(dev_id)
|
||||
new_budget = max(0, min(config.BUDGET_S, current + delta))
|
||||
_set_budget(dev_id, new_budget)
|
||||
with _timer_lock: # same lock the budget tick uses — no lost updates
|
||||
current = _get_budget(dev_id)
|
||||
new_budget = max(0, min(config.BUDGET_S, current + delta))
|
||||
_set_budget(dev_id, new_budget)
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.end_headers()
|
||||
@ -201,8 +211,8 @@ HTML = """\
|
||||
|
||||
// Device status
|
||||
document.getElementById('status').innerHTML = data.devices.map(s => {
|
||||
const devId = s.title === 'TV' ? 'tv' : s.title.includes("Gabi") ? 'gabi' : 'gaja';
|
||||
return `<div class="status-item">
|
||||
const devId = s.id;
|
||||
return `<div class="status-item" data-device="${devId}">
|
||||
<span class="center">
|
||||
<span class="icon">${s.icon}</span>
|
||||
<span class="name">${s.title}</span>
|
||||
@ -221,11 +231,9 @@ HTML = """\
|
||||
|
||||
function adjustBudget(device, delta) {
|
||||
// Update UI immediately (optimistic update)
|
||||
const items = document.querySelectorAll('.status-item');
|
||||
const devMap = {tv: 0, gabi: 1, gaja: 2};
|
||||
const idx = devMap[device];
|
||||
if (idx !== undefined && items[idx]) {
|
||||
const budgetEl = items[idx].querySelector('.budget');
|
||||
const item = document.querySelector(`.status-item[data-device="${device}"]`);
|
||||
if (item) {
|
||||
const budgetEl = item.querySelector('.budget');
|
||||
const current = parseInt(budgetEl.textContent.match(/\\d+/)?.[0] || '0');
|
||||
const maxBudget = {max_budget};
|
||||
const newBudget = Math.max(0, Math.min(maxBudget, current + Math.round(delta / 60)));
|
||||
@ -257,7 +265,7 @@ if __name__ == "__main__":
|
||||
port = int(__import__("os").environ.get("PORT", "10000"))
|
||||
start_timer()
|
||||
start_tui(config.TICK_INTERVAL)
|
||||
server = HTTPServer(("0.0.0.0", port), Handler)
|
||||
server = ThreadingHTTPServer(("0.0.0.0", port), Handler)
|
||||
print(f"🚀 Open http://localhost:{port}")
|
||||
print(f"⏱️ Budget timer running (checks every {config.TICK_INTERVAL}s, reset at 7:00 AM)")
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user