Unable to display camera image in DBv2

I migrated the flow from DBv1 which displays two of my Reolink cameras but the DBv2 flow does not display the images using the ui-template widget. Below are the details of my flow. Any advice/suggested changes are appreciated.

[
    {
        "id": "9be39e2b48730ea4",
        "type": "group",
        "z": "dc3ad1b3d9ba660c",
        "name": "Cameras",
        "style": {
            "label": true
        },
        "nodes": [
            "760b78eb9341149b",
            "b5737ad16fd1f0f9",
            "8b6bbda3b429e5ca",
            "6738810cb5074478",
            "263a6dd31663d25e",
            "177ac6c301333957",
            "ebb9ab80d276d1f2",
            "ec4a261edd755d3f",
            "e1d250fcd728f77a",
            "5dfb99eb0f678919"
        ],
        "x": 34,
        "y": 399,
        "w": 872,
        "h": 162
    },
    {
        "id": "760b78eb9341149b",
        "type": "http request",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "name": "Reolink image",
        "method": "GET",
        "ret": "bin",
        "paytoqs": "ignore",
        "url": "http://172.28.254.90/cgi-bin/api.cgi?cmd=Snap&channel=1&rs=&user=admin&password=Pat3net1",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "basic",
        "senderr": false,
        "headers": [],
        "x": 320,
        "y": 440,
        "wires": [
            [
                "8b6bbda3b429e5ca"
            ]
        ]
    },
    {
        "id": "b5737ad16fd1f0f9",
        "type": "inject",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 140,
        "y": 440,
        "wires": [
            [
                "760b78eb9341149b",
                "177ac6c301333957"
            ]
        ]
    },
    {
        "id": "8b6bbda3b429e5ca",
        "type": "base64",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "name": "",
        "action": "str",
        "property": "payload",
        "x": 500,
        "y": 440,
        "wires": [
            [
                "6738810cb5074478"
            ]
        ]
    },
    {
        "id": "6738810cb5074478",
        "type": "template",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "name": "",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<img width=\"320px\" height=\"240px\" src=\"data:image/jpg;base64,{{{payload}}}\">",
        "output": "str",
        "x": 660,
        "y": 440,
        "wires": [
            [
                "263a6dd31663d25e"
            ]
        ]
    },
    {
        "id": "263a6dd31663d25e",
        "type": "ui-template",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "group": "bb8416c482c3c19f",
        "page": "",
        "ui": "",
        "name": "Driveway",
        "order": 1,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<div ng-bind-html=\"msg.payload\"></div>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 820,
        "y": 440,
        "wires": [
            []
        ]
    },
    {
        "id": "177ac6c301333957",
        "type": "http request",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "name": "Reolink image",
        "method": "GET",
        "ret": "bin",
        "paytoqs": "ignore",
        "url": "http://172.28.254.90/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=&user=admin&password=Pat3net1",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "basic",
        "senderr": false,
        "headers": [],
        "x": 320,
        "y": 480,
        "wires": [
            [
                "ebb9ab80d276d1f2",
                "5dfb99eb0f678919"
            ]
        ]
    },
    {
        "id": "ebb9ab80d276d1f2",
        "type": "base64",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "name": "",
        "action": "str",
        "property": "payload",
        "x": 500,
        "y": 480,
        "wires": [
            [
                "ec4a261edd755d3f"
            ]
        ]
    },
    {
        "id": "ec4a261edd755d3f",
        "type": "template",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "name": "",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<img width=\"320px\" height=\"240px\" src=\"data:image/jpg;base64,{{{payload}}}\">",
        "output": "str",
        "x": 660,
        "y": 480,
        "wires": [
            [
                "e1d250fcd728f77a"
            ]
        ]
    },
    {
        "id": "e1d250fcd728f77a",
        "type": "ui-template",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "group": "bb8416c482c3c19f",
        "page": "",
        "ui": "",
        "name": "Doorbell",
        "order": 2,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<div ng-bind-html=\"msg.payload\"></div>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 820,
        "y": 480,
        "wires": [
            []
        ]
    },
    {
        "id": "5dfb99eb0f678919",
        "type": "image",
        "z": "dc3ad1b3d9ba660c",
        "g": "9be39e2b48730ea4",
        "name": "",
        "width": 160,
        "data": "payload",
        "dataType": "msg",
        "thumbnail": false,
        "active": true,
        "pass": false,
        "outputs": 0,
        "x": 520,
        "y": 520,
        "wires": []
    },
    {
        "id": "bb8416c482c3c19f",
        "type": "ui-group",
        "name": "Video Cameras",
        "page": "e181506f29ed6070",
        "width": "4",
        "height": "4",
        "order": 2,
        "showTitle": true,
        "className": "",
        "visible": "true",
        "disabled": "false",
        "groupType": "default"
    },
    {
        "id": "e181506f29ed6070",
        "type": "ui-page",
        "name": "Reolink",
        "ui": "6f4349e39827abbf",
        "path": "/reolink",
        "icon": "cctv",
        "layout": "grid",
        "theme": "b6221fc08b602ea9",
        "breakpoints": [
            {
                "name": "Default",
                "px": "0",
                "cols": "3"
            },
            {
                "name": "Tablet",
                "px": "576",
                "cols": "6"
            },
            {
                "name": "Small Desktop",
                "px": "768",
                "cols": "9"
            },
            {
                "name": "Desktop",
                "px": "1024",
                "cols": "12"
            }
        ],
        "order": 2,
        "className": "",
        "visible": true,
        "disabled": false
    },
    {
        "id": "6f4349e39827abbf",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "headerContent": "page",
        "navigationStyle": "default",
        "titleBarStyle": "default",
        "showReconnectNotification": true,
        "notificationDisplayTime": 1,
        "showDisconnectNotification": true,
        "allowInstall": true
    },
    {
        "id": "b6221fc08b602ea9",
        "type": "ui-theme",
        "name": "Dark Theme",
        "colors": {
            "surface": "#097479",
            "primary": "#097479",
            "bgPage": "#000000",
            "groupBg": "#000000",
            "groupOutline": "#097479"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px",
            "density": "default"
        }
    },
    {
        "id": "b29221f862e9bb9c",
        "type": "global-config",
        "env": [],
        "modules": {
            "node-red-node-base64": "1.0.0",
            "@flowfuse/node-red-dashboard": "1.26.0",
            "node-red-contrib-image-output": "0.6.4"
        }
    }
]

