Hi,
i want to share my current solution for the ookla docker container integration (based on Speedtest CLI by Ookla - Internet Up-/Downlink Measurement Integration):
- I’m using the ookla docker image tamasboros/ookla-speedtest that is
- writing the json output to /opt/openhab/userdata/tmp/ookla.json, this file is then
- parsed by the speedtest rule (instead of executing the ookla speedtest and fetching the json like in the original rule). To do so, i added a try-catch block around, reading the file and convert the pretty json to a one-liner with 2 regex. (-> see MODIFICATION START / END)
- cronjob is running the ookla speedtest container every minute.
speedtest.rules:
// MODIFICATION START
import java.nio.file.Files
import java.nio.file.Paths
// MODIFICATION END
val String ruleId = "Speedtest"
val Number calc = 125000 // Converting from bits to Mbits
rule "Speedtest init"
when
System started
then
createTimer(now.plusSeconds(195))
[|
if(SpeedtestRerun.state == NULL)
{
SpeedtestRerun.postUpdate(OFF)
}
if(SpeedtestRunning.state == NULL)
{
SpeedtestRunning.postUpdate("-")
}
if(SpeedtestSummary.state == NULL || SpeedtestSummary.state == "")
{
SpeedtestSummary.postUpdate("⁉ (unbekannt)")
}
]
end
rule "Speedtest"
when
Time cron "0 0/15 * * * ?" or
Item SpeedtestRerun changed from OFF to ON or
Item SpeedtestRerun received command ON
then
//logInfo(ruleId, "--> speedtest executed...")
SpeedtestRunning.postUpdate("Messung läuft...")
// execute the script, you may have to change the path depending on your system
// Please use -f json and not -f json-pretty
//val speedtestExecute = "speedtest --accept-gdpr --accept-license -f json"
var String speedtestCliOutput;
// MODIFICATION START
try {
speedtestCliOutput = new String(Files.readAllBytes(Paths.get("/openhab/userdata/tmp/ookla.json")))
speedtestCliOutput = speedtestCliOutput.replaceAll("\\s+", "").replaceAll("\\r\\n+","")
// MODIFICATION END
// for debugging:
// var String speedtestCliOutput = "Ping: 43.32 ms\nDownload: 21.64 Mbit/s\nUpload: 4.27 Mbit/s"
logInfo(ruleId, "--> speedtest output after readfile:\n" + speedtestCliOutput + "\n\n")
SpeedtestRunning.postUpdate("Datenauswertung...")
// starts off with a fairly simple error check, should be enough to catch all problems I can think of
if (speedtestCliOutput.startsWith("{\"type\":\"result\",") && speedtestCliOutput.endsWith("}}"))
{
var ping = Float::parseFloat(transform("JSONPATH", "$.ping.latency", speedtestCliOutput))
SpeedtestResultPing.postUpdate(ping)
var float down = Float::parseFloat(transform("JSONPATH", "$.download.bandwidth", speedtestCliOutput))
down = (down / calc)
SpeedtestResultDown.postUpdate(down)
var float up = Float::parseFloat(transform("JSONPATH", "$.upload.bandwidth", speedtestCliOutput))
up = (up / calc)
SpeedtestResultUp.postUpdate(up)
var String url = transform("JSONPATH", "$.result.url", speedtestCliOutput)
val img = url + ".png"
SpeedtestResultImage.postUpdate(img)
SpeedtestSummary.postUpdate(String::format("ᐁ %.1f Mbit/s ᐃ %.1f Mbit/s (%.0f ms)", down, up, ping))
SpeedtestRunning.postUpdate("-")
// update timestamp for last execution
val DateTimeType ResultDate = DateTimeType.valueOf(transform("JSONPATH", "$.timestamp", speedtestCliOutput))
SpeedtestResultDate.postUpdate(ResultDate)
}
else
{
SpeedtestResultPing.postUpdate(0)
SpeedtestResultDown.postUpdate(0)
SpeedtestResultUp.postUpdate(0)
SpeedtestSummary.postUpdate("(unbekannt)")
SpeedtestRunning.postUpdate("Fehler")
logError(ruleId, "--> speedtest failed. Output:\n" + speedtestCliOutput + "\n\n")
}
SpeedtestRerun.postUpdate(OFF)
// MODIFICATION START
} catch (IOException e){
SpeedtestResultPing.postUpdate(0)
SpeedtestResultDown.postUpdate(0)
SpeedtestResultUp.postUpdate(0)
SpeedtestSummary.postUpdate("(unbekannt)")
SpeedtestRunning.postUpdate("File Error")
logError(ruleId, "--> speedtest failed. Output:\nookla output reading failed!\n\n")
}
// MODIFICATION END
end
/etc/cron.d/docker
* * * * * docker /home/docker/docker_ookla.sh
/home/docker/docker_ookla.sh
docker run --rm --name ookla tamasboros/ookla-speedtest > /opt/openhab/userdata/tmp/ookla.json
I’m quite sure this is not the most beautiful solution, but due to lack of time my only one working ![]()