A simple shell-script to capture a handful of common metrics and push them over MQTT to Home Assistant.
This script has been tested on recent versions of various Linux distributions (Ubuntu, Raspberry Pi OS, Armbian, Alpine, and DD-WRT) on AMD64, ARM(64) and RISC-V based devices. Given its relative simplicity, it probably works on virtually any Linux device that allows installing a handful of (generic) dependencies.
Until December 2023, this script was part of my
Home Assistant configuration-repository
β release history prior to that point is preserved in
π HISTORY.md.
Currently, the following metrics are provided:
cpu_loadβ the 1-minute load as a percentage of maximum nominal load (e.g. for a quad-core system, 100% represents a 1-minute load of 4.0)cpu_tempβ CPU temperature in degrees Celsius (auto-detected from/sys/class/thermal/thermal_zone*/tempβ omitted when none found)mem_usedβ memory in use (excluding buffers and caches) as a percentage of total available memoryfan_speedβ Fan speed in RPM (see below for caveats)uptimeβ uptime in secondsstatusβ overall status of the system (systemd-only; as reported bysystemctl is-system-running)bandwidthβ average bandwidth (receive and transmit) for individual network adapters in kbps during the monitoring interval- For wireless adapters, signal-strength is also reported (detection based on
adapter name matching the
wl*-pattern; requiresiw-binary)
- For wireless adapters, signal-strength is also reported (detection based on
adapter name matching the
rttβ average round-trip (ie, ping) times in ms to one or more hostsaptβ number of APT packages that can upgraded- This assumes a Debian(-derived) distribution; the APT-related metrics are
automatically disabled when no
apt-binary is present
- This assumes a Debian(-derived) distribution; the APT-related metrics are
automatically disabled when no
reboot_requiredβ Reports1if a system reboot is required as a result of APT package upgrades
The metrics are provided as a JSON-object in the sysmon/[device-name]/state
topic.
Additionally, the version of the running sysmon-mqtt-script is provided in
sysmon/[device-name]/version.
The current fan speed implementation is crude: It will report the highest fan
RPM in the output of lm-sensors' sensors-command for any of the supported
chips. If multiple fans are connected (a not unrealistic assumption), the
measurement randomly oscillates between multiple fans...
The currently supported chips are:
it8613-*β IT8613E-chip on DFR1142 Lite Carrier Boardpwmfan-*β Amongst others, the Raspberry Pi 5's onboard fan header
To enable the implementation, SYSMON_FAN_SPEED needs to be
explicitly set to true (and will only remain true if a supported chip is
present).
To report the status of the fan connected to the fan-header on a LattePanda Mu (more specifically, its DFR1142 Lite Carrier Board with the IT8613E-chip), a custom kernel driver needs to be compiled:
sudo apt install \
dkms \
lm-sensors
git clone [email protected]:frankcrawford/it87.git && cd it87
make clean
sudo make dkms
dkms status
echo it87 | sudo tee /etc/modules-load.d/it87.conf > /dev/null
sensorsMore details on the kernel-driver: https://github.com/frankcrawford/it87
On an Intel N100 where
intel_gpu_top is
available (and properly configured, see below) the following additional metrics
are provided:
gpu_loadβ GPU-load as a percentage of maximum nominal loadgpu_powerβ GPU power-consumption in Wattpackage_powerβ Package (CPU/GPU) power-consumption in Watt
These measurements probably work on many/most Intel-devices; they've only been tested to work on an Intel N100.
The values reported are the average of several samples taken during the preceding monitoring interval (filtering out intermittent noise). They do not represent the averages over the entire interval though...
βN.B. For data to be reported, the user running sysmon-mqtt needs to be
able to access intel_gpu_top without root-privileges. Full instructions are
available here:
https://github.com/luisbocanegra/plasma-intel-gpu-monitor#requirements
In a nutshell:
sudo setcap cap_perfmon=ep /usr/bin/intel_gpu_top
sudo sysctl kernel.perf_event_paranoid=2
echo "kernel.perf_event_paranoid = 2" |
sudo tee /etc/sysctl.d/99-perf-event-paranoid.conf > /dev/nullThe setcap setting sticks, but might not survive an update of the
intel_gpu_top-binary. The above GitHub-link provides an elegant solution to
that issue as well.
On a Raspberry Pi 5, the output of vcgencmd pmic_read_ad can be used to
approximate power consumption. The idea, and linear approximation values used
are courtesy of https://github.com/jfikar/RPi5-power:
rpi5_powerβ Approximate Raspberry Pi 5's power consumption in Watt
For the metric to be reported, ensure the user running sysmon-mqtt has access
to the vcio-device (generally achieved by adding that user to the
video-group):
sudo usermod -aG video $USER
# Furthermore, ensure the device's group is "video"
ls -la /dev/vcio
# If that's not the case, add the below udev-rule
echo 'KERNEL=="vcio", GROUP="video", MODE="0660"' |
sudo tee /etc/udev/rules.d/90-rpi-vcio.rules
sudo udevadm control --reload-rules && sudo udevadm triggerA persistent sysmon/[device-name]/connected topic is provided as an indication
of whether the script is active. Its value works as a "heartbeat": It contains
the Unix timestamp of the most recent reporting iteration, -1 while the script
is initialising, and 0 if the script was gracefully shutdown.
In case a stale timestamp is present, it may be assumed the script (or the machine its running on) has crashed / dropped from the network. Stale is best defined as three times the reporting interval. For the default configuration that would amount to 90 seconds.
When the script starts, a heartbeat of -1 is reported until the script's
second iteration; this is done because several metrics are β due to various
technical reasons β only reported from the second iteration onwards...
βN.B. The current version of the script publishes MQTT-payloads compatible with Home Assistant 2025.10 and later.
By default, the script publishes
Home Assistant discovery
messages to the homeassistant/sensors/sysmon topic.
These messages are retained. Any new instance of the script started with an
already present device-name will reuse the existing sensor-entity unique_id
values (and thus "adopt" the previous instance's sensors in Home Assistant).
This behaviour is intended to allow "fixed" sensor-entities in Home Assistant
(which can easily be customised via the GUI).
The apt-metric is presented as a Home Assistant
Update-entity. For
its "entity-picture" to show, copy the images from
π /extras/www into a folder named π sysmon-mqtt in your
Home Assistant's local webroot, and set SYSMON_HA_BASE to your Home
Assistant's base URL.
To unregister (a set of) metrics from Home Assistant, simply remove the device from the MQTT integration (under Settings).
The APT update check refreshes its status once per hour; by default it stores
this status in a temporary file. It is possible to change this behaviour by
setting the SYSMON_APT_CHECK environment variable to a filename of your choice
(eg. ~/.apt-check). In this way, APT-check's status output can be used by
other scripts.
The contents of the status file are as follows:
<# of package upgrades available>
"The following packages can be upgraded:\n\<list of upgradable packages>"
The first line is either 0 or a positive integer, the second line is empty and
the third line contains a list of upgradable packages. The third line is
JSON-encoded and (due to a Home Assistant imposed limit) restricted to a maximum
of 255-characters (prior to JSON-encoding).
While APT-check refreshes its status, the file is empty. This is done to prevent leaving stale information in case of failures. There is thus a small chance of a race-condition: To prevent this, wait until the status file has a non-zero size before continuing...
The script depends on bash,
gawk (alternative
versions of awk are not supported; you need
GNU awk), jq, and
mosquitto-clients.
Additionally, apt and iw are required to report APT status and WiFi
signal-strength respectively β missing these dependencies is handled gracefully.
When running on embedded/minimal systems (e.g. DD-WRT, or OpenWRT), apart from
the above dependencies, coreutils most likely needs to be installed. In case
this package is further split up (like on Entware),
install coreutils-mktemp, coreutils-nproc, and coreutils-timeout.
The script assumes the MQTT broker to be Mosquitto (and uses this assumption to validate the broker configuration).
Furthermore, the script relies on
MQTT-persistence to persist
unique_id values for Home Assistant sensor-entities in between restarts (of
either the script or the MQTT broker). Ensure the broker has persistence (for at
least QoS level-1 messages) enabled. Otherwise, the unique ids used in Home
Assistant will be dynamic (causing duplicate entities to be created after each
restart)...
From the shell:
./sysmon.sh [--daemon] mqtt-broker device-name [network-adapters] [rtt-hosts]--daemon(optional) β enable daemon-mode; start a watchdog to monitor the mainsysmon-mqttprocessmqtt-brokerβ hostname or IP address of the MQTT-brokerdevice-nameβ human-friendly name of the device being monitored (e.g., "My Raspberry Pi"); a low-fidelity version (my_raspberry_pi) is automatically generated and used to construct MQTT-topics and Home Assistant entity-idsnetwork-adapters(optional) β one or more network adapters to monitor as a space-delimited list (e.g.,'eth0 wlan0'; mind the quotes when specifying more than one adapter)- If the adapter's name matches
wl*, signal-strength is also reported
- If the adapter's name matches
rtt-hosts(optional) β one or more hosts to which to monitor the round-trip time as a space-delimited list (e.g.,'8.8.8.8 google.com'; mind the quotes when specifying more than one hostname)
The following optional environment variables can be used to further influence the script's behaviour:
SYSMON_HA_DISCOVER(default:true) β set tofalseto disable publishing to Home Assistant discovery topicSYSMON_HA_TOPIC(default:homeassistant) β base for the Home Assistant discovery topicSYSMON_INTERVAL(default:30) β set the interval (in seconds) at which metrics are reported- In principle, the interval can lowered all the way down to zero for real-time reporting (which will negatively impact system performance)
- When either
rtt-hosts,SYSMON_INTEL_GPU, orSYSMON_RPI5_POWERare provided, the script automatically enforces a minimum reporting interval to ensure the respective command(s) have sufficient time to complete
SYSMON_HA_BASE(default:"") β specify Home Assistant's base URL (e.g.,http://homeassistant.local) to be used as the base for local image resources (see Home Assistant discovery)SYSMON_APT(default:true) β set tofalseto disable reporting APT-related metrics (aptandreboot_required)- Automatically disabled when no
apt-binary is present
- Automatically disabled when no
SYSMON_APT_CHECK(default:Β«temporary fileΒ») β override the location of the file used to store APT-check's statusSYSMON_RTT_COUNT(default4) β number of ping-requests to send per iteration over which to average the round-trip timeSYSMON_DAEMON_LOG(default~/sysmon-mqtt.log) β file to redirect all output to when running in daemon-modeSYSMON_INTEL_GPU(defaulttrue) β useintel_gpu_topto report on additional Intel CPU metrics- This feature is automatically disabled when
intel_gpu_topis not properly configured, so unless you explicitly want to disable these metrics there's no reason to set it tofalse...
- This feature is automatically disabled when
SYSMON_FAN_SPEED(defaultfalse) β enable fan speed measurement(s) using lm-sensors'sensors-command- Currently only a handful of sensor chips are supported, see the fan speed section for more details
SYSMON_RPI5_POWER(defaulttrue) β enable (approximation of) Raspberry Pi 5 power consumption- This feature is automatically disabled when
the required
vcgencmd pmic_read_adcommand is not available, so unless you explicitly want to disable this metric there's no reason to set it tofalse..
- This feature is automatically disabled when
the required
Echo the sysmon-mqtt version and exit:
./sysmon.sh --versionAs of version 1.3.0, sysmon-mqtt includes a simple daemon to ensure the main
monitoring process keeps running (ie, is restarted if it terminates). This is
primarily intended for embedded devices running minimal Linux-distributions
lacking amenities like systemd.
When started with --daemon as its first argument, sysmon-mqtt will start
in daemon-mode and fork off a child-process to do the actual work (all arguments
after --daemon are passed directly to this child-process). Whenever the
child-process exits, it will be restarted by the daemon after waiting
SYSMON_INTERVAL seconds.
All output is redirected to π ~/sysmon-mqtt.log β this can be controlled via
the SYSMON_DAEMON_LOG environment variable.
To stop the daemon, send a SIGKILL the daemon-process.
It's possible to run the script as a systemd-service using something along the
lines of the below configuration:
π /etc/systemd/system/sysmon-mqtt.service
[Unit]
Description=Simple system monitoring over MQTT
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=120
StartLimitBurst=3
[Service]
Type=simple
# Required for the `intel_gpu_top`-implementation β as we're running a shell-
# script in the first place, it probably doesn't hurt to toggle this off (which
# restores the shell's "regular" behaviour)
IgnoreSIGPIPE=false
Restart=on-failure
RestartSec=30
# Update the below match your environment
User=[user]
ExecStart=/usr/bin/env bash /home/<user>/sysmon.sh \
mqtt-broker "Device Name" [network-adapters] [rtt-hosts]
# Optional: Provide additional environment variables
Environment="SYSMON_HA_BASE=http://homeassistant.local"
[Install]
WantedBy=multi-user.targetThis unit configuration aims to start sysmon-mqtt after the network comes
online. For this to work properly, the output of the below command should be
enabled on your system.
systemctl is-enabled systemd-networkd-wait-online.serviceReload, enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable sysmon-mqtt
sudo systemctl start sysmon-mqttTo facilitate this setup process, a setup-script (suitable for Debian(-derived)
distributions) is provided: π install.sh. Once installed,
running the script again will pull the latest version of π sysmon.sh from
GitHub.
The script requires an MQTT-broker address and "Device Name" to be provided.
Optionally, lists of network-adapters and rtt-hosts can also be passed in:
export SYSMON_HA_BASE=http://homeassistant.local
./install.sh \
mqtt-broker.local "Device Name" "eth0 wlan0" "router.local 8.8.8.8"βN.B. The installer will try to invoke sudo if/when required. If sudo
is not present on the system, you'll need to manually ensure the script runs
with root-privileges...
All environment-variables that start with SYSMON_ have their current value
automatically included in the service-definition.
If the service is already installed, the installer can be called without arguments to pull the latest version of the script:
./install.shBy default, the setup-script installs from the main-branch (i.e., it takes the
most recent release). To
install another version, set SYSMON_INSTALL_COMMIT to either a (partial)
commit-hash, or a branch name prior to running the installer:
export SYSMON_INSTALL_COMMIT=7a38346
./install.shFor the very brave, the script can be run from GitHub directly:
export SYSMON_HA_BASE=http://homeassistant.local
curl -fsSL https://github.com/thijsputman/sysmon-mqtt/raw/main/install.sh |
bash -s -- \
mqtt-broker.local "Device Name" "eth0 wlan0" "8.8.8.8 google.com"