Simple video matrix with patrol using conditional cards

I wanted a matrix that would work on a dash, mobile, or tablet. This matrix is setup for ten cameras. The large view is hidden from the selection view when displayed.

Every 10 seconds the large view rotates. If you click on any camera in the selection view it will halt the patrol for 45 second and then resume.

Mobile view

ezgif-6-256067676e

Dash full page view

First you will need to install the conditional card from hacs.

You will need to create a counter. Name the counter frontend_cams if you want to use the entity included with code provided. Use 1 through how ever many cams you want to use. For the setup shown it would be 1 to 10.

Individual card

type: vertical-stack
cards:
  - type: vertical-stack
    cards:
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 0.9
            below: 1.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_mailbox_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 1.9
            below: 2.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_front_step_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 2.9
            below: 3.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_front_garden_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 3.9
            below: 4.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_first_floor_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 4.9
            below: 5.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_second_floor_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 5.9
            below: 6.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_third_floor_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 6.9
            below: 7.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_laundry_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 7.9
            below: 8.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_shop_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 8.9
            below: 9.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_middle_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 9.9
            below: 10.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_back_yard_main
          aspect_ratio: 16 x 9
  - square: false
    type: grid
    cards:
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 1
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 1
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_mailbox_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 1
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 2
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 2
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_front_step_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 2
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 3
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 3
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_front_garden_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 3
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 4
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 4
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_first_floor_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 4
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 5
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 5
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_second_floor_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 5
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 6
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 6
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_third_floor_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 6
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 7
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 7
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_laundry_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 7
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 8
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 8
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_shop_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 8
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 9
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 9
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_middle_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 9
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 10
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 10
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_back_yard_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 10
          aspect_ratio: 16 x 9
    columns: 3

Full page

type: horizontal-stack
cards:
  - square: false
    type: grid
    cards:
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 1
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 1
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_mailbox_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 1
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 2
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 2
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_front_step_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 2
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 3
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 3
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_front_garden_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 3
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 4
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 4
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_first_floor_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 4
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 5
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 5
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_second_floor_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 5
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 6
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 6
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_third_floor_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 6
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 7
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 7
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_laundry_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 7
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 8
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 8
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_shop_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 8
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 9
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 9
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_middle_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 9
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity: counter.frontend_cams
                below: 10
              - condition: numeric_state
                entity: counter.frontend_cams
                above: 10
        card:
          camera_view: auto
          type: picture-glance
          entities: []
          camera_image: camera.xvr_back_yard_main
          tap_action:
            action: call-service
            service: counter.set_value
            target:
              entity_id: counter.frontend_cams
            data:
              value: 10
          aspect_ratio: 16 x 9
    columns: 3
  - type: vertical-stack
    cards:
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 0.9
            below: 1.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_mailbox_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 1.9
            below: 2.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_front_step_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 2.9
            below: 3.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_front_garden_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 3.9
            below: 4.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_first_floor_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 4.9
            below: 5.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_second_floor_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 5.9
            below: 6.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_third_floor_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 6.9
            below: 7.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_laundry_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 7.9
            below: 8.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_shop_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 8.9
            below: 9.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_middle_main
          aspect_ratio: 16 x 9
      - type: conditional
        conditions:
          - condition: numeric_state
            entity: counter.frontend_cams
            above: 9.9
            below: 10.1
        card:
          camera_view: live
          type: picture-glance
          entities: []
          camera_image: camera.xvr_back_yard_main
          aspect_ratio: 16 x 9

That will give you the cards, now the automation, I apologize I am not very well versed in jinga so I can only supply a nodered version. I will give you the steps on how the automation should be setup.

  1. A time based automation that every 10 seconds (adjust this for how fast the main image changes) calls the service counter.increment for counter.frontend_cams

  2. When the counter reaches 10 use counter.reset to return to 1

  3. You will need to create a binary sensor from an event action This entity will be used as a condition, this entity must be off for the counter to increment. How long you set this sensor to stay on, is how long the main screen will hold the selected camera.

Event you are looking for:

{
    "domain": "counter",
    "service": "set_value",
    "service_data": {
        "entity_id": [
            "counter.frontend_cams"
        ]
    }
}

Nodered users. To adjust the time of patrol, change time in the inject node. For how long the the main screen will hold when selected change time value in trigger node.

If you need to adjust the count of cameras, open the function node, where you see count === 10 change 10 to the total number of cameras.

[{"id":"6d333ecfbab3a392","type":"function","z":"60f2d2277843c698","name":"function 6","func":"var count = context.get(\"fccount\" || 1 );\n\nmsg = {};\n\nif( count === 10 ) {\n    count = 1;\n    msg.payload = {\n        \"domain\": \"counter\",\n        \"service\": \"reset\",\n       // \"target\": {\n           // \"entity_id\": \"input_number.frontend_cams\",\n       // }\n    }\n} else {\n    count += 1;\n    msg.payload = {\n        \"domain\": \"counter\",\n        \"service\": \"increment\",\n       // \"target\": {\n         //   \"entity_id\": \"input_number.frontend_cams\"\n          //  }\n    }\n};\n\ncontext.set(\"fccount\", count); //store it\nnode.status({ text: count });\n\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":920,"y":340,"wires":[["330998b81649d1fb"]]},{"id":"a8097c8b69bfcc8d","type":"inject","z":"60f2d2277843c698","name":"patrol time","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"3","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":750,"y":340,"wires":[["6d333ecfbab3a392"]]},{"id":"a702397e9ab9d11f","type":"api-call-service","z":"60f2d2277843c698","name":"","server":"6b1110b5.183a4","version":5,"debugenabled":false,"domain":"","service":"","areaId":[],"deviceId":[],"entityId":["counter.frontend_cams"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1310,"y":340,"wires":[[]]},{"id":"824e94f10f3a28d0","type":"server-events","z":"60f2d2277843c698","name":"","server":"6b1110b5.183a4","version":3,"exposeAsEntityConfig":"","eventType":"call_service","eventData":"{\"domain\":\"counter\",\"service\":\"set_value\",\"service_data\":{\"entity_id\":[\"counter.frontend_cams\"]}}","waitForRunning":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":710,"y":260,"wires":[["b662f7bd1bdce176"]]},{"id":"b662f7bd1bdce176","type":"trigger","z":"60f2d2277843c698","name":"hold main screen","op1":"stop","op2":"allow","op1type":"str","op2type":"str","duration":"45","extend":true,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":920,"y":260,"wires":[["330998b81649d1fb"]]},{"id":"330998b81649d1fb","type":"traffic","z":"60f2d2277843c698","name":"","property_allow":"payload","filter_allow":"allow","ignore_case_allow":false,"negate_allow":false,"send_allow":false,"property_stop":"payload","filter_stop":"stop","ignore_case_stop":false,"negate_stop":false,"send_stop":false,"default_start":true,"differ":false,"x":1130,"y":340,"wires":[["a702397e9ab9d11f"]]},{"id":"6b1110b5.183a4","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":false,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

Final note, you’ll need to get your streams working well. I am using the WebRTC addon.