Add local settings management

Add cellular-first ntfy delivery with WiFi fallback and safe SIM868 HTTP header framing.

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-09 21:26:15 -04:00
co-authored by Copilot
parent b77ed838bd
commit 3a6f12ba9d
15 changed files with 1118 additions and 103 deletions
+41 -4
View File
@@ -6,7 +6,15 @@ from urllib.parse import urlparse
import requests
from modules import app_dash, app_gps, app_health, app_obd, app_sql, app_video
from modules import (
app_cellular,
app_dash,
app_gps,
app_health,
app_obd,
app_sql,
app_video,
)
CHECK_INTERVAL = 60
@@ -94,9 +102,29 @@ def send_ntfy(url, token, title, message, priority="default", tags=None):
headers["Tags"] = ",".join(tags)
if token:
headers["Authorization"] = f"Bearer {token}"
body = message.encode("utf-8")
if app_cellular.is_configured():
cellular_failure = None
try:
status_code, _response_body = app_cellular.http_post(
url,
body,
headers,
content_type="text/plain; charset=utf-8",
)
if 200 <= status_code < 300:
return
cellular_failure = f"HTTP {status_code}"
except app_cellular.CellularError:
cellular_failure = "modem error"
print(
f"Cellular notification failed ({cellular_failure}); "
"trying WiFi/Ethernet."
)
response = requests.post(
url,
data=message.encode("utf-8"),
data=body,
headers=headers,
timeout=5,
)
@@ -104,6 +132,8 @@ def send_ntfy(url, token, title, message, priority="default", tags=None):
def _transport_error(error):
if isinstance(error, app_cellular.CellularError):
return "Cellular notification failed"
if isinstance(error, requests.Timeout):
return "Notification request timed out"
if isinstance(error, requests.HTTPError):
@@ -398,7 +428,10 @@ def process_conditions(conditions, config, now=None, sender=send_ntfy):
condition["priority"],
condition["tags"],
)
except requests.RequestException as error:
except (
requests.RequestException,
app_cellular.CellularError,
) as error:
state["last_error"] = _transport_error(error)
else:
state["last_sent_at"] = now
@@ -423,7 +456,10 @@ def process_conditions(conditions, config, now=None, sender=send_ntfy):
"default",
["white_check_mark"],
)
except requests.RequestException as error:
except (
requests.RequestException,
app_cellular.CellularError,
) as error:
state["last_error"] = _transport_error(error)
else:
state["recovery_pending"] = False
@@ -455,6 +491,7 @@ def _loop():
RuntimeError,
sqlite3.Error,
requests.RequestException,
app_cellular.CellularError,
) as error:
_last_run_error = str(error)
print("Alert check failed:", error)