Files

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 unavailable in 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

DISPLAY and XAUTHORITY are needed only for the idle_time sensor. If you are not using that sensor you can remove those two lines. If your display is not :0, check with echo $DISPLAY in 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.