diff --git a/README.md b/README.md index ce6c0be9..69c522a9 100644 --- a/README.md +++ b/README.md @@ -458,22 +458,6 @@ This card supports several different views: |`clip`|Shows an event viewer for the most recent clip for this camera/zone/label. Can also be accessed by holding down the `clips` menu icon.| |`image`|Shows a static image specified by the `image` parameter, can be used as a discrete default view or a screensaver (via `view_timeout`).| -### Automatic Updates In The `clip` Or `snapshot` View - -Updates will occur whenever on every change of the state of the `camera_entity` -or any entity configured under `update_entities`. In particular, if the desire is -to have an auto-refreshing view of the most recent event, the `camera_entity` -will not be sufficient alone since the Home Assistant state for Frigate camera -entities does not change often. Instead, use the Frigate binary_sensor for that -camera (or any other entity at your discretion) to trigger the update: - -```yaml -update_entities: - - binary_sensor.office_person_motion -``` - -See the [other options](#other-options) above. - ### Navigating From A Snapshot To A Clip Clicking on a snapshot will take the user to a clip that was taken at the ~same @@ -813,6 +797,47 @@ menu: +## Card Refreshes / Updates + +Automated card refreshes / updates are minimized to avoid disruption to the +user, in particular when media is playing. Three sets of flags govern when the +card will automatically re-render in the absence of human interaction. + +The following table describes the behavior these 3 flags have. + +### Card Update Truth Table + +| `view.timeout` | `view.update_force` | `update_entities` & `camera_entity` | Behavior | +| :-: | :-: | :-: | - | +| Unset or `0` | *(Any value)* | Unset | Card will not automatically re-render. | +| Unset or `0` | `false` | *(Any entity)* | Card will reload **current** view when entity state changes, unless media is playing. | +| Unset or `0` | `true` | *(Any entity)* | Card will reload **current** view when entity state changes. | +| `X` seconds | `false` | Unset | Card will reload **default** view `X` seconds after human interaction stops, unless media is playing. | +| `X` seconds | `false` | *(Any entity)* | Card will reload **default** view `X` seconds after human interaction stops and reload the **current** view when entity state changes -- in both cases unless media is playing. | +| `X` seconds | `true` | Unset | Card will reload **default** view every `X` seconds. | +| `X` seconds | `true` | *(Any entity)* | Card will reload **default** view every `X` seconds and reload the **current** view when entity state changes. | + +### Usecases For Automated Refreshes + + * Refreshing the `live` thumbnails periodically. +```yaml +view: + default: live + timeout: 30 + force: true +``` + * Using `clip` or `snapshot` as the default view (for the most recent clip or + snapshot respectively) and having the card automatically refresh (to fetch a + newer clip/snapshot) when an entity state changes. A Frigate `camera_entity` + is generally not sufficient for this since the Home Assistant state for + Frigate camera entities does not change often. Instead, use the Frigate + binary_sensor for that camera (or any other entity at your discretion) to + trigger the update: +```yaml +update_entities: + - binary_sensor.office_person_motion +``` + ## Troubleshooting diff --git a/src/card.ts b/src/card.ts index c488465a..4948c8cc 100644 --- a/src/card.ts +++ b/src/card.ts @@ -507,6 +507,10 @@ export class FrigateCard extends LitElement { if (this.config.camera_entity) { this._entitiesToMonitor.push(this.config.camera_entity); } + if (this.config.view.update_force) { + // If update force is enabled, start a timer right away. + this._resetInteractionTimer(); + } this._changeView(); } @@ -538,7 +542,8 @@ export class FrigateCard extends LitElement { if (!this.config) { return false; } - if (changedProps.has('config')) { + + if (changedProps.size > 1) { return true; } @@ -550,7 +555,7 @@ export class FrigateCard extends LitElement { // are browsing the mini-gallery). Do not allow re-rendering from a Home // Assistant update if there's been recent interaction (e.g. clicks on the // card) or if there is media active playing. - if (this._interactionTimerID || this._mediaPlaying) { + if (!this.config.view.update_force && (this._interactionTimerID || this._mediaPlaying)) { return false; } return shouldUpdateBasedOnHass(this._hass, oldHass, this._entitiesToMonitor); @@ -709,7 +714,10 @@ export class FrigateCard extends LitElement { ) { handleAction(node, this._hass as HomeAssistant, config, ev.detail.action); } + this._resetInteractionTimer(); + } + protected _resetInteractionTimer(): void { if (this.config.view.timeout) { if (this._interactionTimerID) { window.clearTimeout(this._interactionTimerID); @@ -717,6 +725,10 @@ export class FrigateCard extends LitElement { this._interactionTimerID = window.setTimeout(() => { this._interactionTimerID = null; this._changeView(); + if (this.config.view.update_force) { + // If force is enabled, the timer just resets and starts over. + this._resetInteractionTimer(); + } }, this.config.view.timeout * 1000); } } diff --git a/src/const.ts b/src/const.ts index 7f243208..ec6f1e21 100644 --- a/src/const.ts +++ b/src/const.ts @@ -11,6 +11,7 @@ export const CONF_FRIGATE_ZONE = 'frigate.zone'; export const CONF_VIEW_DEFAULT = 'view.default'; export const CONF_VIEW_TIMEOUT = 'view.timeout'; +export const CONF_VIEW_UPDATE_FORCE = 'view.update_force'; export const CONF_EVENT_VIEWER_AUTOPLAY_CLIP = 'event_viewer.autoplay_clip'; export const CONF_EVENT_VIEWER_DRAGGABLE = 'event_viewer.draggable'; diff --git a/src/editor.ts b/src/editor.ts index 4e14fcb7..7683c523 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -53,6 +53,7 @@ import { CONF_MENU_MODE, CONF_VIEW_DEFAULT, CONF_VIEW_TIMEOUT, + CONF_VIEW_UPDATE_FORCE, } from './const.js'; interface EditorOptionsSet { @@ -403,6 +404,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
${this._renderDropdown(CONF_VIEW_DEFAULT, viewModes)} ${this._renderStringInput(CONF_VIEW_TIMEOUT, '[0-9]')} + ${this._renderSwitch( + CONF_VIEW_UPDATE_FORCE, + defaults.view.update_force)}
` : ''} diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json index de7f6ab5..f5270e82 100644 --- a/src/localize/languages/en.json +++ b/src/localize/languages/en.json @@ -27,7 +27,8 @@ "snapshots": "Snapshots gallery", "image": "Static image" }, - "timeout": "View timeout secs (before returning to default, 0=never)" + "timeout": "View timeout secs (before returning to default, 0=never)", + "update_force": "Force card updates (ignore media playing / interaction)" }, "event_viewer": { "autoplay_clip": "Autoplay most recent clip (In 'clip' view)", diff --git a/src/types.ts b/src/types.ts index d1bb6ca2..e279c1a2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -323,6 +323,7 @@ const frigateConfigDefaultSchema = z const viewConfigDefault = { default: 'live' as const, timeout: 180, + update_force: false, }; const viewConfigSchema = z .object({ @@ -340,6 +341,7 @@ const viewConfigSchema = z ) .optional() .default(viewConfigDefault.timeout), + update_force: z.boolean().default(viewConfigDefault.update_force), }) .merge(actionsSchema) .default(viewConfigDefault);