Keep /alive unchanged and serve a dashboard at / with status cards, latency graphs, an event timeline, and a Wi-Fi section. Background monitors on configurable intervals: TCP reachability, DNS resolution, public IP change detection, host reboot detection, Wi-Fi link quality, and Wi-Fi scan of networks in range via iw. The Wi-Fi interface is selected with WIFI_INTERFACE; the page lists visible interfaces and reports when none is available. Scan results stay in memory, other metrics go to SQLite on a mounted volume. Optional MQTT publishing with Home Assistant discovery groups all sensors under one device per host. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TdvJYoY8kc8bmBck89U2i6
87 lines
2.3 KiB
Python
87 lines
2.3 KiB
Python
import logging
|
|
import time
|
|
|
|
from flask import Flask, jsonify, render_template, request
|
|
|
|
from config import Config
|
|
from monitors import State, build_monitors
|
|
from storage import Storage
|
|
|
|
cfg = Config()
|
|
logging.basicConfig(level=cfg.log_level, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
|
|
log = logging.getLogger("app")
|
|
|
|
storage = Storage(cfg.db_path)
|
|
state = State()
|
|
app = Flask(__name__)
|
|
|
|
WIFI_KEYS = ("wifi_interface", "wifi_interfaces", "wifi_status", "wifi_error",
|
|
"wifi_link", "wifi_link_checked_at", "wifi_link_error",
|
|
"wifi_scan", "wifi_scan_error")
|
|
|
|
|
|
@app.route('/alive', methods=['GET'])
|
|
def get_healthcheck():
|
|
response = {
|
|
"alive": True
|
|
}
|
|
return jsonify(response)
|
|
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template("index.html")
|
|
|
|
|
|
@app.route('/api/status')
|
|
def api_status():
|
|
snapshot = state.snapshot()
|
|
snapshot.pop("wifi_scan", None)
|
|
snapshot["now"] = time.time()
|
|
snapshot["config"] = cfg.public()
|
|
return jsonify(snapshot)
|
|
|
|
|
|
@app.route('/api/wifi')
|
|
def api_wifi():
|
|
snapshot = state.snapshot()
|
|
result = {key: snapshot.get(key) for key in WIFI_KEYS}
|
|
result["now"] = time.time()
|
|
result["scan_interval"] = cfg.wifi_scan_interval
|
|
return jsonify(result)
|
|
|
|
|
|
@app.route('/api/events')
|
|
def api_events():
|
|
limit = min(max(request.args.get("limit", 200, type=int), 1), 1000)
|
|
return jsonify(storage.events(limit))
|
|
|
|
|
|
@app.route('/api/samples')
|
|
def api_samples():
|
|
prefix = request.args.get("prefix", "")
|
|
span = min(max(request.args.get("range", 3600, type=int), 60), cfg.sample_retention_days * 86400)
|
|
bucket = max(10, span // 400)
|
|
since = time.time() - span
|
|
return jsonify({"since": since, "bucket": bucket, "series": storage.samples_by_prefix(prefix, since, bucket)})
|
|
|
|
|
|
def start_background():
|
|
storage.add_event("service_started", "Health check service started")
|
|
storage.prune_samples(cfg.sample_retention_days)
|
|
|
|
if cfg.mqtt_host:
|
|
from hamqtt import HomeAssistantPublisher
|
|
publisher = HomeAssistantPublisher(cfg)
|
|
state.on_change(publisher.publish_state)
|
|
publisher.start()
|
|
|
|
for monitor in build_monitors(cfg, state, storage):
|
|
monitor.start()
|
|
log.info("started monitor %s every %ss", monitor.name, monitor.interval)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
start_background()
|
|
app.run(host="0.0.0.0", port=cfg.port)
|