Camera: Clickable thumbnail opens to a larger stream

Hi all,
It work for me:

        - component: widget:ClickableCameraImage
          config:
            streamURL: http://192.168.1.50:8048/snapshots.mjpeg
            showSettings: true
            showAlarms: true
            thumbnailURL: http://192.168.1.50:8048/ipcamera.jpg
            camera: FoscamCamera
            switchItem: FoscamCamera_EnableMotionAlarm

But if i use the android app on the local LAN, when I try the OH3 UI android app, I din´t get any video.

On the other hand, if I try the old Basic UI, with the same URL on the sitemap scrip, I´m able to get the video:

	    Text label="Camaras" icon="camera"
	    {
            Video url="http://192.168.1.50:8048/snapshots.mjpeg" encoding="mjpeg"
            //Image icon="camera" item=FoscamCamera_ImageURL url="http://192.168.1.50:8048/ipcamera.jpg" refresh=5000
            Default	item=FoscamCamera_EnableMotionAlarm	label="Habilitacion Alarma Deteccion Movimiento" 	valuecolor=[ON="red",OFF="green"]
    	    Default	item=FoscamCamera_MotionAlarm 		label="Deteccion Movimiento"	valuecolor=[ON="red",OFF="green"]
	        Switch 	item=FoscamCamera_AutoLED 	        label="AutoControl Infrared LED"
		    Switch 	item=FoscamCamera_LEDControls	    label="Habilitacion Infrared LED"  
        }

any thougths?

See the above posts which are all similar to yours, it works on my Samsung tablet with the app so it is probably a browser issue needing something updated or installed. Best to raise an issue on github against the app since it appears that web frames are also doing the same thing reported above.

@matt1
I just started with the MainUI as a means to power my wall tablets, so sorry for the very basic question:
I do only want to display camera snapshots, no videos. How can I increase the size of the snapshot image that is displayed with the widget? thanks.

Edit the widget code using the developers tools when logged in as a admin. Change the height and width settings which is just css code which you can use screen size in % or pixels as per here

Hello everybody :slight_smile:
I wonder if anyone has already managed to enable this widget with zoneminder bindign?
I will be very happy for any hint :slight_smile:

It should work just fine if you supply it with URLS from zoneminder. However be aware of this issue that may be causing headaches…

Display streams and snapshots from ipcamera binding in oh-image - Add-ons / UIs - openHAB Community

In openhab.log i’m getting this warning all the time I view the widget:

