Files
healthcheck-container/app.py
T
Milan PandurovandClaude Fable 5.1 7206e0c4db Add network monitoring, dashboard, and Home Assistant MQTT publishing
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
2026-09-17 15:42:09 +02:00

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)