How to display mqtt image on uibuilder

Hi,

I can display images received via MQTT on the dashboard using ui_template but I have to send the same image to uibuilder page. I am pretty new to js and can't seem to make it work. Can anyone provide a minimal example or any hint?

Many thanks,

[
    {
        "id": "b58535ab.9ecdd",
        "type": "tab",
        "label": "Flow 2",
        "disabled": false,
        "info": ""
    },
    {
        "id": "4f0679b9.f33208",
        "type": "mqtt in",
        "z": "b58535ab.9ecdd",
        "name": "Image",
        "topic": "home/server/imd",
        "qos": "0",
        "datatype": "base64",
        "broker": "ee936a8f.2f801",
        "x": 70,
        "y": 300,
        "wires": [
            [
                "e4b73c13.021c88"
            ]
        ]
    },
    {
        "id": "e4b73c13.021c88",
        "type": "delay",
        "z": "b58535ab.9ecdd",
        "name": "",
        "pauseType": "rate",
        "timeout": "10",
        "timeoutUnits": "seconds",
        "rate": "3",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "x": 210,
        "y": 300,
        "wires": [
            [
                "3b48df99.15a67"
            ]
        ]
    },
    {
        "id": "3b48df99.15a67",
        "type": "base64",
        "z": "b58535ab.9ecdd",
        "name": "Encode",
        "action": "b64",
        "property": "payload",
        "x": 360,
        "y": 300,
        "wires": [
            [
                "f5360692.2c0658"
            ]
        ]
    },
    {
        "id": "f5360692.2c0658",
        "type": "ui_template",
        "z": "b58535ab.9ecdd",
        "group": "63037273.871b04",
        "name": "Detections",
        "order": 1,
        "width": "7",
        "height": "8",
        "format": "\n\n<img width=\"32\" height=\"32\" alt=\"\" src=\"data:image/jpg;base64,{{msg.payload}}\"/>\n",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "x": 550,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "ee936a8f.2f801",
        "type": "mqtt-broker",
        "name": "readMQTT",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": false,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    },
    {
        "id": "63037273.871b04",
        "type": "ui_group",
        "name": "test",
        "tab": "cd3008cf.a73998",
        "order": 1,
        "disp": true,
        "width": "7",
        "collapse": false
    },
    {
        "id": "cd3008cf.a73998",
        "type": "ui_tab",
        "name": "Home",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]

Hi, do you have an example msg payload to go with that flow? Then I could create an example.

Assuming you are using VueJS, all you need is to use an img tag with the source bound to a Vue data variable:

<img v-bind:src="'data:image/jpg;base64,' + dataVar" />

In the onChange function in your index.js, assign the incoming payload to the dataVar. You will probably want to use the msg.topic so that your onChange function can differentiate between the incoming image data and other types of data.

If using the default templates as a starting point, you can also use bootstrap-vue's b-img tag to help with responsiveness.

Hi, first of all, thank you for replying. I am totally new to this so please bear with me.

I am sending image from a python code:
_, buffer = cv2.imencode('.jpg', image)
jpg_as_text = base64.b64encode(buffer)
client.publish(MQTT_SEND_imgMD, jpg_as_text, qos=0)

where "image" is an array captured from a webcam. I receive this using the MQTT-in node in the above flow.

No problem, as long as the data you are outputting works in a data image link, you can do as indicated and it will all just work. I'll try to convert an image using an online service so that I can test just to make sure.

Many thanks.
I took one of your examples and edited it. I added

<img v-bind:src="'data:image/jpg;base64,' + dataVar" />

in index.html &

uibuilder.onChange('msg', function(msg) {
			    dataVar: msg.payload
		})

in index.js. I can't manage to have the image displayed on uibuilder page. Here is my test flow. This has sample payloads in inject node. Content for index.html, index.js are in the comments:

https://gist.githubusercontent.com/UranKhatolaaa/6aba35eae8c417fb23e31e540b37525d/raw/c9b9eb44b2ed4a1a85659692772425005d8177d3/test.json

@TotallyInformation
Hi Julian, this is a small flow I made that makes it easy to take an image, convert it to base64, send & receive via MQTT. I have installed the excellent image tools, node-red-contrib-image-tools, contributed by @Steve-Mcl to present the result. Anyway hope it helps

@noBody
I'm not sure you win anything, or very little, by converting the image data to base64. In your python code you could simply convert it to a string and then send it. I think there are better chances you find more possibilities "by default" that will present images (as buffer data) instead of base64. So instead in your python code you could simply do:
jpg_as_text = cv2.imencode('.jpg', image).tostring()

[{"id":"4c1839c2.5c8e58","type":"inject","z":"252b9369.44a80c","name":"","props":[{"p":"filename","v":"/home/pi/group-people.jpg","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":130,"y":3550,"wires":[["86f72cc0.83a57"]]},{"id":"86f72cc0.83a57","type":"file in","z":"252b9369.44a80c","name":"","filename":"","format":"","chunk":false,"sendError":false,"encoding":"none","x":310,"y":3550,"wires":[["e16db1dc.dee9c"]]},{"id":"9252018.e1436","type":"mqtt out","z":"252b9369.44a80c","name":"","topic":"pic_transfer","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"75eba16c.094f9","x":710,"y":3510,"wires":[]},{"id":"d332c3f.37f7c4","type":"mqtt in","z":"252b9369.44a80c","name":"","topic":"pic_transfer","qos":"2","datatype":"auto","broker":"75eba16c.094f9","nl":false,"rap":true,"rh":0,"x":710,"y":3580,"wires":[["6db57aae.c13114","758902e3.c8907c"]]},{"id":"758902e3.c8907c","type":"debug","z":"252b9369.44a80c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":950,"y":3580,"wires":[]},{"id":"6db57aae.c13114","type":"image viewer","z":"252b9369.44a80c","name":"","width":"161","data":"payload","dataType":"msg","x":930,"y":3640,"wires":[[]]},{"id":"e16db1dc.dee9c","type":"base64","z":"252b9369.44a80c","name":"","action":"","property":"payload","x":480,"y":3510,"wires":[["9252018.e1436","1aac4b6f.495255"]]},{"id":"1aac4b6f.495255","type":"debug","z":"252b9369.44a80c","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":480,"y":3660,"wires":[]},{"id":"75eba16c.094f9","type":"mqtt-broker","name":"","broker":"127.0.0.1","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"online","birthQos":"0","birthPayload":"BULB-1/LWT","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"offline","willQos":"0","willPayload":"BULB-1/LWT"}]

2 Likes

Hi, thank you for helping me out.

I tried this. It throws an error AttributeError("'tuple' object has no attribute 'tostring'",). I also tried it with str but the image does not show up in ui_template. Base64 is working fine.

Any hints on displaying mqqt image on uibuilder?

OK, so this works:

[{"id":"fc01834c.7727e","type":"inject","z":"857b334d.2d91a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":190,"y":640,"wires":[["69f88c9e.ccd584"]]},{"id":"69f88c9e.ccd584","type":"function","z":"857b334d.2d91a","name":"","func":"const data = \"data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAACPQABbjzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP8AAAAAjzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD///////Xw7f/18O3/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA//Xw7f/18O3/9fDt//Xw7f+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD////////////18O3/o2pG/6NqRv/18O3/jzcA/483AP///////////483AP+PNwD/jzcA/483AP+PNwD//////7+YgP+PNwD/jzcA/483AP+PNwD/////////////////jzcA//Xw7f+PNwD/jzcA/483AP/18O3//////483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP/18O3//v7////////w5+H/o2pG/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP/18O3//////483AP+PNwD//////483AP+PNwD/jzcA//Xw7f/18O3/9fDt//Xw7f/18O3/9fDt/7+YgP+PNwD/o2pG/483AP+PNwD/jzcA//////+jakb/jzcA//////+jakb/o2pG/6NqRv+jakb/o2pG/6NqRv//////jzcA/483AP+PNwD/jzcA/483AP+PNwD//v7/////////////jzcA/483AP+PNwD/jzcA/483AP+PNwD/9fDt/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD//////6NqRv+jakb/o2pG/6NqRv+jakb/o2pG//////+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP/18O3/9fDt//Xw7f/18O3/9fDt//Xw7f+/mID/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP8AAAAAjzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PNwD/jzcA/483AP+PQABbgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAA==\"\nmsg.payload = data\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":640,"wires":[["eb95241.bca25d8"]]},{"id":"2cf3f051.3dd33","type":"debug","z":"857b334d.2d91a","name":"simple-debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":740,"y":620,"wires":[]},{"id":"eb95241.bca25d8","type":"uibuilder","z":"857b334d.2d91a","name":"Simple Example","topic":"","url":"data_image","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"useSecurity":false,"sessionLength":"","tokenAutoExtend":false,"oldUrl":"uib_simple","x":530,"y":640,"wires":[["2cf3f051.3dd33"],["4e7c2860.c5c318"]]},{"id":"4e7c2860.c5c318","type":"debug","z":"857b334d.2d91a","name":"uib controls","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":730,"y":660,"wires":[]}]

html

<!doctype html><html lang="en"><head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>uibuilder simple example</title>
    <meta name="description" content="Node-RED uibuilder - Simple example using VueJS">

    <link rel="icon" href="./images/node-blue.ico">

    <!-- Put your own custom styles in here -->
    <link rel="stylesheet" href="./index.css" media="all">

</head><body>
    <!-- The "app" element is where the code for dynamic updates is attached -->
	<div id="app">
	    <h1>Demo of dynamic loading of data image</h1>
	    <p>
	        The elements below are dynamically updated when you send
	        a msg to your uibuilder node.
	    </p>
	    
	    <div v-if="msg.payload">
	        <img :src="imgData" />
	    </div>
	    
	    <h2>The full msg object</h2>
		<code>{{ msg }}</code>
		
	</div>

    <!-- Vendor Libraries - Load in the right order -->
    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
    <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>

    <!-- REQUIRED: Sets up Socket listeners, the uibuilder and msg objects -->
    <script src="./uibuilderfe.min.js"></script>

    <!-- Put your own custom code in here -->
    <script src="./index.js"></script>

</body></html>

JS

/* jshint browser: true, esversion: 5, asi: true */
/*globals uibuilder, Vue */
'use strict'

//const { default: Vue } = require( 'vue/types/umd' )

/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */

// @ts-ignore
var app1 = new Vue({
    // The HTML element to attach to
    el: '#app',
    /** Pre-defined data
	 *  Anything defined here can be used in the HTML
	 *  if you update it, the HTML will automatically update
	 */
    data: {
        msg: '[Nothing Recieved Yet]',
        imgData: '',
    },

    // Called after the Vue app has been created. A good place to put startup code
    created: function() {
        // **REQUIRED** Start uibuilder comms with Node-RED
        // @ts-ignore
        uibuilder.start()
    },

    // Called when Vue is fully loaded
    mounted: function() {
        // Keep a convenient reference to the Vue app
        var vueApp = this

        /** Triggered when the node on the Node-RED server
         *  recieves a (non-control) msg
         */
        // @ts-ignore
        uibuilder.onChange('msg', function(msg) {
            // @ts-ignore
            vueApp.msg = msg
            vueApp.imgData = msg.payload
        })

        // Send message back to node-red
        // uibuilder.send({payload:'some message'})
    },
	
}) // --- End of the Vue app definition --- //

// EOF

I've inserted the data image using a function node and created the data using an online tool. You can see how simple it is. 1 line of html and 5 lines of js in total.


And here is an updated flow (use the same html/js) that reads the image from a file and uses the base64 node to convert it then uses a change node to add the required "data:image/x-icon;base64," on the beginning (which you could do in the front end as originally described of course).

[{"id":"2cf3f051.3dd33","type":"debug","z":"857b334d.2d91a","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":795,"y":500,"wires":[],"l":false},{"id":"eb95241.bca25d8","type":"uibuilder","z":"857b334d.2d91a","name":"Simple Example","topic":"","url":"data_image","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"useSecurity":false,"sessionLength":"","tokenAutoExtend":false,"oldUrl":"uib_simple","x":650,"y":520,"wires":[["2cf3f051.3dd33"],["4e7c2860.c5c318"]]},{"id":"4e7c2860.c5c318","type":"debug","z":"857b334d.2d91a","name":"uib controls","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":795,"y":540,"wires":[],"l":false},{"id":"b3e74383.55a2c","type":"base64","z":"857b334d.2d91a","name":"","action":"","property":"payload","x":240,"y":520,"wires":[["231b7bab.66ef94"]]},{"id":"731e2b3f.79e3a4","type":"file in","z":"857b334d.2d91a","name":"","filename":"C:\\src\\nr2\\data\\uibuilder\\common\\images\\node-blue.ico","format":"","chunk":false,"sendError":false,"encoding":"none","x":135,"y":520,"wires":[["b3e74383.55a2c"]],"l":false},{"id":"c0c4f8b2.1fb358","type":"inject","z":"857b334d.2d91a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":75,"y":520,"wires":[["731e2b3f.79e3a4"]],"l":false},{"id":"231b7bab.66ef94","type":"change","z":"857b334d.2d91a","name":"","rules":[{"t":"change","p":"payload","pt":"msg","from":"^(.*)$","fromt":"re","to":"data:image/x-icon;base64,$1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":520,"wires":[["eb95241.bca25d8"]]}]

Hi, a big thank you. the updated flow worked very nicely.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.