[e.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: MyCam_AudioAlarm

I didn’t configure an item for the audio alarm, because the camera is outside and with wind it doesn’t have any use. How can I disable this message without adding an AudioAlarm item?

uninstall and re add the widget as I just updated it to have show and hide of both the alarms separately. If you dont have the item created, just don’t tick the show alarm box and the message in the logs now wont appear.

Modified the widget for my doorphone. The first widget is for static image or moving MJEPG image. Second widget popups screenwide and can handle HLS stream for better quality.

uid: ClickablePTZCamera
tags:
  - video
props:
  parameters:
    - context: item
      label: Camera image URL
      name: camerajpeg
      required: true
      type: TEXT
    - context: item
      label: Camera stream URL
      name: camerahls
      required: false
      type: TEXT
    - context: item
      label: Motion item
      name: motionItem
      required: false
      type: TEXT
    - context: item
      label: Item for the door lock
      name: lockItem
      required: false
      type: TEXT
    - context: item
      label: Item for door state
      name: doorItem
      required: false
      type: TEXT
timestamp: Jul 30, 2022, 12:05:43 AM
component: f7-card
config:
  style:
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px var(--f7-bars-bg-color)
    color: var(--f7-text-color)
    max-height: 150px
    font-size: medium
    font-weight: 500
    noShadow: false
slots:
  default:
    - component: oh-image
      config:
        action: popover
        actionModal: widget:Video
        actionModalConfig:
          camerahls: =props.camerahls
          motionItem: =props.motionItem
          lockItem: =props.lockItem
          doorItem: =props.doorItem
        item: =[props.camerajpeg]
        lazy: false
        style:
          border-radius: var(--f7-card-expandable-border-radius)
          margin: 0px
          padding: 0px
          height: 150px
          width: 100%
          z-index: -2
    - component: f7-row
      config:
        style:
          position: absolute
          top: 2px
          width: 99%
          z-index: 30
      slots:
        default:
          - component: oh-icon
            config:
              icon: "=(items[props.doorItem].state === 'ON') ? 'door-closed' : 'door'"
              style:
                color: "=(items[props.doorItem].state === 'ON') ? 'cyan' : 'red'"
                z-index: 99
              visible: =props.doorItem !== undefined
              width: 22
          - component: oh-button
            config:
              action: command
              actionCommand: ON
              actionItem: =props.lockItem
              class: card-prevent-open
              iconF7: "=(items[props.lockItem].state === 'ON') ? 'lock_open' : 'lock'"
              style:
                margin-left: auto
                margin-right: auto
                color: "=(items[props.lockItem].state === 'ON') ? 'cyan' : 'var(--f7-card-header-text-color)'"
                z-index: 99
              visible: =props.lockItem !== undefined
              iconSize: 23
          - component: oh-icon
            config:
              icon: "=(items[props.motionItem].state === 'ON') ? 'mymotion-on' : 'mymotion-off'"
              style:
                z-index: 99
              visible: =props.motionItem !== undefined
              width: 23

uid: Video
tags:
  - video
props:
  parameters:
    - context: item
      label: Camera stream URL
      name: camerahls
      required: false
      type: TEXT
    - context: item
      label: Motion item
      name: motionItem
      required: false
      type: TEXT
    - context: item
      label: Item for the door lock
      name: lockItem
      required: false
      type: TEXT
    - context: item
      label: Item for door state
      name: doorItem
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jul 30, 2022, 12:07:00 AM
component: f7-card
config:
  class: no-margin
  style:
    --f7-card-margin-horizontal: 0px
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px var(--f7-bars-bg-color)
    color: var(--f7-text-color)
    font-size: medium
    font-weight: 500
    height: 100%
    margin: 5
    noShadow: false
    padding: 0
    text-shadow: 1px 0px 2px var(--f7-bars-bg-color), -1px 0px 2px var(--f7-bars-bg-color), 0px 0px 2px var(--f7-bars-bg-color), 0px 0px 3px var(--f7-bars-bg-color)
    width: 100%
slots:
  default:
    - component: oh-video-card
      config:
        hideControls: false
        item: =props.camerahls
        startManually: false
        style:
          height: 90%
    - component: f7-row
      config:
        style:
          position: absolute
          top: 10px
          width: 99%
          z-index: 30
      slots:
        default:
          - component: oh-icon
            config:
              icon: "=(items[props.doorItem].state === 'ON') ? 'door-closed' : 'door'"
              style:
                color: "=(items[props.doorItem].state === 'ON') ? 'cyan' : 'red'"
                z-index: 99
              visible: =props.doorItem !== undefined
              width: 30
          - component: oh-button
            config:
              action: command
              actionCommand: ON
              actionItem: =props.lockItem
              class: card-prevent-open
              iconF7: "=(items[props.lockItem].state === 'ON') ? 'lock_open' : 'lock'"
              style:
                margin-left: auto
                margin-right: auto
                color: "=(items[props.lockItem].state === 'ON') ? 'cyan' : 'var(--f7-card-header-text-color)'"
                z-index: 99
              visible: =props.lockItem !== undefined
              iconSize: 30
          - component: oh-icon
            config:
              icon: "=(items[props.motionItem].state === 'ON') ? 'mymotion-on' : 'mymotion-off'"
              style:
                z-index: 99
              visible: =props.motionItem !== undefined
              width: 30

1 Like

If your using a static image, you can get it to auto refresh with this…
oh-image - Image | openHAB

Also your first widget requires the second one to be created before the first one will work. Thanks for sharing how to get HLS working.

Hope together we will come someday to an universal widget, opening all cameras possibilities.

I have to call @JustinG for some magic. Trying to get ipcamera video record history widget working and a bit tired to figure out rather simple logic, so rewind and forward buttons don’t work.
MP4 history length contains a number of yet unrecorded mp4 file like if Domofon1.mp4 is present MP4 history length will be = 2. What I’m trying is to switch played file name, to enable widget user to browse through record history.

uid: videoArchive
tags:
  - videoArchive
props:
  parameters:
    - context: item
      description: Ipcamera MP4 history length item
      label: MP4 history length
      name: history
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Aug 9, 2022, 1:24:35 AM
component: f7-card
config:
  class: no-margin
  style:
    --f7-card-margin-horizontal: 0px
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px var(--f7-bars-bg-color)
    color: var(--f7-text-color)
    font-size: medium
    font-weight: 500
    height: auto
    margin: 5
    noShadow: false
    padding: 0
    text-shadow: 1px 0px 2px var(--f7-bars-bg-color), -1px 0px 2px var(--f7-bars-bg-color), 0px 0px 2px var(--f7-bars-bg-color), 0px 0px 3px var(--f7-bars-bg-color)
    width: 100%
slots:
  default:
    - component: oh-video-card
      config:
        hideControls: false
        url: '= vars.rewind != NULL ? "http://192.168.2.9:8080/static/doorphone/Domofon" + vars.rewind + ".mp4" : "http://192.168.2.9:8080/static/doorphone/Domofon" + (items[props.history].state -1) + ".mp4"'
        type: video/mp4
        startManually: false
        style:
          height: auto
          width: auto
    - component: f7-row
      config:
        style:
          position: absolute
          top: 10px
          width: 99%
          z-index: 30
      slots:
        default:
          - component: oh-button
            config:
              action: variable
              actionVariable: rewind
              actionVariableValue: '=(vars.rewind > 1 || rewind == NULL) ? (items[props.history].state - 1) : 1'
              iconColor: white
              iconF7: backward_end
              iconSize: 30
          - component: oh-button
            config:
              action: variable
              actionVariable: rewind
              actionVariableValue: '=vars.rewind < (items[props.history].state -1) ? (items[props.history].state + 1) : (items[props.history].state + 1)'
              iconColor: white
              iconF7: forward_end
              iconSize: 30

And I cannot close this widget popovered by button

          - component: oh-button
            config:
              iconColor: white
              iconF7: clear
              iconSize: 30
              popoverClose: widget:Video

The widget itself disappears from the screen, but still remains somehow active and it’s path is still in the address field of browser, so i’m getting an error if I reload openhab’s page.
And maybe it’s better to use actionPhotos for history widget, but I can’t even imagine how to dynamically form inside a widget it’s array of video URLs to get

  actionPhotos:
    - html: <video src="http://192.168.2.9:8080/static/doorphone/Domofon" + (items[props.history].state -1) + ".mp4"></video>
    - html: <video src="http://192.168.2.9:8080/static/doorphone/Domofon" + (items[props.history].state -2) + ".mp4"></video>
    - html: <video src="http://192.168.2.9:8080/static/doorphone/Domofon" + (items[props.history].state -3) + ".mp4"></video>

I see a few things going on here:

In the javascript-like expressions in the widget you rarely have to explicitly test if a variable is empty or undefined because both of these states are interpreted as false values and any existing state is interpreted as a true value. Just in case you ever do come across one of the situations where it is better to explicitly test if something exists, in these expressions it would be

vars.rewind === undefined

Also, instead of duplicating all the url, you can put the ternary expression right in the middle where you need the change. So this line can work better and be simplified to:

url: = "http://192.168.2.9:8080/static/doorphone/Domofon" + ((vars.rewind)?(vars.rewind):(items[props.history].state -1)) + ".mp4"

But I would take it even one step further. Because of the way or expressions are evaluated, you can use an even more convenient shortcut for using a default value if a variable doesn’t exist. The or expression will fully evaluate the first condition and if that condition is true it will return that value, only if the first condition is false does it both to evaluate the second condition. If the second condition is true it returns the second conditions value. That gives us:

url: = "http://192.168.2.9:8080/static/doorphone/Domofon" + (vars.rewind || items[props.history].state -1) + ".mp4"

Some of the same issue here (plus you forgot the vars in front of the rewind in the second condition):

But, more than that, 1) you don’t want to change the variable to the item state minus 1, you want to decrement the variable itself and 2) while it is probably best practice to test if vars.rewind exists before checking if it is greater than one, it’s not really necessary; ( vars.rewind > 1) will still be false if vars.rewind doesn’t exist . So formally this could look like:

