Push notification with image from an IPCAM (suitable for dorbell automation and so on)

Dear Openhab Mates,
I think is interesting to share my experience for other that want to reply the case.

1 - Decide the triggering item that you want to actrivate the rule that push the image as broadcast notification (I’m at the moment implementing a way to intercept standard doorbell ringing as trigger, but that is another story)
1 - Implement as Thing an IP camera or any device running with IpCameraBinding
2 - Create an Image item in your Model (“TestImage” for example)
3 - copy paste these Rule:

import java.net.URL
import java.util.Base64


rule "Send doorbell push with pics"
when
    Item TRIGGERING_ITEM changed from OFF to ON
then

	executeCommandLine("wget", "http://OPENHABIANIP:8080/ipcamera/IPCAMERAUID/ipcamera.jpg", "-O", "/etc/openhab/html/CAMIMAGE.jpg");

	val imageUrl = "http://OPENHABIANIP:8080/static/CAMIMAGE.jpg"
	val username = "YOURopenhabianUSER"
	val password = "YOURopenhabianPASS"

	val url = new URL(imageUrl)
	val connection = url.openConnection()

	val userpass = username + ":" + password
	val basicAuth = "Basic " + new String(Base64.getEncoder().encode(userpass.getBytes()))
	connection.setRequestProperty("Authorization", basicAuth)

	val contentType = connection.getContentType()
	val bytes = connection.getInputStream().readAllBytes()
	val image = new RawType(bytes, contentType)
	TestImage.postUpdate(image)

	sendBroadcastNotification("Test", "motion", "Tag",
						"Title, "id-1234", null, "item:TestImage",
                                   null, null, null)

end

in that way, TestImage will be sended also if you are not in the same network of your openhabian system!

1 Like

Thanks for posting! This is a great candidate for a rule template. That way users can just install and configure the rule instead of needing to copy/paste/edit.

If you are willing I’m willing to help. You can keep it as Rules DSL but it will need to be reformatted as a managed rule. But as a rule tempalte, the TRIGGERINT_ITEM, imageURL, username, and password would all be configuration properties.

I tried to do the whole thing in Javascript because I don’t want to use DSL, but I get the following error message. I can’t find anything in the forum that helps me. Does anyone have any ideas?

Error

2025-01-14 11:40:52.789 [WARN ] [rnal.defaultscope.ScriptBusEventImpl] - State 'raw type (image/jpeg): 356597 bytes' cannot be parsed for item 'TestImage'.

Code

urlType = Java.type("java.net.URL");
base64Type = Java.type("java.util.Base64");
RawType = Java.type("org.openhab.core.library.types.RawType");

actions.Exec.executeCommandLine("wget", "http://192.168.53.68:8080/ipcamera/DoorbellReolink/image.jpg", "-O", "/etc/openhab/html/CAMIMAGE.jpg");

var imageUrl = "http://192.168.53.68:8080/static/CAMIMAGE.jpg";
var username = "YOURopenhabianUSER";
var password = "YOURopenhabianPASS";

var url = new urlType(imageUrl);
var connection = url.openConnection();

var userpass = username + ":" + password;
var basicAuth = "Basic " + new String(base64Type.getEncoder().encode(userpass.getBytes()));
connection.setRequestProperty("Authorization", basicAuth);

var contentType = connection.getContentType();
console.log("copyImageLocal: " + "after contentType [ " + contentType.toString() + " ]");
var bytes = connection.getInputStream().readAllBytes();
console.log("copyImageLocal: after bytes");
var image = new RawType(bytes, contentType);
console.log("copyImageLocal: after rawType");
items.getItem("TestImage").postUpdate(image);

This might be a case where JS’s wrapping of everything causes problems.

In Rules DSL additional postUpdate methods are patched onto the Item Object that take in additional compatible Item types (e.g. in this case a raw binary jpg). But in all the other languages those additional methods are not available. No matter what you pass to postUpdate, it’s going to be converted to a String because in the non-Rules DSL languages all that we have is the postUpdate(String, String) Action.

IIRC the Image Item does support Base64 encoded images and you are already importing the Base64 utility. But I don’t know if just running the binary through an encode is good enough.

Maybe I can jump in and help :slight_smile: :slight_smile:

            var URL = Java.type("java.net.URL");
            const url = new URL(camurl);
            const connection = url.openConnection();
            const contentType = connection.getContentType();
            const inputStream = connection.getInputStream();
            const bytes = inputStream.readAllBytes();

            const byteToBase64 = (byte) => {
                const key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
                let bytes = new Uint8Array(byte)
                let newBase64 = ''
                let currentChar = 0
                for (let i=0; i<bytes.length; i++) {   // Go over three 8-bit bytes to encode four base64 6-bit chars
                    if (i%3===0) { // First Byte
                        currentChar = (bytes[i] >> 2)      // First 6-bits for first base64 char
                        newBase64 += key[currentChar]      // Add the first base64 char to the string
                        currentChar = (bytes[i] << 4) & 63 // Erase first 6-bits, add first 2 bits for second base64 char
                    }
                    if (i%3===1) { // Second Byte
                        currentChar += (bytes[i] >> 4)     // Concat first 4-bits from second byte for second base64 char
                        newBase64 += key[currentChar]      // Add the second base64 char to the string
                        currentChar = (bytes[i] << 2) & 63 // Add two zeros, add 4-bits from second half of second byte
                    }
                    if (i%3===2) { // Third Byte
                        currentChar += (bytes[i] >> 6)     // Concat first 2-bits of third byte for the third base64 char
                        newBase64 += key[currentChar]      // Add the third base64 char to the string
                        currentChar = bytes[i] & 63        // Add last 6-bits from third byte for the fourth base64 char
                        newBase64 += key[currentChar]      // Add the fourth base64 char to the string
                    }
                }
                if (bytes.length%3===1) { // Pad for two missing bytes
                    newBase64 += `${key[currentChar]}==`
                }
                if (bytes.length%3===2) { // Pad one missing byte
                    newBase64 += `${key[currentChar]}=`
                }
                return newBase64
            }
            const image = "data:"+contentType+";base64," + byteToBase64(bytes);
            itemImage.postUpdate(image);

This is working in js for me!

1 Like

If you write that rule in JRuby this is how it would look like:

IMAGE_SOURCE = "http://OPENHABIANIP:8080/ipcamera/IPCAMERAUID/ipcamera.jpg"

rule "Send doorbell push with pics" do
  changed TRIGGERING_ITEM, from: OFF, to: ON
  run do
    TestImage.update_from_url IMAGE_SOURCE
    Notification.send "Test",
      icon: "motion",
      tag: "Tag",
      title: "Title",
      id: "id-1234",
      attachment: TestImage
  end
end

Or with a UI rule, just drop those two instructions + the IMAGE_SOURCE from within the “run do … end” block into your script editor.

@jimtng The JRuby solution is really extremely short and works perfectly.