Custom Home Assistant Lovelace card for browsing camera media in a clean timeline-style gallery with preview player, object filters, optional live view, and a built-in visual editor.
Current version: v2.11.0
- Click the button above
- Click Download
- Restart Home Assistant
Using sensor mode? Follow the steps below to set up your file sensors.
The FileTrack integration creates a sensor that scans a folder and exposes its contents as a fileList attribute — this is what the Camera Gallery Card reads in sensor mode.
FileTrack is a fork of the archived files integration by TarheelGrad1998.
- Open HACS
- Go to Integrations
- Click the three-dot menu and choose Custom repositories
- Add
https://github.com/TheScubadiver/FileTrackas an Integration - Search for FileTrack and install it
- Restart Home Assistant
- Go to Settings → Devices & Services and add FileTrack
Note
Once you have installed FileTrack, you can leave it alone. No need to configure anything in FileTrack — everything is configured in the card editor UI.
- Image & video preview
- Fullscreen image viewer — tap to open in fullscreen, rotates to landscape on mobile
- Timeline thumbnails with lazy loading
- Day grouping
- Filename timestamp parsing
- Object filter buttons with custom icon support
- Object detection pill in timestamp bar
- Horizontal or vertical thumbnail layout
- Mobile friendly
- Media type icon (image / video)
- Cover / Contain option for media display
- Sensor entities with
fileList - Home Assistant
media_source - Combined mode — use a sensor entity and a media source simultaneously, merged into a single timeline
- Multiple sensors or media folders
- Native Home Assistant WebRTC live preview
- Redesigned live view layout: camera name on the left, controls on the right
- Controls mode — choose between
overlay(controls fade out after inactivity) orfixed(always visible) - Native fullscreen button (iOS + Android/desktop)
- Pinch to zoom in fullscreen — touch, trackpad, and Ctrl + scroll wheel; pan with the mouse after zooming in
- Aspect ratio toggle — quickly switch between 16:9, 4:3 and 1:1, remembered per camera
- Live badge
- Multiple live cameras — configure several cameras and switch between them using chevron arrows
- Multi-camera grid layout (
live_layout: grid) — show all cameras at once, tap a tile to focus on one - Multiple RTSP streams — configure multiple named RTSP streams via
live_stream_urls - Default live camera
- Camera friendly names and entity IDs in selector
- Auto-teardown of stream + push-to-talk on view switch (no audio/bandwidth leak)
- Two-way audio (talkback) — tap the mic pill in live view to talk back through go2rtc. Per-camera — multi-camera setups can have mic talkback on some entries and not others; the pill appears/disappears as the user navigates the picker. Supports toggle and push-to-talk interaction, live input-level ring, four typed visual states (idle / connecting / active / error), retry on transient WebSocket glitches, screen-reader announcements, and reduced-motion fallback. Prerequisites: a camera with an audio backchannel speaker, the AlexxIT/WebRTC HACS integration installed (provides the signed
/api/webrtc/wsendpoint the card connects to), and a go2rtcstreams:entry that routes the camera's backchannel. Configure withlive_mic_streams: { <camera_id>: <go2rtc_stream> }(and optionallylive_mic_mode: ptt,live_mic_audio_processing, or custom ICE servers vialive_mic_ice_servers/live_mic_force_relay)
- Menu buttons — configure custom action buttons (toggle, navigate, perform action, etc.) accessible via a hamburger menu during live view
- Delete (sensor files + Frigate clips)
- Multiple delete with red selection style
- Download
- Long-press action menu
- WebSocket via HA integration (default) — uses the official Frigate HA integration's WS API. CORS-free, no
frigate_urlrequired, works with standalone Frigate containers - Direct REST API (
frigate_url, optional) — even faster if your Frigate is reachable from the browser - Automatic fallback: if REST fails, WS path takes over
- Automatic Frigate snapshot thumbnails
- Frigate clip delete via
rest_command— see Delete setup
- Automatic frame capture for all video sources in media source mode
- Frigate cameras: Frigate snapshot
- All other sources (NAS, Blue Iris, etc.): first-frame capture
- Sensor mode: first-frame capture
- Video autoplay toggle (gallery)
- Separate auto-mute toggle for gallery and live view
- Per-object filter color customization
Built-in Lovelace editor with tabs:
- General
- Viewer
- Live
- Thumbnails
- Styling
Features:
- Entity suggestions
- Media folder browser (starts at root)
- Field validation
- Object filter picker
- Controls mode dropdown (Overlay / Fixed)
- Menu buttons tab — configure action buttons with entity, icon, label and on/off icon
- Frigate URL field — set the direct Frigate API URL (shown in media and combined mode)
- Auto-detect path datetime format — scans your sources and suggests a working format
- Cleanup of legacy config keys
- Live preview in the HA card picker
- Create new FileTrack sensor from the General tab
Enable Debug mode in the General tab to surface a debug pill in live view. Tapping it opens a diagnostics modal with card version, HA info, Frigate state, runtime queue stats, and the active source/path. Useful for support questions and issue reports.
The Styling tab provides a visual editor for colors and border radius.
Show styling sections
| Section | Options |
|---|---|
| Card | Background, Border color, Border radius |
| Preview bar | Bar text color, Pill color |
| Thumbnails | Bar background, Bar text color, Border radius |
| Filter buttons | Background, Icon color, Active background, Active icon color, Border radius |
| Today / Date / Live | Text color, Chevron color, Live active color, Border radius |
The card supports two independent delete paths. Configure either or both — they're complementary:
For sensor mode items (and combined-mode sensor-backed items). Add a shell_command to your configuration.yaml:
shell_command:
gallery_delete: 'rm -f "{{ path }}"'Then in the card editor: General → Delete services → Sensor → pick shell_command.gallery_delete.
Note
Sensor delete is path-based. The path must start with /config/www/ (or resolve from /local/). Media-source URIs without a filesystem mapping can't use this path.
For Frigate event items in any source mode. Add a rest_command that calls Frigate's DELETE /api/events/<id> endpoint:
rest_command:
delete_frigate:
url: "http://frigate.local:5000/api/events/{{ event_id }}"
method: DELETEThen in the card editor: General → Delete services → Frigate → pick rest_command.delete_frigate.
Tip
Frigate's DELETE event endpoint wipes the clip, snapshot, and thumbnail in one call. The card hides paired items together after a successful delete.
delete_confirm— show confirmation dialog (default:true)allow_bulk_delete— enable multi-select bulk delete (default:true)
Show all configuration options
| Option | Description |
|---|---|
| Source | |
source_mode |
sensor, media, or combined |
entity / entities |
Sensor source(s) |
media_source / media_sources |
Media browser source(s) |
path_datetime_format |
Format pattern for parsing timestamps from paths (required for media / combined) |
max_media |
Max media items |
| Frigate | |
frigate_url |
Optional direct Frigate REST API URL (e.g. http://192.168.1.x:5000). If omitted, the card uses the HA Frigate integration via WebSocket |
| Gallery view | |
start_mode |
Default view: gallery or live |
preview_position |
top or bottom |
clean_mode |
Hide overlays when preview is closed |
object_fit |
Media display mode: cover or contain |
aspect_ratio |
Preview aspect ratio: 16:9, 4:3, or 1:1 |
autoplay |
Auto-play videos in gallery |
auto_muted |
Auto-mute videos in gallery |
| Thumbnails | |
thumb_layout |
horizontal or vertical |
thumb_size |
Thumbnail size in px |
thumb_bar_position |
Thumb timestamp bar position |
thumb_sort_order |
newest_first or oldest_first |
bar_position |
Preview timestamp bar position |
bar_opacity |
Preview timestamp bar opacity |
| Live view | |
live_enabled |
Enable live mode |
live_camera_entity |
Default camera entity for live view |
live_camera_entities |
Camera entities visible in the live picker |
live_layout |
single or grid (multi-camera) |
live_grid_labels |
Show camera name labels in grid mode |
live_stream_urls |
Array of named RTSP streams: [{url, name}] |
live_auto_muted |
Auto-mute audio in live view |
controls_mode |
Live controls display: overlay or fixed |
show_camera_title |
Show camera name in controls bar |
persistent_controls |
Always show controls (don't fade out) |
menu_buttons |
Configurable action buttons in the hamburger menu |
live_mic_streams |
Per-camera map of go2rtc backchannel streams. Keys are camera entity ids (camera.front_door) or synthetic stream ids (__cgc_stream_0__ — see caveat below); values are non-empty go2rtc stream names. The mic pill appears only on cameras present in the map. Once any row is filled, the map is authoritative — cameras absent from it have no mic. Example: live_mic_streams: { camera.front_door: front_door, camera.driveway: driveway } |
live_go2rtc_stream |
Legacy single-camera fallback. Applies globally only when live_mic_streams is empty/absent. Filling any row in live_mic_streams makes the map authoritative and this key is ignored |
live_go2rtc_url |
Optional. Direct connection URL to an external go2rtc instance (e.g. http://192.168.1.x:1984) — used by the live video fast-path only, not by the mic. Leave empty to route through the WebRTC integration |
live_mic_mode |
Mic interaction model: toggle (default — tap to start/stop) or ptt (push-to-talk — press and hold while speaking) |
live_mic_audio_processing |
Per-mic Web Audio constraints. All true by default. Sub-keys: echo_cancellation, noise_suppression, auto_gain_control. (Capture is always mono — channelCount: 1 — to halve upstream bandwidth without losing voice quality.) |
live_mic_ice_servers |
Advanced. Override the built-in STUN-only list with your own STUN/TURN servers. Same shape as WebRTC's RTCIceServer: [{urls: "...", username: "...", credential: "..."}, ...]. Default is STUN-only (Cloudflare + Google) — voice traffic stays direct and never proxies through a third party. Add a TURN entry here only if you're behind symmetric NAT (see Coturn example below) |
live_mic_force_relay |
Advanced. Set to true to force iceTransportPolicy: "relay" (TURN-only). Useless without a TURN server configured in live_mic_ice_servers |
| Filters | |
object_filters |
Filter buttons (built-in and custom) |
object_colors |
Color per object filter |
| Delete | |
delete_service |
Sensor file delete service (shell_command.*) |
frigate_delete_service |
Frigate clip delete service (rest_command.*) |
delete_confirm |
Show confirmation before deleting (default: true) |
allow_bulk_delete |
Enable bulk delete (default: true) |
| Layout & misc | |
card_height |
Card height in px |
pill_size |
Pill text size (px) — pills scale around this |
debug_enabled |
Show debug pill with diagnostics in live view |
style_variables |
Custom CSS variable overrides |
Multi-camera setup with mic on the two cameras that actually have speakers:
type: custom:camera-gallery-card
source_mode: media
live_enabled: true
live_camera_entities:
- camera.front_door # mic-capable doorbell
- camera.driveway # mic-capable PTZ
- camera.backyard # no speaker, intentionally omitted from the map
live_mic_streams:
camera.front_door: front_door # go2rtc streams: key
camera.driveway: driveway
live_mic_mode: ptt # press-and-hold; omit for tap-to-toggleCaveat for live_stream_urls (synthetic ids). When you put mic config on a stream-URL entry, the key is __cgc_stream_<N>__ where N is the position in live_stream_urls. If you reorder the array, the keys point at the wrong streams. Use the editor (it re-binds the inputs to the visible names) or stick to entity-keyed entries for stability.
Custom TURN (Coturn) for symmetric NAT. Default is STUN-only — voice traffic stays direct on your LAN. If you need a relay (e.g. talking to a Tailscale endpoint from a flaky cellular network):
live_mic_ice_servers:
- urls: stun:stun.cloudflare.com:3478
- urls:
- turn:turn.example.com:3478
- turns:turn.example.com:5349
username: your_username
credential: your_secret
# live_mic_force_relay: true # only if direct ICE never succeedsFor users with file sensors (FileTrack) pointing at local camera storage:
type: custom:camera-gallery-card
source_mode: sensor
entities:
- sensor.frontdoor_files
- sensor.driveway_files
delete_service: shell_command.gallery_delete
object_filters:
- person
- carFor Frigate add-on or standalone container — uses the HA WebSocket path, CORS-free:
type: custom:camera-gallery-card
source_mode: media
media_sources:
- media-source://frigate/main/event/clips/all/all/recent/7
path_datetime_format: YYYY/MM/DD/HHmmss
frigate_delete_service: rest_command.delete_frigate
live_enabled: true
live_camera_entities:
- camera.frontdoor
- camera.driveway
live_layout: single
object_filters:
- person
- carAll visual styling can be customized via the Styling tab in the editor.
Show all CSS variables
| Variable | Element | Default |
|---|---|---|
--cgc-card-bg |
Card background | theme card color |
--cgc-card-border-color |
Card border | theme divider color |
--r |
Card border radius | 10px |
--cgc-tsbar-txt |
Preview bar text color | #fff |
--cgc-pill-bg |
Preview pill background | theme secondary bg |
--cgc-tbar-bg |
Thumbnail bar background | theme secondary bg |
--cgc-tbar-txt |
Thumbnail bar text color | theme text color |
--cgc-thumb-radius |
Thumbnail border radius | 10px |
--cgc-obj-btn-bg |
Filter button background | theme secondary bg |
--cgc-obj-icon-color |
Filter icon color | theme text color |
--cgc-obj-btn-active-bg |
Active filter background | primary color |
--cgc-obj-icon-active-color |
Active filter icon color | #fff |
--cgc-obj-btn-radius |
Filter button border radius | 10px |
--cgc-ctrl-txt |
Today/date/live text color | theme secondary text |
--cgc-ctrl-chevron |
Date navigation chevron color | theme text color |
--cgc-live-active-bg |
Live button active background | error color |
--cgc-ctrl-radius |
Controls bar border radius | 10px |
--cgc-pill-size |
Pill text size (px). Fixed-mode pill height scales as 2 × this value |
14px |
Supported built-in filters:
bicycle · bird · bus · car · cat · dog · motorcycle · person · truck · visitor
Custom filters with a custom icon can be added via the editor.
Object filter colors can be assigned per filter type in the editor.
Tip
Recommended filename format for object detection:
2026-03-09_12-31-10_person.jpg
2026-03-09_12-31-10_car.mp4
The card extracts timestamps from a single configurable pattern, path_datetime_format, that matches the tail of each item's path (or media-source URI). The / character separates directory levels; the leaf segment matches the filename. The format is required for media and combined modes (Frigate REST is exempt — it uses event-id timestamps).
| Token | Meaning |
|---|---|
| YYYY | 4-digit year |
| YY | 2-digit year (pivots at 2000) |
| MM | 2-digit month |
| DD | 2-digit day |
| HH | 2-digit hour (24h) |
| mm | 2-digit minute |
| ss | 2-digit second |
| X | 10-digit Unix timestamp in seconds (decoded to local time) |
| x | 13-digit Unix timestamp in milliseconds (decoded to local time) |
Range filenames (start-end). When a format includes the same date/time token twice (or the X token twice), the first capture is used as the canonical timestamp and the second is treated as a structural anchor. This is what lets a single format string describe start-end-style filenames without picking the wrong instant.
The same knob handles three real-world archive shapes — and for nested archives the walker discovers the date tree without browsing inside day folders, then loads each day's files only when the user navigates to it. Very large archives (thousands of days) stay snappy.
A. Flat folder — all files in one directory
/media/cam/RLC_20260502_050106.mp4
└────date────┘└─time─┘
path_datetime_format: RLC_YYYYMMDD_HHmmss.mp4
The literal extension is optional — leave it off and the format matches as a substring of the filename, useful when timestamps are surrounded by other tokens (RLC-520A-front_00_20260502050106.mp4 matches YYYYMMDDHHmmss).
B. Date-named folder — recordings inside daily folders
/media/recordings/20260502/173154.mp4
└─date─┘ └─time─┘
path_datetime_format: YYYYMMDD/HHmmss
If the filenames carry no time tokens, write just YYYYMMDD — the card uses the folder name as the dayKey and shows files in their natural order.
C. Nested folders — year/month/day/file
/media/cam/Front/2026/04/30/RLC_20260430131245.mp4
└y─┘└m┘└d┘ └─────filename─────┘
path_datetime_format: YYYY/MM/DD/RLC_YYYYMMDDHHmmss.mp4
(Reolink/FTP shape — see issue #99.)
D. Unix epoch in the filename
/media/tapo/1706108297-1706108310.mp4
└──start──┘ └───end───┘
path_datetime_format: X-X
Tapo Control writes unix_start-unix_end.mp4; X-X captures both but uses the start as the canonical time. A single epoch (1706108297.mp4) is just X. UniFi Protect's .ubv files encode the millisecond epoch — B4FBE47EEF30_0_rotating_1642402659065.ubv matches x.
E. Calendar-range filenames
/media/reolink/cam_20240315083000_20240315083127.mp4
└────start────┘└─────end────┘
path_datetime_format: YYYYMMDDHHmmss_YYYYMMDDHHmmss
/media/dahua/2024-03-15/ch1/dav/11/11.00.01-11.00.59[M][0@0][0].mp4
└─start─┘└──end──┘
path_datetime_format: YYYY-MM-DD/HH.mm.ss-HH.mm.ss
Reolink SD-card exports and Dahua/Amcrest filenames carry two same-format timestamps; the first wins.
MIT License

