oH 3.1 Download an image from the internet and save into a item of the type Image

Hi there,

this is a “is this possible” thing and not something i really need.
I would like to understand the steps and maybe use the knowledge for future ideas.

I´m currently using the speedtest binding to measure my connection, collect the data with InfluxDB and view them in Grafana.
The binding obtains the url of the speedtest result and i would like to get this image into an openHAB item of the type Image.

I already tried different methods but all of them failed…

1.Using HttpUtil
I was playing around with HttpUtil.downloadImage(<url>) but i wasn´t able to get the function working.
First try:

import org.openhab.core.io.net.http
...
    val speedImage = spdURL.state + ".png"
    val ImageName = speedImage.toString.split("/").get(5)
    logInfo(ruleId, "URL der Grafik: " + speedImage)
    logInfo(ruleId, "Name der Grafik: " + ImageName)
    var RawType Image = HttpUtil.downloadImage(speedImage)
    spdImage.postUpdate(Image)
[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Testswitch-1' failed: The name 'httpUtil' cannot be resolved to an item or type; line 28, column 25, length 8 in Testswitch

Second try:

import org.openhab.core.io.net.http
...
    val speedImage = spdURL.state + ".png"
    val ImageName = speedImage.toString.split("/").get(5)
    logInfo(ruleId, "URL der Grafik: " + speedImage)
    logInfo(ruleId, "Name der Grafik: " + ImageName)
    spdImage.postUpdate(HttpUtil.downloadImage(speedImage))
[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Testswitch-1' failed: The name 'httpUtil' cannot be resolved to an item or type; line 28, column 25, length 8 in Testswitch

2.Using wget and base64
Ok when oH can´t do it i´ll try it with executeCommandLine

    val speedImage = spdURL.state + ".png"
    val ImageName = speedImage.toString.split("/").get(5)
    val wget = "wget"
    val base64 = "base64"
    val getImage = speedImage + " -O /tmp/ " + ImageName
    logInfo(ruleId, "Command: " + wget + " " + getImage)
    var getImageResult = executeCommandLine(Duration.ofSeconds(10), wget, getImage)
    logInfo(ruleId, "wget result: " + getImageResult)
    var getBase64 = executeCommandLine(Duration.ofSeconds(10), base64, ImageName)
    logInfo(ruleId, "Base64 result: " + getBase64)
    spdImage.postUpdate(getBase64)

wget is able to download the image but not into /tmp/ and i´m not able to find the file…
It´s not in the same directory the executeCommandLine is working as the base64 isn´t able to see the file and convert it.

[INFO ] [g.openhab.core.model.script.TestRule] - Command: wget https://www.speedtest.net/result/c/c83e6a49-53c5-47c2-96be-8db3d2a53ec4.png -O /tmp/ c83e6a49-53c5-47c2-96be-8db3d2a53ec4.png
[INFO ] [g.openhab.core.model.script.TestRule] - wget result: --2021-08-07 17:28:39--  https://www.speedtest.net/result/c/c83e6a49-53c5-47c2-96be-8db3d2a53ec4.png%20-O%20/tmp/%20c83e6a49-53c5-47c2-96be-8db3d2a53ec4.png
Auflösen des Hostnamens www.speedtest.net (www.speedtest.net)… 151.101.66.219, 151.101.130.219, 151.101.2.219, ...
Verbindungsaufbau zu www.speedtest.net (www.speedtest.net)|151.101.66.219|:443 … verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet … 200 OK
Länge: 48712 (48K) [text/html]
Wird in » c83e6a49-53c5-47c2-96be-8db3d2a53ec4.png« gespeichert.
     0K .......... .......... .......... .......... .......   100% 5,72M=0,008s
2021-08-07 17:28:40 (5,72 MB/s) - » c83e6a49-53c5-47c2-96be-8db3d2a53ec4.png« gespeichert [48712/48712]
[INFO ] [g.openhab.core.model.script.TestRule] - Base64 result: base64: /tmp/c83e6a49-53c5-47c2-96be-8db3d2a53ec4.png: Datei oder Verzeichnis nicht gefunden

I´m wondering why the logged wget command is correct but the base64 command has : between the base64 and the filename.
And where is the wget download stored?!
Doing this manually in a SSH sessions works without a problem.


  • Platform information:
    • Hardware: Raspberry Pi 4 Model B Rev 1.1
    • OS: Raspbian GNU/Linux 10 (buster)
    • Java Runtime Environment:
      • openjdk version “11.0.12” 2021-07-20 LTS
      • OpenJDK Runtime Environment Zulu11.50+19-CA (build 11.0.12+7-LTS)
      • OpenJDK Client VM Zulu11.50+19-CA (build 11.0.12+7-LTS, mixed mode)
    • openHAB version: 3.1.0 (Build)

I´m now able to download the image with wget
The syntax for oH 3.1 isn´t very intuitive…

val wget = "wget"
val speedImage = spdURL.state + ".png"
val ImageName = speedImage.toString.split("/").get(5)
val getImage = "/tmp/" + ImageName
var getImageResult = executeCommandLine(Duration.ofSeconds(10), wget, speedImage, "-O", getImage)

I´m able to convert the image into base64 but not store it into the Image item…

val convertName = "/tmp/" + ImageName
val getBase64 = executeCommandLine(Duration.ofSeconds(10), base64, convertName)
spdImage.postUpdate(getBase64)
[WARN ] [b.core.model.script.actions.BusEvent] - Cannot convert 'iVBOR...ACBjSFJN' to a state type which item 'spdImage' accepts: [RawType, UnDefType].
val convertName = "/tmp/" + ImageName
val RawType getBase64 = executeCommandLine(Duration.ofSeconds(10), base64, convertName)
spdImage.postUpdate(getBase64)
[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Testswitch-1' failed: An error occurred during the script execution: Could not invoke method: org.openhab.core.model.script.actions.BusEvent.postUpdate(org.openhab.core.items.Item,org.openhab.core.types.State) on instance: null in Testswitch

You can’t coerce it like that, the result of exec is just a string.

This might help

var getBase64 = RawType.valueOf(executeCommandLine(Duration.ofSeconds(10), base64, convertName))
getBase64 = "data:image/png;base64," + getBase64
spdImage.postUpdate(getBase64)

ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Testswitch-1' failed: Invalid data URI syntax for argument

The post doesn´t say how to get the image from an external source, convert it into base64 and then store it into the item.
The linked post shows a static way that needs interaction by the user.

I wasn´t able to get it working with that one but with this monster of a rule.

Thanks rossko57!

Final rule for the speedtest image:

import java.io.ByteArrayOutputStream
import java.net.URL

rule "Speedtest Image Update"

when

    Item spdURL changed

then

    val speedImage = spdURL.state + ".png"
    var userImageDataBytes = newByteArrayOfSize(0)

    try {
        // use the built in java URL class - pass it the url to download
        val url = new URL(speedImage)
        // create an output stream - we'll use it for building up our downloaded bytes
        val byteStreamOutput = new ByteArrayOutputStream()
        // open the url as a stream - aka - start getting stuff
        val inputStream = url.openStream()
        // n is a variable for tracking how much data we have read off the inputStream per loop (and how much we write to the output stream)
        var n = 0
        // buffer is another byte array. basically we're using it as a fixed size copy byte array
        var buffer = newByteArrayOfSize(1024)
        do {
        // read from input stream (the data at the url) into buffer - and place how many bytes were read into n
        n = inputStream.read(buffer)
        if (n > 0)  {
        // if we read more than 0 bytes - copy them from buffer into our output stream
        byteStreamOutput.write(buffer, 0, n)
        }
        } while (n > 0) // keep doing this until we don't have anything to read.
        userImageDataBytes = byteStreamOutput.toByteArray() // assemble all the bytes we wrote into an actual byte array
    } catch(Throwable t) {
        logError(ruleId, "Es ist ein Problem aufgetreten: " + t.toString)
    }
    logInfo(ruleId, "Bild Daten erhalten")

    // make a new RawType with the byte array and the mime type - the open hab type Image needs a raw type
    // my images are all jpeg - so this mime type is right for me
    val rawType = new RawType(userImageDataBytes, "image/jpeg")

    // entry.getValue is the "ImageItem" from the map. In my case - this is BrelandImage
    if (rawType.toString != spdImage.state.toString) { // if this raw type isn't the same as what's already there (the toString is a kind of accurate way to test - the raw type to string is bascially mime + size - which is a bad approximation for equality - but good enough for me
        spdImage.postUpdate(rawType) // update it!
        logInfo(ruleId, "Bild aktualisiert")
    } else {
        logInfo(ruleId, "Bilder waren identisch")
    }

end

Maybe someone can explaine how to use the HttpUtil because i always got the Error The name 'httpUtil' cannot be resolved to an item or type;.
I would still try to understand how this method works.

kind regards

People usually use the HTTP binding, or one of the HTTP Actions in a rule.

If you look at the class HttpUtil there‘s a downloadImage method.
I thought this would give us the opportunity to use it inside of rules.

HttpUtil wasn’t really intended to be used from Rules and therefore is not there by default. However you can maybe access it and use it but you’ll have to import it first.

import  org.openhab.core.io.net.http.HttpUtil

Ok it was worth the try :slight_smile:
I already tried to use it with different imports

import  org.openhab.core.io.net.http
import  org.openhab.core.io.net.http.HttpUtil
The name 'HttpUtil' cannot be resolved to an item or type

I know the field of usage is pretty small so i´m happy with the solution from mindstorms6.

1 Like