Your template is using AngularJS. Dashboard 2 is Vue.

use this kind of template .. loading and base64 encoding is same


<template>
  <v-card>
    <v-data-iterator :items="games" :items-per-page="3" :search="search">

    <template v-slot:header="{ page, pageCount, prevPage, nextPage }">
      <div class="d-flex align-center justify-center pa-0">
        <v-btn :disabled="page === 1" density="comfortable" icon="mdi-arrow-left" variant="tonal" rounded @click="prevPage">
        </v-btn>
    
        <v-btn :disabled="page >= pageCount" density="comfortable" icon="mdi-arrow-right" variant="tonal" rounded
          @click="nextPage"></v-btn>
      </div>
    </template>


      <template v-slot:default="{ items }">
        <v-container class="pa-0" >
          <v-row>
            <v-col v-for="item in items" :key="item.text">
              <v-card class="pb-0" color="grey">
                <v-img :src="`data:image/jpg;base64,${item.raw.pic}`"></v-img>

                      

                <div class="d-flex align-center justify-center px-1">
                  <div class="d-flex align-center text-caption text-medium-emphasis me-1">
                    <v-icon icon="mdi-clock" start></v-icon>

                    <div class="text-truncate">{{ item.raw.date }}-{{ item.raw.time }} >> {{ item.raw.type}}</div>
                  </div>
                </div>
              </v-card>
            </v-col>
          </v-row>
        </v-container>
      </template>

    </v-data-iterator>
  </v-card>
</template>








