Automated speedtest with Breitbandmessung docker send to MQTT and read with Node-Red

Hi everyone, I created an automation to measure my internet speed with the german government official tool Breitbandmessung and send the results to HA. A systemd service starts a python script at boot, this measures every 10 minutes the speed with a Breitbandmessung docker container, parses the results and sends those to MQTT. Node-Red listens for the results, parses those and feeds them in two entities.

Disclaimer: I have a second server on which the measurement is taking place, because I wasn’t able to run docker on the HA server. There is probably a better way to do this, I welcome tips anytime.

Instructions
First you have to install this https://github.com/shiaky/breitbandmessung docker. After that, set up python with MQTT. There are several instructions on the internet. In HA you need an MQTT server and Node-Red (it should be possible to use this with native HA as well). You also need to connect Node-Red to MQTT.

Then you have to copy the systemd service to /etc/systemd/system and the python script to a place you like. Please adapt the path to the python script in the systemd service and the credentials in the python script for the MQTT server. Now you can import the Node-Red flow and after you saved everything and started the systemd service, it should be working :slight_smile:

Files
The systemd service called mqtt_breitbandmessung.service:

[Unit]
Description=Service to start a Breitbandmessung every 10 Min.
After=multi-user.target

[Service]
Type=simple
Restart=always
KillSignal=SIGINT
ExecStart=/usr/bin/python3 /home/henry/breitbandmessung.py

[Install]
WantedBy=multi-user.target

The python programm called mqtt_breitbandmessung.py:

#!/usr/bin/python
import time
import logging
import random
import subprocess
from paho.mqtt import client as mqtt_client
import numpy

### Logging festlegen
logging.basicConfig(level=logging.INFO) # DEBUG, INFO, WARNING, ERROR, CRITICAL
### Einen eigenen Logging-Befehl mit Info festlegen
logMqtt = logging.getLogger("MQTT")
logger = logging.getLogger("HA_Breitbandmessung_Status")

MQTT_BROKER = "***.***.***.***"
MQTT_PORT = ****
MQTT_TOPIC = "mqtt-breitbandmessung"
MQTT_USERNAME = "******"
MQTT_PASSWORD = "******"
MQTT_CLIENT_ID = f'python-mqtt-{random.randint(0, 1000)}'

### MQTT-Connect funktion
def connect_mqtt():
    def on_connect(client, userdata, flags, rc):
        if rc == 0:
            logMqtt.info(f"Connected to MQTT Broker!")
        else:
            logMqtt.info(f"Failed to connect, return code '{rc}'")

    client = mqtt_client.Client(MQTT_CLIENT_ID)
    client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
    client.on_connect = on_connect
    client.connect(MQTT_BROKER, MQTT_PORT)
    return client

### Funktion um Programm sauber zu beenden
def exit_gracefully(signum=0, frame=None):
    logger.info('Exiting now')
#    loop.quit()
    ha_update("False")
    exit(0)

### Funktion um Status via MQTT an HomeAssistant zu senden
def ha_update(msg):
    if not client.is_connected:
        client.reconnect()
    result = client.publish(MQTT_TOPIC, msg)
    if result[0] == 0:
        logger.info(f"Send `{msg}` to topic `{MQTT_TOPIC}`")


#################
### Hauptprogramm

client = connect_mqtt()
client.loop_start()
time.sleep(1)

