Usage of Image Item

I’m super sorry to “beat a dead horse” but after digging a bit through the source code of the RawType - I’ve managed to get a rule that updates an Image type.

Here’s the details:

In my people.items file, I’ve got an Image declared:

Image BrelandImage

For my use case, I want to periodically refresh images from instagram - but the ideas would work for any url. I’m using this to update images on a “who’s home” presence board.

Here’s my people_images.rules:

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

when
  // run hourly or at system start
  Time cron "0 0 0/1 1/1 * ? *" or
  System started
then
  logWarn("Picture.rules", "Trying to get profile images")
  // create a map of our "people" to their image item names
  // my home has mutliple people, i am just listing me here
  val peopleToItems = newHashMap(
    "mindstorms6" -> BrelandImage
  )

  // iterate over everyone in my map - and get their profile image
  for (entry : peopleToItems.entrySet()) {
     // start by getting their "json" user data (public) from IG (the ig username is the key in the map above)
     val userJson = sendHttpGetRequest("https://www.instagram.com/" + entry.getKey() + "/?__a=1")
     // using the JSONPATH transform, extract the actual profile picture url
     val userImageUrl = transform("JSONPATH", "$.graphql.user.profile_pic_url_hd", userJson)
     // fetch the raw image data. Start by declaring an empty byte array.
     var userImageDataBytes = newByteArrayOfSize(0)
     try {
       // use the built in java URL class - pass it the url to download
       val url = new URL(userImageUrl)
       // 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("Picture.rules", "Some bad stuff happened in my rule: " + t.toString)
     }
     logWarn("Picture.rules", "Got image bytes")

     // 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 != entry.getValue().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
       entry.getValue().postUpdate(rawType) // update it!
       logWarn("Picture.rules", "Value updated")
     } else {
       logWarn("Picture.rules", "Images were same")
     }
  }
logWarn("Picture.rules", "Finished rule update")


end

And just like that - you can update an Image from a rule. Sadly, the fetch was more complex than I wanted it to be (I tried to coerce the response from sendHttpGetRequest into the right byte array, but never could get it to work quite right. I also couldn’t seem to get HttpUtils directly usable in a rule, despite a lot of trying to. YMMV)

7 Likes