# healthcheck-container Small Flask service for remote devices (Raspberry Pi in Docker) that answers `/alive` for container monitoring and, on top of that, watches the network the device sits on: internet reachability, DNS, public IP changes, Wi-Fi link quality, Wi-Fi networks in range, and host reboots. Metrics go to SQLite and are shown on a dashboard at `/`. Optionally the same values are published to an MQTT broker with Home Assistant discovery, so each device shows up in Home Assistant as a device with sensors and no manual configuration. ## Endpoints | Path | Purpose | | --- | --- | | `/alive` | Unchanged health check, returns `{"alive": true}` | | `/` | Dashboard: status, Wi-Fi, graphs, timeline | | `/api/status` | Latest state of every monitor as JSON | | `/api/wifi` | Interface list, link details, last scan results | | `/api/events?limit=200` | Timeline events, newest first | | `/api/samples?prefix=reach.&range=3600` | Bucketed samples for graphs | ## Running ```sh docker compose up -d --build ``` Then open `http://:9999/`. Wi-Fi needs two things from Docker: - `network_mode: host`, otherwise the container has no wireless interface at all. - `cap_add: NET_ADMIN`, otherwise link and interface info work but scanning fails with a permission error that is shown on the dashboard. Without `WIFI_INTERFACE` the dashboard lists the interfaces it can see and nothing is scanned. Set the variable to one of the listed wireless interfaces and restart the container. ## Storage Samples and events are stored in SQLite at `DB_PATH` (default `/data/healthcheck.db`). The compose file mounts a named volume at `/data`, so the database survives `docker compose down`, image rebuilds and `up -d`. Only `docker compose down -v` or `docker volume rm` deletes it. A bind mount works as well, for example `- /opt/healthcheck:/data`. Samples older than `SAMPLE_RETENTION_DAYS` (default 7) are deleted hourly. Events are kept. Wi-Fi scan results are held in memory only and never written. ## Configuration All settings are environment variables. Intervals are in seconds. | Variable | Default | Meaning | | --- | --- | --- | | `PORT` | `9999` | HTTP port | | `DB_PATH` | `/data/healthcheck.db` | SQLite file | | `SAMPLE_RETENTION_DAYS` | `7` | How long graph samples are kept | | `DEVICE_NAME` | hostname | Shown on the dashboard and in Home Assistant | | `DEVICE_ID` | slug of `DEVICE_NAME` | Used in MQTT topics and unique ids | | `REACH_TARGETS` | `1.1.1.1:443,8.8.8.8:443,9.9.9.9:443` | TCP connect targets, `host:port` | | `REACH_INTERVAL` | `30` | | | `REACH_TIMEOUT` | `3` | Connect timeout per target | | `DNS_NAME` | `cloudflare.com` | Name resolved through the system resolver | | `DNS_INTERVAL` | `60` | | | `PUBLIC_IP_URLS` | ipify, ifconfig.me, icanhazip | Tried in order, first answer wins | | `PUBLIC_IP_INTERVAL` | `300` | | | `PUBLIC_IP_TIMEOUT` | `5` | | | `UPTIME_INTERVAL` | `60` | Reboot detection via `/proc/uptime` | | `WIFI_INTERFACE` | empty | Wireless interface to use, empty disables Wi-Fi | | `WIFI_LINK_INTERVAL` | `30` | Signal, bitrate, retries of the current link | | `WIFI_SCAN_INTERVAL` | `60` | Scan for networks in range | | `MQTT_HOST` | empty | Broker host, empty disables MQTT | | `MQTT_PORT` | `1883` | | | `MQTT_USERNAME` | empty | | | `MQTT_PASSWORD` | empty | | | `MQTT_DISCOVERY_PREFIX` | `homeassistant` | Must match the MQTT integration setting | | `LOG_LEVEL` | `INFO` | | ## Events on the timeline - Internet unreachable / reachable again (all targets failed, then any succeeded) - DNS resolution failed / working again - Public IP is X, Public IP changed from X to Y - Wi-Fi connected, disconnected, roamed to another BSSID - Host rebooted - Health check service started ## Home Assistant Set `MQTT_HOST` (and credentials if the broker needs them). On connect the service publishes retained discovery messages under `homeassistant//healthcheck_//config`, all pointing to one device, then publishes state to `healthcheck//state` and availability to `healthcheck//availability`. When Home Assistant restarts it announces itself on `homeassistant/status` and the service re-publishes discovery. Entities per device: - Internet, DNS, Wi-Fi (binary sensors, `connectivity` class) - Internet latency, DNS latency (ms) - Public IP - Host uptime - Wi-Fi SSID, BSSID, channel, signal (dBm), TX bitrate (Mbit/s), networks in range ## Development ```sh python3 -m unittest discover -s tests DB_PATH=./data/hc.db python3 app.py ```