OpenHAB - Manage the internet router

Sometimes it happens that the internet connection in my home is no more available, even if the service provider router returns no alarms. Most of the times the usual switch-off/switch-on is enough to have back the internet service again. If this occurs when nobody is using internet or when we are all out of home, the internet connection remain down for a long time, loosing the update of the cloud services I use and excluding the possibility to connect the LAN from outside via VPN or OpenHAB cloud service.

Looking in the OpenHAB community I've found this tutorial on speedtest-cli internet up/downlink measurement.

The internet connection is tested as explained every 5 minutes. The results are displayed as actual measure for the standard UI and stored in the InfluxDB database to create a historical graph.

This is the configuration with some little changes from the tutorial one:

items/speedtest.items

Group gSpeedtest <"network-icon"> (Whg)

String      SpeedtestSummary        "Speedtest [%s]"             <"speedtest_network">       (gSpeedtest)
Number      SpeedtestResultPing     "Ping [%.3f ms]"             <"speedtest_next5">         (gSpeedtest)
Number      SpeedtestResultDown     "Downlink [%.2f Mbit/s]"     <"speedtest_download">      (gSpeedtest)
Number      SpeedtestResultUp       "Uplink [%.2f Mbit/s]"       <"speedtest_upload">        (gSpeedtest)
String      SpeedtestRunning        "Speedtest running ... [%s]" <"speedtest_new">           (gSpeedtest)
Switch      SpeedtestRerun          "Start manually"             <"speedtest_reload2">       (gSpeedtest)
DateTime    SpeedtestResultDate     "Last executed [%1$td.%1$tm.%1$tY, %1$tH:%1$tM]"   <"speedtest_problem4">      (gSpeedtest)
Number      SpeedtestCountOK        "Test OK [%d times]"         <"speedtest_network">       (gSpeedtest)
Number      SpeedtestCountKO        "Test KO [%d times]"         <"speedtest_network">       (gSpeedtest)
Number      RouterRestartCount      "Restarted [%d times]"       <"speedtest_network">       (gSpeedtest)
Switch      RouterRestart           "Restart Internet Router"    <"network">                 (gSpeedtest)

rules/speedtest.rules

val String filename = "speedtest.rules"
var Number speedtestVarOK
var Number speedtestVarKO
var Number routerRestartCountVar

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("⁉ (Sconosciuto)")
    ]
    speedtestVarOK = 0
    speedtestVarKO = 0
    routerRestartCount = 0
end

rule "Speedtest"
when
    //Time cron "0 0 5,13 * * ?" or
    Time cron "0 0/5 * * * ?" or
    Item SpeedtestRerun received command ON
then
    logInfo(filename, "--> speedtest executed...")
    SpeedtestRunning.postUpdate("Misura in corso...")

    // update timestamp for last execution
    SpeedtestResultDate.postUpdate(new DateTimeType())

    // execute the script, you may have to change the path depending on your system
    var String speedtestCliOutput = executeCommandLine("/usr/local/bin/speedtest-cli@@--simple@@--server@@395 ", 120*1000)

    // for debugging:
    //var String speedtestCliOutput = "Ping: 43.32 ms\nDownload: 21.64 Mbit/s\nUpload: 4.27 Mbit/s"
    //logInfo(filename, "--> speedtest output:\n" + speedtestCliOutput + "\n\n")

    SpeedtestRunning.postUpdate("Analisi dei dati...")

    // starts off with a fairly simple error check, should be enough to catch all problems I can think of
    if (speedtestCliOutput.startsWith("Ping") && speedtestCliOutput.endsWith("Mbit/s")) 
    {
        var String[] results = speedtestCliOutput.split("\\r?\\n")
        var float ping = new java.lang.Float(results.get(0).split(" ").get(1))
        var float down = new java.lang.Float(results.get(1).split(" ").get(1))
        var float up   = new java.lang.Float(results.get(2).split(" ").get(1))
        SpeedtestResultPing.postUpdate(ping)
        SpeedtestResultDown.postUpdate(down)
        SpeedtestResultUp.postUpdate(up)
        SpeedtestSummary.postUpdate(String::format("ᐁ  %.1f Mbit/s  ᐃ %.1f Mbit/s (%.0f ms)", down, up, ping))
        SpeedtestRunning.postUpdate("-")
        speedtestVarOK = speedtestVarOK + 1
        speedtestVarKO = 0
        SpeedtestCountOK.postUpdate(speedtestVarOK)
        SpeedtestCountKO.postUpdate(speedtestVarKO)
        logInfo(filename, "--> speedtest finished.")
    } 
    else 
    {
        SpeedtestResultPing.postUpdate(0)
        SpeedtestResultDown.postUpdate(0)
        SpeedtestResultUp.postUpdate(0)
        SpeedtestSummary.postUpdate("(sconosciuto)")
        SpeedtestRunning.postUpdate("Errore durante l'esecuzione")
        speedtestVarKO = speedtestVarKO + 1
        SpeedtestCountKO.postUpdate(speedtestVarKO)
        logError(filename, "--> speedtest failed. Output:\n" + speedtestCliOutput + "\n\n")
        if (speedtestVarKO > 1)
        {
            sendCommand(Rele2, ON)
            speedtestVarKO = 0
            routerRestartCountVar = routerRestartCountVar + 1
            RouterRestartCount.postUpdate(routerRestartCountVar)
            set_timer = createTimer(now.plusSeconds(5))
            [
                sendCommand(Rele2, OFF)
                RouterRestart.postUpdate(ON)
                set_timer = null
            ] 
        }
    }
    SpeedtestRerun.postUpdate(OFF)
end

rule "Router Restart"
when 
    Item RouterRestart changed
then
    sendCommand(Rele2, ON)
    set_timer = createTimer(now.plusSeconds(5))
    [
        sendCommand(Rele2, OFF)
        RouterRestart.postUpdate(ON)
        set_timer = null
    ] 
end 

// vim: syntax=Xtend

The failed tests are cumulated in a variable, when the faults number is higher than a given threshold the relay is energized for 5 seconds, the NC contact opens the circuit and cut off the power supply for the router along the same time.

In the picture below the internet router plug connected:

It's tied to the normally closed contact of the relay so energizing the relay the router is switched off for a while.

Hardware details on Relay interface article.

Classic UI example:

Using this UI the speedtest as well as the router reset can be forced manually at any moment.

Graph example:

Guido Ottaviani (Author/Designer/Developer for electronics-firmware-robotics)
He deals with electronic design and embedded system firmware for Robotics and industrial and civil applications, as well as technical disclosure on the internet.
http://www.guiott.com - https://github.com/guiott - guido@guiott.com



Le attività del TanzoLab si svolgono ogni mercoledi sera, salvo casi speciali, dalle ore 18:30 presso i locali della Acme Systems srl e consistono in:

  • Talk monotematici a cura di professionisti in vari settori tecnologici
  • Workshop pratici su elettronica embedded, produzione e informatica
  • Progettazione e realizzazione di nuovi prodotti embedded per l'IT

Le attività vengono coordinate tramite questo sito, in cui vengono pubblicati tutti i lavori svolti o in via di sviluppo, e tramite un gruppo Telegram con cui per interagire direttamente via chat con gli altri membri.