try:
    while True:
        logger.info(f"Messung gestartet")
        processing = subprocess.run(["docker run -v $PWD/messprotokolle:/export/ shiaky/breitbandmessung:latest  | awk '/RESULTS >>>/{x=NR+2;next}(NR<=x){print}'"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        process = str(processing.stdout.decode("utf-8"))
        download = process.split("\n",1)[0].replace("D:[", "").replace("]","")
        upload = process.split("\n",1)[1].replace("U:[", "").replace("]\n","")
        msg = '{download:' + str(download) + ', updload:' + str(upload) +'}'
        ha_update(msg)
        time.sleep(600)
except KeyboardInterrupt:
    if client.is_connected:
        exit_gracefully()
    else:
        exit

The Node-Red flow:

[{"id":"7801784aec4f3a6f","type":"comment","z":"bf6a2db4.23112","name":"Breitbandmessung","info":"","x":110,"y":3460,"wires":[]},{"id":"c71ea843c589729c","type":"mqtt in","z":"bf6a2db4.23112","name":"","topic":"mqtt-breitbandmessung","qos":"2","datatype":"auto","broker":"6d7737bc.0cd38","nl":false,"rap":false,"inputs":0,"x":140,"y":3580,"wires":[["92f1c3efb369f56f","febbf65ee938173b"]]},{"id":"6fa703217ef95cd6","type":"function","z":"bf6a2db4.23112","name":"in Object umwandeln","func":"let zwischen, objekt = [];\n\n// Nachricht in Down- und Upload aufteilen\nzwischen = msg.payload.split(\", \");\n\n// Name von Wert trennen\nfor (let i = 0; i < zwischen.length; i++) {\n    objekt.push(zwischen[i].split(\":\"));\n}\n\n// Objekt aus den Aufteilungen bilden\nmsg.payload = { download: Number(objekt[0][1].replace(\",\", \".\")), upload: Number(objekt[1][1].replace(\"}\", \"\").replace(\",\", \".\")) };\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":3600,"wires":[["8d9795bd6b98add6","0f80c507d162032d"]]},{"id":"8d9795bd6b98add6","type":"ha-entity","z":"bf6a2db4.23112","name":"sensor.breitbandmessung_download","server":"be104a93.6e2ed8","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Breitbandmessung Download"},{"property":"device_class","value":""},{"property":"icon","value":"mdi:speedometer"},{"property":"unit_of_measurement","value":"Mbit/s"}],"state":"payload.download","stateType":"msg","attributes":[],"resend":true,"outputLocation":"payload","outputLocationType":"none","inputOverride":"allow","x":890,"y":3580,"wires":[[]]},{"id":"0f80c507d162032d","type":"ha-entity","z":"bf6a2db4.23112","name":"sensor.breitbandmessung_updload","server":"be104a93.6e2ed8","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Breitbandmessung Upload"},{"property":"device_class","value":""},{"property":"icon","value":"mdi:speedometer"},{"property":"unit_of_measurement","value":"Mbit/s"}],"state":"payload.upload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"payload","outputLocationType":"none","inputOverride":"allow","x":880,"y":3620,"wires":[[]]},{"id":"92f1c3efb369f56f","type":"trigger","z":"bf6a2db4.23112","name":"15 Min.","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"15","extend":true,"overrideDelay":false,"units":"min","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":480,"y":3520,"wires":[["10800c69e3d63bb2"]]},{"id":"10800c69e3d63bb2","type":"change","z":"bf6a2db4.23112","name":"unknown","rules":[{"t":"set","p":"payload.download","pt":"msg","to":"unknown","tot":"str"},{"t":"set","p":"payload.upload","pt":"msg","to":"unknown","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":3520,"wires":[["8d9795bd6b98add6","0f80c507d162032d"]]},{"id":"cc12b20e55737897","type":"inject","z":"bf6a2db4.23112","name":"Output on connect","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"5","topic":"output on connect","payload":"true","payloadType":"str","x":150,"y":3520,"wires":[["92f1c3efb369f56f"]]},{"id":"a0f2a917ce75c910","type":"function","z":"bf6a2db4.23112","name":"Reset Timers","func":"msg = {\"reset\":true};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":365,"y":3560,"wires":[["92f1c3efb369f56f"]],"icon":"node-red/timer.svg","l":false},{"id":"febbf65ee938173b","type":"switch","z":"bf6a2db4.23112","name":"False?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"False","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":330,"y":3600,"wires":[["a0f2a917ce75c910","10800c69e3d63bb2"],["6fa703217ef95cd6"]]},{"id":"6d7737bc.0cd38","type":"mqtt-broker","name":"Mosqitto - Addon","broker":"***.***.***.***","port":"****","clientid":"","usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""},{"id":"be104a93.6e2ed8","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":false,"cacheJson":true}]

Thanks a lot! Worked like a charm. I took the time to make some extensions and modifications to the python script and put everything into an easy to use docker image. In case anyone is interested in using it, feel free to check out my GitHub: NCHagelstein/BreitbandmessungToMQTT