actionVariableValue: =( vars.rewind && vars.rewind > 1 )?(vars.rewind - 1):1

But you could also reduce it to this:

actionVariableValue: =( vars.rewind > 1 )?(vars.rewind - 1):1

The main problem here:

appears to be that you have the same value for both the true result and the false result. This stems from the same issue as above (most of the time you want the new variable value to be based old variable value).

actionVariableValue: =( vars.rewind < (items[props.history].state -1))?(vars.rewind + 1):(items[props.history].state - 1)

The popoverClose property comes directly from the f7 base component, not the OH features overlaid on top. This means that this property doesn’t understand OH specific things such as widget or page IDs. If you are going to use this property then you have to set the value to a css selector that uniquely identifies your popver. Most of the time I do this just by adding a non-standard class name to the widget:

component: f7-card
config:
  class:
    - no-margin
    - video_history_popup

Then the appropriate css selector would look like this:

popoverClose: .popover.video_history_popup

I have to agree that this is probably not feasible at the moment (well…sufficiently non-trivial to probably not be worth it).

1 Like

Thanks, this one: actionVariableValue: =( vars.rewind > 1 )?(vars.rewind - 1):1 is genius. And with much more clear brains today i’ve seen my mistakes. Almost did it, but a rather big issue still remains - the video is not loading when i popup the widget until two switches from live to archive. And used all kind of css sizes, but still can’t fit the entire video into page - a small part in bottom is hidden and shows up only in fullscreen mode.