<script>
  export default {
    data: () => ({
      search: '',
      myAlert: 'Garden Alert',
      station: 'Driveway',
      games: [
        {
          pic: '',
          time: '8 minutes',
          date: '',
        },
      ],
    }),
        watch: {
            msg: function () {
              if (this.msg.payload != undefined) this.games = this.msg.payload;
              if (this.msg.station != undefined) this.station = this.msg.station;
            },
        },
        methods: {},
  }
</script>

should give a view like this (e.g. history of last 20 camera events .. depending on your array size for raw.pic)

1 Like

@xx_Nexus_xx … much thanks for the sample code…

I’m an extreme novice at CSS/Template so will try to modify your code to fit my situation.

Although the http request to your doorbell appears to work, I think rs needs a random string (the documentation says it should not be blank).

This is the syntax shown in Reolink's documentation (should be on a single line)

http://<camera_ip>/cgi-bin/api.cgi
cmd=Snap&channel=0&rs=12345678&user=admin&password=xxxx

In my Reolink setup I have &rs=snapshot (rather than some random numbers).

This flow is working with DBv1 as well as the image preview widget is displaying the image. I’m actually downloading the image from the NVR (not directly from the camera).

I do not know the actual ratio but there are now many Node-RED projects that involve video (camera) features (viewing, controlling, recording of video & audio, playback etc etc). I therefore think such features has matured to such a level that they should be a part of the basic Node-RED install

Also FlowFuse would benefit, and could piggy-back from such, especially since advanced remote monitoring is becoming more & more required (remote inspection possibilities are key for distributed sites for several reasons)

So question, is this something vital for core developments to grasp or should it be a free-floating topic heavily depending on attracted and long-term retained contributers?

I’m clueless about this new DBv2 template & v-card coding. I’m only trying to display the image in msg.payload and this is all I get so far.

Ok a blind squirrel found a nut (lol)… I changed the template to the code below and now have the image being displayed

<template>
    <v-card>
        <v-data-iterator :items="games" :items-per-page="1" :search="search">

            <template v-slot:header="{ page, pageCount, prevPage, nextPage }">
                <div class="d-flex align-center justify-center pa-0">
                    <v-btn :disabled="page === 1" density="comfortable" icon="mdi-arrow-left" variant="tonal" rounded
                        @click="prevPage">
                    </v-btn>
                    <v-btn :disabled="page >= pageCount" density="comfortable" icon="mdi-arrow-right" variant="tonal"
                        rounded @click="nextPage">
                    </v-btn>
                </div>
            </template>

            <template v-slot:default="{ items }">
                <v-container class="pa-0">
                    <v-row>
                        <v-col v-for="item in items" :key="item.text">
                            <v-card class="pb-0" color="grey">
                                <v-img :src="`data:image/jpg;base64,${payload}`"></v-img>

                                <div class="d-flex align-center justify-center px-1">
                                    <div class="d-flex align-center text-caption text-medium-emphasis me-1">
                                        <v-icon icon="mdi-clock" start></v-icon>
                                        <div class="text-truncate">{{ item.raw.date }}-{{ item.raw.time }} >> {{
                                            item.raw.type}}</div>
                                    </div>
                                </div>
                            </v-card>
                        </v-col>
                    </v-row>
                </v-container>
            </template>
        </v-data-iterator>
    </v-card>
</template>

<script>
    export default {
        data: () => ({
        search: '',
        myAlert: 'Garden Alert',
        station: 'Driveway',
        games: [
            {
            pic: '',
            time: '8 minutes',
            date: '',
            },
        ],
        }),
        watch: {
            msg: function () {
            if (this.msg.payload != undefined) this.games = this.msg.payload;
            if (this.msg.payload != undefined) this.payload = this.msg.payload;
            if (this.msg.station != undefined) this.station = this.msg.station;
            },
        },
        methods: {},
    }
</script>

in the original template you had to provide a structure with payload.raw.pic to display the image .. additionally you could have provided an array payload[ ].raw.pic and it would have displayed all pictures .. and you could have used the arrows to navigate fwd and backwd