Camera: Image that can be clicked to open up larger, also with MP4 history + more

This widget allows you to view your live IP camera with JPG snapshots that refresh every second (configurable), then when clicked it will open up to a larger stream of the camera. When any MP4 recordings are made via the ipCamera binding, it will allow the folder icon to be clicked to show the recordings to be reviewed. Once you have seen all the recordings, you can click on the “X” button to clear the counter which will NOT delete the recordings. The X just resets the number of how many recordings that have been made since you last reviewed and cleared the counter. You need to be logged in as an administrator to see the X.
The video camera icon returns you to the live view. You can enter full screen mode if the live view or the recording contains something of interest.

To get this widget working you MUST name your items with the default naming that occurs when you use the feature “Add equipment to model” from the things channel page (found at very bottom of page). If you manually create items, then the following items will need to be named as follows and linked to the channels of the same name:

EquipmentName (this is the item you select in the widget’s setup)
EquipmentName_MP4_History
EquipmentName_HLS_URL
EquipmentName_MP4_History_Length

The Base Url config needs to be set to use your openHAB IP address and also be updated to include the cameras unique ID. This will look like this and will end with a /

http://192.168.1.2:8080/ipcamera/19216813/
The better way is to use relative URLs so it works via http and https like…
/ipcamera/19216813/

Resources

uid: ClickableJpgWithHistory
props:
  parameters:
    - description: "example: http://192.168.1.2:8080/ipcamera/CameraUniqueID/"
      label: Base URL /ipcamera/CameraUniqueID/
      name: cameraBaseURL
      required: true
      type: TEXT
    - description: /ipcamera/CameraUniqueID/ipcamera.mjpeg
      label: Stream URL (Relative or Absolute)
      name: streamURL
      required: true
      type: TEXT
    - context: item
      label: Select the Camera (Equipment)
      name: camera
      required: true
      type: TEXT
    - context: item
      label: Item to Switch
      name: switchItem
      required: false
      type: TEXT
      advanced: true
    - default: "1000"
      description: The refresh rate of the thumbnail image
      label: Refresh rate ms
      name: refreshRate
      required: false
      type: INTEGER
      min: 1000
      max: 100000
      advanced: true
    - default: "true"
      label: Show Equipment Controls
      name: showSettings
      required: false
      type: BOOLEAN
      advanced: true
    - default: "false"
      label: Show Audio Alarms
      name: showAudioAlarms
      required: false
      type: BOOLEAN
      advanced: true
    - default: "true"
      label: Show Motion Alarms
      name: showMotionAlarms
      required: false
      type: BOOLEAN
      advanced: true
    - default: "true"
      label: Show Recording Icon
      name: showRecording
      required: false
      type: BOOLEAN
      advanced: true
  parameterGroups: []
timestamp: Mar 10, 2025, 4:55:42 PM
component: f7-card
config:
  key: "=(vars.selected === undefined) ? Math.random() : Math.random() +
    vars.selected"
  style:
    --f7-card-margin-horizontal: 0px
    height: 9rem
    width: 16rem
