Navlost 1 год назад
Сommit
2cb7a01988
5 измененных файлов: 195 добавлений и 0 удалений
  1. +25
    -0
      README.md
  2. Двоичные данные
      doc/connect-host.png
  3. Двоичные данные
      doc/ksysguard-window.png
  4. +22
    -0
      example-config.yaml
  5. +148
    -0
      ksysguard-sensor-mqtt.py

+ 25
- 0
README.md Просмотреть файл

@@ -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
```

![](./doc/connect-host.png)

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.

![](./doc/ksysguard-window.png)

Двоичные данные
doc/connect-host.png Просмотреть файл

До После
Ширина: 557  |  Высота: 395  |  Размер: 39KB

Двоичные данные
doc/ksysguard-window.png Просмотреть файл

До После
Ширина: 1442  |  Высота: 794  |  Размер: 101KB

+ 22
- 0
example-config.yaml Просмотреть файл

@@ -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
- 0
ksysguard-sensor-mqtt.py Просмотреть файл

@@ -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)

Загрузка…
Отмена
Сохранить