Separate timeout_seconds from refresh_seconds .
This commit is contained in:
@@ -135,12 +135,12 @@ view:
|
||||
| Option | Default | Overridable | Description |
|
||||
| - | - | - | - |
|
||||
| `default` | `live` | :heavy_multiplication_x: | The view to show in the card by default. See [views](#views) below.|
|
||||
| `timeout_seconds` | | :heavy_multiplication_x: | A numbers of seconds of inactivity after which the card will reset to the default configured view. Inactivity is defined as lack of interaction with the Frigate menu.|
|
||||
| `actions` | | :heavy_multiplication_x: | Actions to use for all views, individual actions may be overriden by view-specific actions. See [actions](#actions) below.|
|
||||
| `update_force` | `false` | :heavy_multiplication_x: | Whether card updates/refreshes should ignore playing media and human interaction. See [card updates](#card-updates) below for behavior and usecases.|
|
||||
| `timeout_seconds` | `300` | :heavy_multiplication_x: | A numbers of seconds of inactivity after human interaction, after which the card will reset to the default configured view (i.e. 'screensaver' functionality). Inactivity is defined as lack of mouse/touch interaction with the Frigate card. If the default view occurs sooner (e.g. via `update_seconds` or manually) the timer will be stopped. `0` means disable this functionality. |
|
||||
| `update_seconds` | `0` | :heavy_multiplication_x: | A number of seconds after which to automatically update/refresh the default view. See [card updates](#card-updates) below for behavior and usecases. If the default view occurs sooner (e.g. manually) the timer will start over. `0` disables this functionality.|
|
||||
| `update_force` | `false` | :heavy_multiplication_x: | Whether automated card updates/refreshes should ignore human interaction. See [card updates](#card-updates) below for behavior and usecases.|
|
||||
| `update_entities` | | :heavy_multiplication_x: | **YAML only**: A list of entity ids that should cause the view to reset to the default. See [card updates](#card-updates) below for behavior and usecases.|
|
||||
| `update_cycle_camera` | `false` | :heavy_multiplication_x: | When set to `true` the selected camera is cycled on each default view change. |
|
||||
|
||||
| `actions` | | :heavy_multiplication_x: | Actions to use for all views, individual actions may be overriden by view-specific actions. See [actions](#actions) below.|
|
||||
### Menu Options
|
||||
|
||||
All configuration is under:
|
||||
@@ -412,8 +412,6 @@ item, that has both of the following parameters set:
|
||||
| `conditions` | | :heavy_multiplication_x: | A set of conditions that must evaluate to `true` in order for the overrides to be applied. See [Frigate Card Conditions](#frigate-card-conditions). |
|
||||
| `overrides` | | :heavy_multiplication_x: |Configuration overrides to be applied. Any configuration parameter described in this documentation as 'Overridable' is supported. |
|
||||
|
||||
|
||||
|
||||
### Using WebRTC
|
||||
|
||||
WebRTC support blends the use of the ultra-realtime [WebRTC live
|
||||
@@ -1099,31 +1097,34 @@ image:
|
||||
|
||||
## Card Refreshes
|
||||
|
||||
Three sets of flags govern when the card will automatically re-render in the
|
||||
Four sets of flags govern when the card will automatically refresh in the
|
||||
absence of human interaction.
|
||||
|
||||
The following table describes the behavior these 3 flags have.
|
||||
The following table describes the behavior these flags have.
|
||||
|
||||
### Card Update Truth Table
|
||||
|
||||
| `view.timeout_seconds` | `view.update_force` | `view.update_entities` | Behavior |
|
||||
| :-: | :-: | :-: | - |
|
||||
| Unset or `0` | *(Any value)* | Unset | Card will not automatically refresh. |
|
||||
| Unset or `0` | `false` | *(Any entity)* | Card will reload default view when entity state changes, unless media is playing. |
|
||||
| Unset or `0` | `true` | *(Any entity)* | Card will reload default 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 or 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 or when entity state changes. |
|
||||
| `view.update_seconds` | `view.timeout_seconds` | `view.update_force` | `view.update_entities` | Behavior |
|
||||
| :-: | :-: | :-: | :-: | - |
|
||||
| `0` | `0` | *(Any value)* | Unset | Card will not automatically refresh. |
|
||||
| `0` | `0` | *(Any value)* | *(Any entity)* | Card will reload default view when entity state changes. |
|
||||
| `0` | `X` seconds | *(Any value)* | Unset | Card will reload default view `X` seconds after human interaction stops. |
|
||||
| `0` | `X` seconds | `false` | *(Any entity)* | Card will reload default view `X` seconds after human interaction stops, or when entity state changes (as long as human interaction has not occurred in the last `X` seconds). |
|
||||
| `0` | `X` seconds | `true` | *(Any entity)* | Card will reload default view `X` seconds after human interaction stops or when entity state changes. |
|
||||
| `Y` seconds | `0` | *(Any value)* | Unset | Card will reload default view every `Y` seconds. |
|
||||
| `Y` seconds | `0` | *(Any value)* | *(Any entity)* | Card will reload default view every `Y` seconds, or whenever entity state changes. |
|
||||
| `Y` seconds | `X` seconds | `false` | Unset | Card will reload default view `X` seconds after human interaction stops, and every `Y` seconds (as long as there hasn't been human interaction in the last `X` seconds). |
|
||||
| `Y` seconds | `X` seconds | `false` | *(Any entity)* | Card will reload default view `X` seconds after human interaction stops, and every `Y` seconds or whenever entity state changes (in both cases -- as long as there hasn't been human interaction in the last `X` seconds). |
|
||||
| `Y` seconds | `X` seconds | `true` | Unset | Card will reload default view `X` seconds after human interaction stops, and every `Y` seconds. |
|
||||
| `Y` seconds | `X` seconds | `true` | *(Any entity)* | Card will reload default view `X` seconds after human interaction stops, and every `Y` seconds or whenever entity state changes. |
|
||||
|
||||
### Usecases For Automated Refreshes
|
||||
|
||||
* Refreshing the `live` thumbnails periodically.
|
||||
* Refreshing the `live` thumbnails every 30 seconds.
|
||||
```yaml
|
||||
view:
|
||||
default: live
|
||||
timeout_seconds: 30
|
||||
force: true
|
||||
update_seconds: 30
|
||||
```
|
||||
* 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
|
||||
@@ -1135,6 +1136,19 @@ view:
|
||||
update_entities:
|
||||
- binary_sensor.office_person_motion
|
||||
```
|
||||
* Cycle the live view of the camera every 60 seconds
|
||||
```yaml
|
||||
view:
|
||||
update_cycle_camera: true
|
||||
update_seconds: 60
|
||||
```
|
||||
* Return to the most recent clip of the default camera 30 seconds after human
|
||||
interaction with the card stops.
|
||||
```yaml
|
||||
view:
|
||||
default: clip
|
||||
timeout_seconds: 30
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
+67
-38
@@ -151,11 +151,12 @@ export class FrigateCard extends LitElement {
|
||||
@query('frigate-card-elements')
|
||||
_elements?: FrigateCardElements;
|
||||
|
||||
// Human interaction timer ID.
|
||||
// Human interaction timer ("screensaver" functionality, return to default
|
||||
// view after human interaction).
|
||||
protected _interactionTimerID: number | null = null;
|
||||
|
||||
// Whether or not media is actively playing (live or clip).
|
||||
protected _mediaPlaying = false;
|
||||
// Automated refreshes of the default view.
|
||||
protected _updateTimerID: number | null = null;
|
||||
|
||||
// Information about the most recently loaded media item.
|
||||
protected _mediaShowInfo: MediaShowInfo | null = null;
|
||||
@@ -619,10 +620,6 @@ export class FrigateCard extends LitElement {
|
||||
this._cameras = undefined;
|
||||
this._view = undefined;
|
||||
|
||||
if (this._getConfig().view.update_force) {
|
||||
// If update force is enabled, start a timer right away.
|
||||
this._resetInteractionTimer();
|
||||
}
|
||||
this._changeView();
|
||||
}
|
||||
|
||||
@@ -640,6 +637,7 @@ export class FrigateCard extends LitElement {
|
||||
}
|
||||
|
||||
if (args?.view === undefined) {
|
||||
// Load the default view.
|
||||
let camera = this._view?.camera;
|
||||
if (this._cameras?.size) {
|
||||
if (!camera) {
|
||||
@@ -658,6 +656,14 @@ export class FrigateCard extends LitElement {
|
||||
camera: camera,
|
||||
});
|
||||
this._generateConditionState();
|
||||
|
||||
// The default view has been loaded, so can abandon any running
|
||||
// 'screensaver' timer.
|
||||
this._clearInteractionTimer();
|
||||
|
||||
// Restart the update timer, so the default view is refreshed at a fixed
|
||||
// interval from now (if so configured).
|
||||
this._startUpdateTimer();
|
||||
}
|
||||
} else {
|
||||
this._view = args.view;
|
||||
@@ -692,8 +698,7 @@ export class FrigateCard extends LitElement {
|
||||
// Assistant update if there's been recent interaction (e.g. clicks on the
|
||||
// card) or if there is media active playing.
|
||||
if (
|
||||
(this._getConfig().view.update_force ||
|
||||
!(this._interactionTimerID && this._mediaPlaying)) &&
|
||||
this._isAutomatedViewUpdateAllowed() &&
|
||||
shouldUpdateBasedOnHass(
|
||||
this._hass,
|
||||
oldHass,
|
||||
@@ -701,9 +706,8 @@ export class FrigateCard extends LitElement {
|
||||
)
|
||||
) {
|
||||
// If entities being monitored have changed then reset the view to the
|
||||
// default and allow a re-render. Note that as per the Lit lifecycle,
|
||||
// the setting of the view itself will not trigger an *additional*
|
||||
// re-render here.
|
||||
// default. Note that as per the Lit lifecycle, the setting of the view
|
||||
// itself will not trigger an *additional* re-render here.
|
||||
this._changeView();
|
||||
return true;
|
||||
}
|
||||
@@ -891,25 +895,66 @@ export class FrigateCard extends LitElement {
|
||||
) {
|
||||
handleAction(node, this._hass as HomeAssistant, config, ev.detail.action);
|
||||
}
|
||||
this._resetInteractionTimer();
|
||||
|
||||
// Set the 'screensaver' timer.
|
||||
this._startInteractionTimer();
|
||||
}
|
||||
|
||||
protected _resetInteractionTimer(): void {
|
||||
/**
|
||||
* Clear the human interaction ('screensaver') timer.
|
||||
*/
|
||||
protected _clearInteractionTimer(): void {
|
||||
if (this._interactionTimerID) {
|
||||
window.clearTimeout(this._interactionTimerID);
|
||||
this._interactionTimerID = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the human interaction ('screensaver') timer to reset the view to
|
||||
* default `view.timeout_seconds` after human interaction.
|
||||
*/
|
||||
protected _startInteractionTimer(): void {
|
||||
this._clearInteractionTimer();
|
||||
if (this._getConfig().view.timeout_seconds) {
|
||||
if (this._interactionTimerID) {
|
||||
window.clearTimeout(this._interactionTimerID);
|
||||
}
|
||||
this._interactionTimerID = window.setTimeout(() => {
|
||||
this._interactionTimerID = null;
|
||||
this._changeView();
|
||||
if (this._getConfig().view.update_force) {
|
||||
// If force is enabled, the timer just resets and starts over.
|
||||
this._resetInteractionTimer();
|
||||
}
|
||||
}, this._getConfig().view.timeout_seconds * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the update timer to trigger an update refresh every
|
||||
* `view.update_seconds`.
|
||||
*/
|
||||
protected _startUpdateTimer(): void {
|
||||
if (this._updateTimerID) {
|
||||
window.clearTimeout(this._updateTimerID);
|
||||
this._updateTimerID = null;
|
||||
}
|
||||
if (this._getConfig().view.update_seconds) {
|
||||
this._updateTimerID = window.setTimeout(() => {
|
||||
if (this._isAutomatedViewUpdateAllowed()) {
|
||||
this._changeView();
|
||||
} else {
|
||||
// Not allowed to update this time around, but try again at the next
|
||||
// interval.
|
||||
this._startUpdateTimer();
|
||||
}
|
||||
}, this._getConfig().view.update_seconds * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an automated view update is allowed.
|
||||
* @returns `true` if it's allowed, `false` otherwise.
|
||||
*/
|
||||
protected _isAutomatedViewUpdateAllowed(): boolean {
|
||||
return (
|
||||
this._getConfig().view.update_force || !this._interactionTimerID
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the card menu.
|
||||
* @returns A rendered template.
|
||||
@@ -929,20 +974,6 @@ export class FrigateCard extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for media play event.
|
||||
*/
|
||||
protected _playHandler(): void {
|
||||
this._mediaPlaying = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for media pause event.
|
||||
*/
|
||||
protected _pauseHandler(): void {
|
||||
this._mediaPlaying = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message to display and trigger an update.
|
||||
* @param message The message to display.
|
||||
@@ -1115,8 +1146,6 @@ export class FrigateCard extends LitElement {
|
||||
@frigate-card:message=${this._messageHandler}
|
||||
@frigate-card:change-view=${this._changeViewHandler}
|
||||
@frigate-card:media-show=${this._mediaShowHandler}
|
||||
@frigate-card:pause=${this._pauseHandler}
|
||||
@frigate-card:play=${this._playHandler}
|
||||
>
|
||||
${this._getConfig().menu.mode == 'above' ? this._renderMenu() : ''}
|
||||
<div class="container outer" style="${styleMap(outerStyle)}">
|
||||
|
||||
@@ -114,22 +114,6 @@ export function dispatchFrigateCardEvent<T>(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a Frigate card play event.
|
||||
* @param element The element to send the event.
|
||||
*/
|
||||
export function dispatchPlayEvent(element: HTMLElement): void {
|
||||
dispatchFrigateCardEvent(element, 'play');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a Frigate card pause event.
|
||||
* @param element The element to send the event.
|
||||
*/
|
||||
export function dispatchPauseEvent(element: HTMLElement): void {
|
||||
dispatchFrigateCardEvent(element, 'pause');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MediaShowInfo object.
|
||||
* @param source An event or HTMLElement that should be used as a source.
|
||||
|
||||
+1
-25
@@ -40,8 +40,6 @@ import {
|
||||
dispatchExistingMediaShowInfoAsEvent,
|
||||
dispatchMediaShowEvent,
|
||||
dispatchMessageEvent,
|
||||
dispatchPauseEvent,
|
||||
dispatchPlayEvent,
|
||||
getCameraIcon,
|
||||
getCameraTitle,
|
||||
homeAssistantSignPath,
|
||||
@@ -701,7 +699,7 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
||||
* @returns The player or `null` if not found.
|
||||
*/
|
||||
protected _getPlayer(): HTMLVideoElement | null {
|
||||
return this.renderRoot.querySelector('#video') as HTMLVideoElement | null;
|
||||
return this.renderRoot?.querySelector('#video') as HTMLVideoElement | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -763,8 +761,6 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
||||
const video = this._getPlayer();
|
||||
if (video) {
|
||||
const onloadedmetadata = video.onloadedmetadata;
|
||||
const onplay = video.onplay;
|
||||
const onpause = video.onpause;
|
||||
|
||||
video.onloadedmetadata = (e) => {
|
||||
if (onloadedmetadata) {
|
||||
@@ -772,18 +768,6 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
||||
}
|
||||
dispatchMediaShowEvent(this, video);
|
||||
};
|
||||
video.onplay = (e) => {
|
||||
if (onplay) {
|
||||
onplay.call(video, e);
|
||||
}
|
||||
dispatchPlayEvent(this);
|
||||
};
|
||||
video.onpause = (e) => {
|
||||
if (onpause) {
|
||||
onpause.call(video, e);
|
||||
}
|
||||
dispatchPauseEvent(this);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -864,14 +848,6 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
||||
url,
|
||||
{
|
||||
canvas: this._jsmpegCanvasElement,
|
||||
hooks: {
|
||||
play: () => {
|
||||
dispatchPlayEvent(this);
|
||||
},
|
||||
pause: () => {
|
||||
dispatchPauseEvent(this);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
pauseWhenHidden: false,
|
||||
|
||||
@@ -12,12 +12,7 @@
|
||||
import { TemplateResult, css, html } from 'lit';
|
||||
import { Ref, createRef, ref } from 'lit/directives/ref';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
|
||||
import {
|
||||
dispatchMediaShowEvent,
|
||||
dispatchPauseEvent,
|
||||
dispatchPlayEvent,
|
||||
} from '../common.js';
|
||||
import { dispatchMediaShowEvent } from '../common.js';
|
||||
|
||||
customElements.whenDefined('ha-hls-player').then(() => {
|
||||
@customElement('frigate-card-ha-hls-player')
|
||||
@@ -54,8 +49,6 @@ customElements.whenDefined('ha-hls-player').then(() => {
|
||||
@loadeddata=${(e) => {
|
||||
dispatchMediaShowEvent(this, e);
|
||||
}}
|
||||
@pause=${() => dispatchPauseEvent(this)}
|
||||
@play=${() => dispatchPlayEvent(this)}
|
||||
></video>
|
||||
`;
|
||||
}
|
||||
|
||||
+4
-2
@@ -370,7 +370,8 @@ export type PictureElements = z.infer<typeof pictureElementsSchema>;
|
||||
*/
|
||||
const viewConfigDefault = {
|
||||
default: 'live' as const,
|
||||
timeout: 180,
|
||||
timeout_seconds: 300,
|
||||
update_seconds: 0,
|
||||
update_force: false,
|
||||
update_cycle_camera: false,
|
||||
};
|
||||
@@ -380,7 +381,8 @@ const viewConfigSchema = z
|
||||
.enum(FRIGATE_CARD_VIEWS_USER_SPECIFIED)
|
||||
.optional()
|
||||
.default(viewConfigDefault.default),
|
||||
timeout_seconds: z.number().default(viewConfigDefault.timeout),
|
||||
timeout_seconds: z.number().default(viewConfigDefault.timeout_seconds),
|
||||
update_seconds: z.number().default(viewConfigDefault.update_seconds),
|
||||
update_force: z.boolean().default(viewConfigDefault.update_force),
|
||||
update_cycle_camera: z.boolean().default(viewConfigDefault.update_cycle_camera),
|
||||
update_entities: z.string().array().optional(),
|
||||
|
||||
Reference in New Issue
Block a user