uid: Video
tags:
  - video
props:
  parameters:
    - context: item
      label: Camera stream URL
      name: camerahls
      required: false
      type: TEXT
    - context: item
      label: Motion item
      name: motionItem
      required: false
      type: TEXT
    - context: item
      label: Item for the door lock
      name: lockItem
      required: false
      type: TEXT
    - context: item
      label: Item for door state
      name: doorItem
      required: false
      type: TEXT
    - context: item
      description: Ipcamera MP4 history length item
      label: MP4 history length
      name: history
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Aug 10, 2022, 2:56:17 AM
component: f7-card
config:
  class: videoPopover
  key: '=(vars.rewind === undefined) ? Math.random() : Math.random() + vars.rewind'
  style:
    --f7-card-margin-horizontal: 0px
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px var(--f7-bars-bg-color)
    color: var(--f7-text-color)
    font-size: medium
    font-weight: 500
    height: auto
    margin: 5
    noShadow: false
    padding: 0
    text-shadow: 1px 0px 2px var(--f7-bars-bg-color), -1px 0px 2px var(--f7-bars-bg-color), 0px 0px 2px var(--f7-bars-bg-color), 0px 0px 3px var(--f7-bars-bg-color)
    width: 100%
slots:
  default:
    - component: oh-video-card
      config:
        hideControls: false
        startManually: false
        style:
          height: auto
          width: auto
        url: =items[props.camerahls].state
        visible: =vars.archive != 2
    - component: oh-video-card
      config:
        hideControls: false
        startManually: false
        style:
          height: auto
          width: auto
        url: ="http://192.168.2.9:8080/static/doorphone/Domofon" + (vars.rewind || items[props.history].state -1) + ".mp4"
        visible: =vars.archive == 2
    - component: f7-row
      config:
        style:
          position: absolute
          top: 10px
          width: 99%
          z-index: 30
      slots:
        default:
          - component: oh-button
            config:
              action: variable
              actionVariable: archive
              actionVariableValue: 2
              iconColor: white
              iconF7: folder
              iconSize: 30
              visible: =(vars.archive !== 2)
          - component: oh-button
            config:
              action: variable
              actionVariable: archive
              actionVariableValue: 1
              iconColor: white
              iconF7: videocam
              iconSize: 30
              visible: =(vars.archive == 2)
          - component: oh-button
            config:
              action: variable
              actionVariable: rewind
              actionVariableValue: =(vars.rewind != undefined)?(( vars.rewind > 1 )?(vars.rewind - 1):1):(items[props.history].state -2)
              iconColor: white
              iconF7: backward_end
              iconSize: 30
              visible: =(vars.archive == 2)
          - component: oh-icon
            config:
              icon: "=(items[props.doorItem].state === 'ON') ? 'door-closed' : 'door'"
              style:
                color: "=(items[props.doorItem].state === 'ON') ? 'cyan' : 'red'"
                z-index: 99
              visible: =((props.doorItem !== undefined) && (vars.archive !== 2))
              width: 30
          - component: oh-button
            config:
              action: command
              actionCommand: ON
              actionItem: =props.lockItem
              class: card-prevent-open
              iconF7: "=(items[props.lockItem].state === 'ON') ? 'lock_open' : 'lock'"
              iconSize: 30
              style:
                color: "=(items[props.lockItem].state === 'ON') ? 'cyan' : 'var(--f7-card-header-text-color)'"
                margin-left: auto
                margin-right: auto
                z-index: 99
              visible: =((props.lockItem !== undefined) && (vars.archive !== 2))
          - component: oh-icon
            config:
              icon: "=(items[props.motionItem].state === 'ON') ? 'mymotion-on' : 'mymotion-off'"
              style:
                z-index: 99
              visible: =((props.motionItem !== undefined) && (vars.archive !== 2))
              width: 30
          - component: oh-button
            config:
              action: variable
              actionVariable: rewind
              actionVariableValue: =( vars.rewind < items[props.history].state - 1)?(vars.rewind + 1):(items[props.history].state - 1)
              iconColor: white
              iconF7: forward_end
              iconSize: 30
              visible: =(vars.archive == 2)
          - component: oh-button
            config:
              iconColor: white
              iconF7: clear
              iconSize: 30
              popoverClose: .videoPopover
              style:
                z-index: 99
              tooltip: Закрыть

