Array Mismatch Exec Binding Openhab3

Tags: #<Tag:0x00007efec695bc78>

Hello,

I´m Migrating my Existing Openhab2 Setup Step-byStep to Openhab3. I used the Speedtest Tutorial from here:

and it works fine in Openhab 2.5. I Knew the Exec Binding changed in OH3 but i dont have any idea what i have to change to get it Work with the new Syntax.

  • Platform information:
    • Hardware: RaspberryPi4
    • OS: OpenHABian
    • Java Runtime Environment: Java 11
    • openHAB version: Openhab3
  • Issue of the topic :Array Speedtest-CLI mismatch
  • Please post configurations (if applicable):
    • Items
Group gSpeedtest    <"speedtest">
Group gSpeedChart
String SpeedtestCharts

String      SpeedtestSummary        "Speedtest [%s]"                                            <"speedtest_summary">   (gSpeedtest)
Number      SpeedtestResultPing     "Ping [%.3f ms]"                                            <"speedtest_ping">      (gSpeedtest, gSpeedChart)
Number      SpeedtestResultDown     "Download [%.2f Mbit/s]"                                    <"speedtest_download">  (gSpeedtest, gSpeedChart)
Number      SpeedtestResultUp       "Upload [%.2f Mbit/s]"                                      <"speedtest_upload">    (gSpeedtest, gSpeedChart)
String      SpeedtestRunning        "Speedtest running ... [%s]"                                <"speedtest_run">       (gSpeedtest)
Switch      SpeedtestRerun          "Manuell starten"                                           <"speedtest_reload">    (gSpeedtest)
DateTime    SpeedtestResultDate     "Last Run [%1$td.%1$tm.%1$tY, %1$tH:%1$tM]"                 <"speedtest_date">      (gSpeedtest)
String      SpeedtestResultError    "Error Message [%s]"                                        <"speedtest_error">     (gSpeedtest)

String      SpeedtestResultImage    "Bild"
  • Rules
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 -f json"
    var speedtestCliOutput = executeCommandLine(speedtestExecute, 120*1000,)

    // for debugging:
    // var String speedtestCliOutput = "Ping: 43.32 ms\nDownload: 21.64 Mbit/s\nUpload: 4.27 Mbit/s"
    //logInfo(ruleId, "--> speedtest output:\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)

end
  • Logs
2020-12-18 15:00:00.462 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'speedtest-2' failed: An error occurred during the script execution: array element type mismatch in speedtest

Why not use the Network binding with integrated speedtest?

This binding allows checking whether a device is currently available on the network. This is either done using ping or by a successful TCP connection on a specified port.

It is also capable to perform bandwidth speed tests.

I suffer from the very same issue. The original question more leads to the changes in executeCommandLine, which we try to understand and adopt the rule. Btw the rule based approach as alternative to network binding allows extraction of more data, when using json output speedtest-cli@@--json, e.g. location parameters of client and server. Not really required, but available - and only after successful executeCommandLine.
Only swapping command and timeout +change to duration does not help:
var String speedtestCliOutput = executeCommandLine(Duration.ofSeconds(120),"/usr/bin/speedtest-cli@@--json")
, as described here:

@Tiggr1994 your lines
val speedtestExecute = “speedtest -f json”
var speedtestCliOutput = executeCommandLine(speedtestExecute, 120*1000,)
should be:
var speedtestCliOutput = executeCommandLine(Duration.ofSeconds(120), “speedtest”, “-f”, “json”)

@cwi your line
var String speedtestCliOutput = executeCommandLine(Duration.ofSeconds(120),"/usr/bin/speedtest-cli@@–json")
should be:
var String speedtestCliOutput = executeCommandLine(Duration.ofSeconds(120), “/usr/bin/speedtest-cli”, “–json”)

I tried but don’t get it work, Thing is online but i get no States. Only Upload item is Updating after Speedtest and throws an Error:

No Metadata added (State Description) only the Item (Number:DataTransferRate) created via Main UI (Equipment via Thing)

Exception while formatting value '27.0453008792 Mbit/s' of item SpeedTest_UploadRate with format '%d %unit%': d != java.math.BigDecimal 

So i tried my “old” solution via Speedtest CLI

Please use the example from the binding docs, works out of the box.

Edit: ahh, now I remenber: don’t use UoM, then it will work :innocent:

I changed my rules file as Norbert suggested, however it fails the output test now and reports error, while I get the result correctly in the log files.(if (speedtestCliOutput.startsWith("{“type”:“result”,") && speedtestCliOutput.endsWith("}}")))

From another thread, i removed the second test and it is now working ok

Yes this worked for me too. I dont nkow why the If condition dont work. But without it it works

Good evening,
I have the problem that when I execute
var speedtestCliOutput = executeCommandLine (Duration.ofSeconds (120)," speedtest "," -f "," json ")
from Rule there seems to be no waiting for the response after 120 seconds. Putting a logInfo on the “speedtestCliOutput” variable I immediately get “null”. If I look at the processes in htop, speedtest is performed in a few milliseconds: I see it appear and then disappear. It is as if it was not executed or stopped immediately. What do you suggest me to do?
Thanks
Matteo

Can you clarify on this? I’m executing successful speedtests from the binding, but seeing the “Exception while formatting value” error

I had to remove the Unit of Measurement tags to make it work.

There are troubling spaces in your arguments please remove them and try again:
var speedtestCliOutput = executeCommandLine (Duration.ofSeconds (120), "speedtest", "-f", "json")

I fixed the syntax as indicated, trying to put and remove “spaces” before the brackets, but nothing. The process starts but ends almost immediately without waiting 120 seconds. If I launch it manually everything works correctly. Could it be a permissions issue?

same problem here, running speedtest -f json manualy works, but when started in openhab i get error in openhab.log:
"[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'speedtest-2' failed: cannot invoke method public boolean java.lang.String.startsWith(java.lang.String) on null in speedtest"

I get the same error. It seems that executeCommandLine(Duration.ofSeconds(120), "/usr/bin/speedtest", "-f", "json") won’t wait to be finished.

My Log after this line will return directly null.

@AntaresTeo , @RomanF and @flo-ferox to debug the problem please try this in Karaf…

log:set DEBUG org.openhab.core.io.net.exec

Did you run speedtest at least once as openhab user?
If not, please use

sudo -u openhab speedtest

You may need to accept the GDPR and license when running speedtest for the first time.
This should be saved after the first execution.

2 Likes

I always executed the command from shell as user openhabian.

But sudo -u openhab speedtest asked me again for accept the licence.

Now I will get an other error. But this is fixable:
2021-01-13 22:12:53.781 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'speedtest-2' failed: The name 'calc' cannot be resolved to an item or type; line 61, column 24, length 4 in speedtest

I will have a Look tomorrow. Thanks so far!

UPDATE: speedtest works finde for me!
If just forgot to add the two variables at the top of the rules.
Thanks a lot!

1 Like

to execute
sudo -u openhab speedtest

and accept the GDPR was it! speedtest works now, thanks!

1 Like

Thanks, or everything works!
Matteo

1 Like