6.7 KiB
monitor-py
A Linux system monitor that publishes machine stats to MQTT with full Home Assistant MQTT auto-discovery support. Each machine running the script appears as a single device in HA with all its sensors grouped underneath it — similar to what IOTLink or HASS.Agent provide on Windows.
Features
- CPU — usage %, frequency, temperature, load averages (1m / 5m / 15m)
- GPU — usage %, temperature, VRAM used/total/%, power draw (NVIDIA via
nvidia-smi) - RAM — usage %, used / available / total GiB, swap usage
- Disk — per-partition usage %, used / free / total GiB; aggregate read/write speed
- Network — per-interface download / upload speed
- System — uptime, logged-in users
- Session idle time — time since last keyboard/mouse input (requires
xprintidle) - HA availability tracking — sensors go
unavailablein HA when the script stops - HA restart resilience — discovery configs are re-sent automatically when HA comes back online
Requirements
Python packages (all available via pip):
| Package | Purpose |
|---|---|
paho-mqtt |
MQTT client |
psutil |
System stats |
pyyaml |
Config file parsing |
Optional system tools:
| Tool | Purpose |
|---|---|
nvidia-smi |
NVIDIA GPU stats (skipped gracefully if absent) |
xprintidle |
Session idle time (requires X11; skipped gracefully if absent) |
Install Python dependencies:
pip install paho-mqtt psutil pyyaml
Install xprintidle (Arch / Arch-based):
sudo pacman -S xprintidle
Setup
1. Clone or copy the files
Place the project directory wherever you like. /opt/monitor-py is a sensible system-wide location; your home directory works equally well.
git clone https://github.com/youruser/monitor-py.git /opt/monitor-py
cd /opt/monitor-py
2. Create your config file
Copy the example config and edit it:
cp config.yaml.example config.yaml
Open config.yaml and at minimum set your broker address and credentials:
mqtt:
host: "192.168.1.x" # your MQTT broker IP or hostname
port: 1883
username: "your_user" # leave empty if broker has no auth
password: "your_password" # leave empty if broker has no auth
topic_prefix: "monitor-py" # base topic: {topic_prefix}/{node_id}/...
client_id: "" # auto-generated from hostname if left empty
monitor:
interval: 30 # seconds between publishes
device_name: "" # human-readable HA device name; defaults to hostname
node_id: "" # MQTT topic slug; defaults to hostname if empty
sensors:
cpu: true
gpu: true # NVIDIA via nvidia-smi; skipped gracefully if unavailable
ram: true
disk:
enabled: true
partitions: [] # empty = auto-detect; or list specific mounts: ["/", "/home"]
io_stats: true # enable read/write speed sensors
network:
enabled: true
interfaces: [] # empty = all non-loopback; or list specific: ["enp6s0"]
system: true # uptime, logged-in users
idle_time: true # session idle via xprintidle (requires X11 + xprintidle in PATH)
device_name vs node_id
| Field | What it affects |
|---|---|
device_name |
The display name shown in the Home Assistant device card |
node_id |
The slug used in MQTT topics and HA's internal unique IDs |
Both default to the machine's hostname if left empty. You can set them independently — for example node_id: "tony-desktop" with device_name: "Tony's Desktop".
The node_id determines the MQTT topic structure:
homeassistant/sensor/{node_id}/{node_id}_{metric}/config ← discovery
{topic_prefix}/{node_id}/state ← state payload
{topic_prefix}/{node_id}/availability ← LWT
Disk partition detection
If partitions is empty, the script auto-detects all physical, non-virtual mounts (excludes tmpfs, squashfs, loop devices, etc.). On machines with many mounts (e.g. Windows drives mounted under /mnt/) it is cleaner to list only the partitions you care about:
disk:
partitions: ["/", "/home", "/mnt/data"]
3. Test manually
Run the script directly to confirm it connects and publishes:
python3 monitor.py
You should see log output like:
2026-05-04 12:00:00 [INFO] Connecting to 192.168.1.x:1883 as device 'tony-asus' (interval=30s)...
2026-05-04 12:00:00 [INFO] Connected to MQTT broker.
2026-05-04 12:00:00 [INFO] Published 42 discovery configs.
Open Home Assistant → Settings → Devices & Services → MQTT — your machine should appear as a new device with all sensors populated.
To use a different config file path:
python3 monitor.py /path/to/my-config.yaml
4. Install as a systemd service
Copy the example service file and edit it:
cp monitor-py.service.example monitor-py.service
Replace the placeholder values in monitor-py.service:
User=YOUR_USERNAME
WorkingDirectory=/path/to/monitor-py
ExecStart=/usr/bin/python3 /path/to/monitor-py/monitor.py
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/YOUR_USERNAME/.Xauthority
DISPLAYandXAUTHORITYare needed only for theidle_timesensor. If you are not using that sensor you can remove those two lines. If your display is not:0, check withecho $DISPLAYin a terminal.
Install and enable the service:
sudo cp monitor-py.service /etc/systemd/system/monitor-py.service
sudo systemctl daemon-reload
sudo systemctl enable --now monitor-py.service
Check the status and logs:
systemctl status monitor-py
journalctl -u monitor-py -f
Verifying discovery on the broker
To watch all discovery payloads as they arrive:
mosquitto_sub -h 192.168.1.x -u your_user -P your_password \
-t 'homeassistant/sensor/{node_id}/#' -v
Cleaning up stale entities
If you change a machine's node_id, or remove the script from a machine entirely, the old retained discovery configs will remain on the broker and leave ghost entities in HA. Delete them with an empty retained publish to each config topic.
Bulk-delete all discovery configs for a given node_id:
mosquitto_sub -h 192.168.1.x -u your_user -P your_password \
-t 'homeassistant/sensor/{node_id}/#' --retained-only -C 999 -F '%t' 2>/dev/null \
| xargs -I{} mosquitto_pub -h 192.168.1.x -u your_user -P your_password \
-t '{}' -n -r
Running on multiple machines
Deploy the project directory to each machine, create a config.yaml on each, and start the service. Each machine will appear as its own device in HA based on its node_id (hostname by default). No other configuration is needed.