Merge pull request #225 from dermotduffy/update-force

Add ability to force card auto-update
This commit is contained in:
Dermot Duffy
2021-11-28 15:49:09 -08:00
committed by GitHub
6 changed files with 64 additions and 19 deletions
+41 -16
View File
@@ -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.| |`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`).| |`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 ### Navigating From A Snapshot To A Clip
Clicking on a snapshot will take the user to a clip that was taken at the ~same Clicking on a snapshot will take the user to a clip that was taken at the ~same
@@ -813,6 +797,47 @@ menu:
</details> </details>
## 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 ## Troubleshooting
<a name="jsmpeg-troubleshooting"></a> <a name="jsmpeg-troubleshooting"></a>
+14 -2
View File
@@ -507,6 +507,10 @@ export class FrigateCard extends LitElement {
if (this.config.camera_entity) { if (this.config.camera_entity) {
this._entitiesToMonitor.push(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(); this._changeView();
} }
@@ -538,7 +542,8 @@ export class FrigateCard extends LitElement {
if (!this.config) { if (!this.config) {
return false; return false;
} }
if (changedProps.has('config')) {
if (changedProps.size > 1) {
return true; return true;
} }
@@ -550,7 +555,7 @@ export class FrigateCard extends LitElement {
// are browsing the mini-gallery). Do not allow re-rendering from a Home // 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 // Assistant update if there's been recent interaction (e.g. clicks on the
// card) or if there is media active playing. // 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 false;
} }
return shouldUpdateBasedOnHass(this._hass, oldHass, this._entitiesToMonitor); 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); handleAction(node, this._hass as HomeAssistant, config, ev.detail.action);
} }
this._resetInteractionTimer();
}
protected _resetInteractionTimer(): void {
if (this.config.view.timeout) { if (this.config.view.timeout) {
if (this._interactionTimerID) { if (this._interactionTimerID) {
window.clearTimeout(this._interactionTimerID); window.clearTimeout(this._interactionTimerID);
@@ -717,6 +725,10 @@ export class FrigateCard extends LitElement {
this._interactionTimerID = window.setTimeout(() => { this._interactionTimerID = window.setTimeout(() => {
this._interactionTimerID = null; this._interactionTimerID = null;
this._changeView(); 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); }, this.config.view.timeout * 1000);
} }
} }
+1
View File
@@ -11,6 +11,7 @@ export const CONF_FRIGATE_ZONE = 'frigate.zone';
export const CONF_VIEW_DEFAULT = 'view.default'; export const CONF_VIEW_DEFAULT = 'view.default';
export const CONF_VIEW_TIMEOUT = 'view.timeout'; 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_AUTOPLAY_CLIP = 'event_viewer.autoplay_clip';
export const CONF_EVENT_VIEWER_DRAGGABLE = 'event_viewer.draggable'; export const CONF_EVENT_VIEWER_DRAGGABLE = 'event_viewer.draggable';
+4
View File
@@ -53,6 +53,7 @@ import {
CONF_MENU_MODE, CONF_MENU_MODE,
CONF_VIEW_DEFAULT, CONF_VIEW_DEFAULT,
CONF_VIEW_TIMEOUT, CONF_VIEW_TIMEOUT,
CONF_VIEW_UPDATE_FORCE,
} from './const.js'; } from './const.js';
interface EditorOptionsSet { interface EditorOptionsSet {
@@ -403,6 +404,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
<div class="values"> <div class="values">
${this._renderDropdown(CONF_VIEW_DEFAULT, viewModes)} ${this._renderDropdown(CONF_VIEW_DEFAULT, viewModes)}
${this._renderStringInput(CONF_VIEW_TIMEOUT, '[0-9]')} ${this._renderStringInput(CONF_VIEW_TIMEOUT, '[0-9]')}
${this._renderSwitch(
CONF_VIEW_UPDATE_FORCE,
defaults.view.update_force)}
</div> </div>
` `
: ''} : ''}
+2 -1
View File
@@ -27,7 +27,8 @@
"snapshots": "Snapshots gallery", "snapshots": "Snapshots gallery",
"image": "Static image" "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": { "event_viewer": {
"autoplay_clip": "Autoplay most recent clip (In 'clip' view)", "autoplay_clip": "Autoplay most recent clip (In 'clip' view)",
+2
View File
@@ -323,6 +323,7 @@ const frigateConfigDefaultSchema = z
const viewConfigDefault = { const viewConfigDefault = {
default: 'live' as const, default: 'live' as const,
timeout: 180, timeout: 180,
update_force: false,
}; };
const viewConfigSchema = z const viewConfigSchema = z
.object({ .object({
@@ -340,6 +341,7 @@ const viewConfigSchema = z
) )
.optional() .optional()
.default(viewConfigDefault.timeout), .default(viewConfigDefault.timeout),
update_force: z.boolean().default(viewConfigDefault.update_force),
}) })
.merge(actionsSchema) .merge(actionsSchema)
.default(viewConfigDefault); .default(viewConfigDefault);