I don’t see any obvious reason for this behavior, and the widget appears to function as expected when tested in the widget editor. This is just a guess, but I can only surmise that this has something to do with calling an f7-card as a popup. Since you’re only using it as a container anyway and not really taking advantage of any card specific features, you might try changing the base component here to a f7-popup. That might also make the final bit of css easier to fix that last problem of hidden bottom.

1 Like

If i use f7-popup instead of f7-card or f7-block, then video height becomes NaN.
The error with non-loading video seems to come from parent widget not passing modalConfig (it’s shown as [object Object] in page inspector). Inside parent widget modalConfig is described like this:

        actionModalConfig:
          camerahls: =props.camerahls
          history: =props.history
          doorItem: =props.doorItem
          lockItem: =props.lockItem
          motionItem: =props.motionItem

But how then video becomes fine after some switching here and there and why buttons and door/motion states do work normally?
Tested with

    - component: oh-video
      config:
        hideControls: false
        startManually: false
        url: =items[props.camerahls].state

to get away from all these ternary and undefined variables and got the same issue! No video on initial popover, but if i close popover and reopen it again - then i see hls stream working from the very first milliseconds, as if the stream itself started in the first popover.

I have had a lot of trouble today playing with the - component: oh-video-card and using it with the url: I found behaviour similar to yours until I changed to using it like this…

- component: oh-video-card
  config:
    item: =[props.camera + '_HLSURL']

props.camera is the equipment level group, then all your member items can be guessed based off the auto naming convention that is used when you create equipment from things.

When ever you refresh the page, it fails a lot of the time if your using the url and not the item.

I can’t use item because for mp4 history i have to use URL. Maybe some kind of auto-refresh (like changing some variable inside key: ) needed on the first widget load.

Do you have more than one widget on a page calling this popup widget? I wonder if there’s some conflict with the settings.

It’s worth testing with matt1’s suggestion just to see if that is the problem. 1) Because it might be worth a github issue (if there isn’t one already) and 2) It would be possible (and only a little awkward) to convert this to a system that uses an item state instead of a variable if that turns out to be a fix.

Already tested, indeed if item: props.item is used instead of url: items[props.item].state, then there is no problem.