Initial commit
This commit is contained in:
25
README.md
Normal file
25
README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# MQTT bridge for the KDE System Monitor (ksysguard)
|
||||
|
||||
A short Python script for monitoring arbitrary MQTT topics via [ksysguard](https://userbase.kde.org/KSysGuard).
|
||||
|
||||
# Install
|
||||
|
||||
Save the script somewhere sensible in your computer and give it executable permissions. Root is not required.
|
||||
|
||||
# Configure
|
||||
|
||||
Take a look the [example configuration file](./example-config.yaml) and use it as the basis for your own configuration.
|
||||
|
||||
# Use
|
||||
|
||||
Open ksysguard and go to `File` → `Monitor Remote Machine…`. In the dialog box, enter an arbitrary name under “Host”, select “custom command” and in the command edit box enter the full path to the script in your computer, followed by `-c` (or `--config`) and the full path to your configuration file. For instance:
|
||||
|
||||
```
|
||||
/home/navlost/bin/ksysguard-sensor-mqtt.py -c /home/navlost/.config/ksysguard-sensor-mqtt.yaml
|
||||
```
|
||||
|
||||

|
||||
|
||||
Ensure that the sensor browser on the right-hand side of the ksysguard client is visible, if not, slide it into view by clicking near (but not on) the right-hand edge of the window and dragging left. You should now see your configured sensors and be able to drag them into a tab.
|
||||
|
||||

|
||||
BIN
doc/connect-host.png
Normal file
BIN
doc/connect-host.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
BIN
doc/ksysguard-window.png
Normal file
BIN
doc/ksysguard-window.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
22
example-config.yaml
Normal file
22
example-config.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
mqtt:
|
||||
host: mqtt.example.com
|
||||
username: yourusername # Optional
|
||||
password: yourpassword # Optional
|
||||
subscriptions:
|
||||
-
|
||||
topic: "/things/living_room/sensors/temperature/01"
|
||||
monitor: "Living room/temperature"
|
||||
type: float
|
||||
description:
|
||||
name: "Living room temperature"
|
||||
units: "° C"
|
||||
-
|
||||
topic: "/things/living_room/sensors/humidity/01"
|
||||
monitor: "Living room/humidity"
|
||||
type: integer
|
||||
description:
|
||||
name: "Living room relative humidity"
|
||||
min: 0
|
||||
max: 100
|
||||
units: "%"
|
||||
148
ksysguard-sensor-mqtt.py
Executable file
148
ksysguard-sensor-mqtt.py
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
import paho.mqtt.client as mqtt
|
||||
from random import random
|
||||
|
||||
global config
|
||||
|
||||
config_file_path = os.path.dirname(os.path.abspath(__file__))+"/ksysguard-sensor-mqtt.yaml"
|
||||
|
||||
|
||||
# Get and parse command line arguments
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("-c", "--config", required=False, default=None, help="path to the configuration file")
|
||||
args = vars(ap.parse_args())
|
||||
|
||||
if args["config"]:
|
||||
config_file_path = args["config"]
|
||||
|
||||
|
||||
# Read configuration
|
||||
|
||||
with open(config_file_path, "r") as config_file:
|
||||
try:
|
||||
config = yaml.safe_load(config_file)
|
||||
except yaml.YAMLError as err:
|
||||
print("Could not load configuration file")
|
||||
print(err)
|
||||
|
||||
|
||||
# We cache the values received via MQTT, so that they are available whenever
|
||||
# requested by the ksysguard client.
|
||||
|
||||
global values
|
||||
values = dict()
|
||||
|
||||
for subscription in config['mqtt']['subscriptions']:
|
||||
values[subscription['topic']] = None
|
||||
|
||||
|
||||
# Given a monitor name, returns its data if the name matches the list
|
||||
# of monitors in the configuration. If not, returns None.
|
||||
|
||||
def have_monitor(monitor):
|
||||
return next(iter([subscription for subscription in config['mqtt']['subscriptions'] if subscription['monitor'] == monitor]), None)
|
||||
|
||||
|
||||
# Given a monitor name, returns its most recent value. If the monitor
|
||||
# does not exist, returns UNKNOWN COMMAND.
|
||||
|
||||
def monitor_value(monitor):
|
||||
subscription = have_monitor(monitor)
|
||||
if subscription:
|
||||
return values[subscription['topic']]
|
||||
else:
|
||||
return "UNKNOWN COMMAND"
|
||||
|
||||
# Given a monitor name, returns its info string. If the monitor does
|
||||
# not exist, returns UNKNOWN COMMAND.
|
||||
|
||||
def monitor_info(monitor):
|
||||
subscription = have_monitor(monitor)
|
||||
#print("# Monitor info for "+monitor)
|
||||
#print(subscription)
|
||||
if subscription:
|
||||
descr = subscription['description'] # Shorter to type
|
||||
return (descr['name'] + "\t" +
|
||||
(descr['min'] if 'min' in descr else "") + "\t" +
|
||||
(descr['max'] if 'max' in descr else "") + "\t" +
|
||||
descr['units'])
|
||||
else:
|
||||
return "UNKNOWN COMMAND"
|
||||
|
||||
|
||||
# MQTT logic
|
||||
|
||||
# The on_connect handler subscribes to all configured topics
|
||||
# and issues the ksysguard prompt when finished. From that point,
|
||||
# the client can start requesting data (although none may be available
|
||||
# depending on the relative refresh rates of MQTT publishers and the
|
||||
# ksysguard client.
|
||||
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
print("# Connected with result code "+str(rc))
|
||||
for subscription in config['mqtt']['subscriptions']:
|
||||
print("# Subscribing to " + subscription['topic'])
|
||||
client.subscribe(subscription['topic'])
|
||||
print("ksysguardd> ", end="", flush=True)
|
||||
|
||||
|
||||
# The callback for when a PUBLISH message is received from the server.
|
||||
# It stores the received value under the relevant topic key in the
|
||||
# values dictionary, from where it will be retrieved when requested
|
||||
# by the ksysguard client.
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
global values
|
||||
values[msg.topic] = msg.payload.decode("utf8").strip()
|
||||
|
||||
|
||||
# Create the MQTT client and assign event handlers
|
||||
|
||||
client = mqtt.Client(client_id="ksysguardd-"+str(random())[2:])
|
||||
client.on_connect = on_connect
|
||||
client.on_message = on_message
|
||||
|
||||
if 'username' in config['mqtt'] and 'password' in config['mqtt']:
|
||||
client.username_pw_set(config['mqtt']['username'], config['mqtt']['password'])
|
||||
client.connect_async(config['mqtt']['host'], config['mqtt']['port'] if 'port' in config['mqtt'] else 1883, 60)
|
||||
|
||||
|
||||
# Ksysguard logic
|
||||
|
||||
# Start by introducing ourselves. The protocol has been mostly deduced
|
||||
# from this link https://techbase.kde.org/Development/Tutorials/Sensors
|
||||
# and from the ksysguardd source code.
|
||||
|
||||
print("ksysguardd 4")
|
||||
|
||||
client.loop_start() # Asynchronously start the MQTT client
|
||||
|
||||
# Process lines of input from the ksysguard client
|
||||
|
||||
for line in sys.stdin:
|
||||
line = line.strip()
|
||||
|
||||
if line == "monitors": # List available sensors
|
||||
for subscription in config['mqtt']['subscriptions']:
|
||||
print(subscription['monitor']+"\t"+subscription['type'])
|
||||
|
||||
elif line == "quit": # Shut down this process
|
||||
print("# Quitting")
|
||||
client.loop_stop()
|
||||
exit()
|
||||
|
||||
elif len(line) and line[-1] == "?": # This is a query for the monitor's information
|
||||
print(monitor_info(line[0:-1]))
|
||||
else: # Assume that this is a query for the monitor's value
|
||||
print("NaN" if monitor_value(line) == None else monitor_value(line))
|
||||
|
||||
print("ksysguardd> ", end="", flush=True)
|
||||
|
||||
Reference in New Issue
Block a user