slots:
  default:
    - component: oh-video-card
      config:
        hideControls: false
        startManually: false
        url: =props.cameraBaseURL + items[props.camera +
          '_MP4_History'].state.split(",")[(vars.selected || 0)] +".mp4"
        visible: =vars.archive == 2
    - component: oh-image-card
      config:
        action: photos
        actionPhotoBrowserConfig:
          lazy: true
          theme: dark
          type: popup
        actionPhotos: =[props.streamURL]
        refreshInterval: =props.refreshRate
        style:
          border-radius: 6px
          height: 9rem
          margin: 0px
          width: 100%
        url: =(props.cameraBaseURL + 'ipcamera.jpg')
    - component: f7-row
      config:
        class: no-gap
        style:
          position: absolute
          top: 0px
          width: 99%
          z-index: 2
      slots:
        default:
          - component: oh-button
            config:
              action: variable
              actionVariable: archive
              actionVariableValue: 2
              iconColor: white
              iconF7: folder
              iconSize: 28
              style:
                margin: -2px
              visible: "=(items[props.camera + '_MP4_History'].state !== NULL &&
                items[props.camera + '_MP4_History_Length'].state > 0 ?
                vars.archive !== 2 : false)"
              z-index: 2
          - component: Label
            config:
              style:
                color: white
                font-size: 9px
                left: 18px
                position: absolute
                top: 12px
                z-index: -1
              text: =items[props.camera+'_MP4_History_Length'].state
              visible: =vars.archive != 2 && items[props.camera+'_MP4_History_Length'].state >
                0
          - component: Label
            config:
              style:
                color: white
                font-size: 9px
                left: 12px
                position: absolute
                top: 9px
                z-index: -1
              text: =((vars.selected || 0)+1)
              visible: =vars.archive == 2
          - component: oh-button
            config:
              action: variable
              actionVariable: archive
              actionVariableValue: 1
              iconColor: white
              iconF7: videocam
              iconSize: 28
              style:
                margin: -2px
              visible: =(vars.archive == 2)
          - component: oh-button
            config:
              action: variable
              actionVariable: selected
              actionVariableValue: =(items[props.camera +'_MP4_History_Length'].state-1)
              iconColor: white
              iconF7: backward_end
              iconSize: 28
              style:
                margin: -2px
              visible: =vars.archive == 2 && items[props.camera+'_MP4_History_Length'].state >
                0
          - component: oh-button
            config:
              action: variable
              actionVariable: selected
              actionVariableValue: =((vars.selected || 0)+1 < items[props.camera
                +'_MP4_History_Length'].state)?((vars.selected ||
                0)+1):(items[props.camera +'_MP4_History_Length'].state-1)
              iconColor: white
              iconF7: arrowtriangle_left
              iconSize: 28
              style:
                margin: -2px
              visible: =vars.archive == 2 && items[props.camera+'_MP4_History_Length'].state >
                0
          - component: oh-button
            config:
              action: variable
              actionVariable: selected
              actionVariableValue: =((vars.selected || 0) != 0)?((vars.selected || 0)-1):(0)
              iconColor: white
              iconF7: arrowtriangle_right
              iconSize: 28
              style:
                margin: -2px
              visible: =vars.archive == 2 && items[props.camera+'_MP4_History_Length'].state >
                0
          - component: oh-button
            config:
              action: variable
              actionVariable: selected
              actionVariableValue: 0
              iconColor: white
              iconF7: forward_end
              iconSize: 28
              style:
                margin: -2px
              visible: =vars.archive == 2 && items[props.camera+'_MP4_History_Length'].state >
                0
          - component: oh-button
            config:
              action: toggle
              actionCommand: "0"
              actionItem: =props.camera+'_MP4_History_Length'
              iconColor: white
              iconF7: clear
              iconSize: 28
              style:
                margin: -2px
              tooltip: Clear cameras mp4 history
              visible: =vars.archive == 2 && items[props.camera+'_MP4_History_Length'].state >
                0
              visibleTo:
                - role:administrator
          - component: oh-link
            config:
              action: toggle
              actionCommand: ON
              actionCommandAlt: OFF
              actionItem: =props.switchItem
              iconF7: power
              iconSize: 28
              style:
                color: "=(items[props.switchItem].state === 'ON') ? 'cyan' : 'white'"
                opacity: "=(items[props.switchItem].state === 'ON') ? '0.8' : '0.7'"
              visible: =props.switchItem !== undefined && vars.archive !== 2
          - component: oh-link
            config:
              action: group
              actionGroupPopupItem: =props.camera
              iconF7: gear_alt
              iconSize: 28
              style:
                color: white
                opacity: 0.7
              visible: =props.showSettings === true && vars.archive !== 2
              visibleTo:
                - role:administrator
    - component: f7-row
      config:
        class: no-gap
        style:
          position: absolute
          top: 7.7rem
          width: 99%
          z-index: 2
      slots:
        default:
          - component: oh-link
            config:
              color: red
              iconF7: videocam_circle
              iconSize: 22
              style:
                left: 0.2rem
                opacity: 0.7
                position: absolute
              visible: =props.showRecording === true && items[props.camera +
                '_MP4_Recording'].state > 0 && vars.archive !== 2
          - component: oh-link
            config:
              iconF7: eye
              iconSize: 22
              style:
                color: white
                opacity: "=(items[props.camera + '_Motion_Alarm'].state === 'ON') ? '0.7' : '0'"
                position: absolute
                right: 0.2rem
              visible: =props.showMotionAlarms === true
          - component: oh-link
            config:
              iconF7: ear
              iconSize: 22
              style:
                color: white
                left: 1.2rem
                opacity: "=(items[props.camera + '_Audio_Alarm'].state === 'ON') ? '0.7' : '0'"
                position: absolute
              visible: =props.showAudioAlarms === true
2 Likes

Posted updated widget code that fixes the switch when pressed.
Added default values to the switches in case people do not know the advanced page of settings is there.
Minor formatting changes to give some consistency between the camera widgets.