Add untrigger timer.
This commit is contained in:
@@ -191,14 +191,20 @@ view:
|
|||||||
scan:
|
scan:
|
||||||
```
|
```
|
||||||
|
|
||||||
Scan mode allows the card to automatically "follow the action". In this mode the card will automatically select a camera in the `live` view when it is triggered (as defined by your camera configuration, see `trigger_by_motion`, `trigger_by_occupancy` and `trigger_by_entities` parameters). When the camera untriggers, the camera selection will return to the next most recently triggered camera (as long as it is still triggered) -- if there are no triggered cameras remaining, the camera will return to the default. Triggering is only allowed when there is no ongoing human interaction with the card -- interaction will automatically untrigger it and further triggering will not occur until after the card has been unattended for `view.timeout_seconds`.
|
Scan mode allows the card to automatically "follow the action". In this mode the card will automatically select a camera in the `live` view when an entity changes to an active state (specifically `on` or `open`). The entities considered are defined by your camera configuration (see `trigger_by_motion`, `trigger_by_occupancy` and `trigger_by_entities` parameters). An untrigger is defined as the state for all the configured entities returning to inactive (i.e. not `on` or `open`), with an optional number of seconds to wait prior to the untriggering (see `untrigger_seconds`).
|
||||||
|
|
||||||
Scan mode tracks Home Assistant state changes -- when the card is first started, it takes a positive change in state to trigger (i.e. an already occupied room will not trigger it, but a newly occupied room would trigger it).
|
When the camera untriggers, the view will either remain as-is (if `untrigger_reset` is `false`) and the card return to normal operation, or reset to the default view (if `untrigger_reset` is `true` -- the default).
|
||||||
|
|
||||||
|
Triggering is only allowed when there is no ongoing human interaction with the card -- interaction will automatically untrigger and further triggering will not occur until after the card has been unattended for `view.timeout_seconds`.
|
||||||
|
|
||||||
|
Scan mode tracks Home Assistant state *changes* -- when the card is first started, it takes an active change in state to trigger (i.e. an already occupied room will not trigger it, but a newly occupied room will).
|
||||||
|
|
||||||
| Option | Default | Overridable | Description |
|
| Option | Default | Overridable | Description |
|
||||||
| - | - | - | - |
|
| - | - | - | - |
|
||||||
| `enabled` | `false` | :white_check_mark: | Whether to enable scan mode. |
|
| `enabled` | `false` | :white_check_mark: | Whether to enable scan mode. |
|
||||||
| `show_trigger_status` | `true` | :white_check_mark: | Whether or not the card should show a visual indication that it is triggered (a pulsing border around the card edge). |
|
| `show_trigger_status` | `true` | :white_check_mark: | Whether or not the card should show a visual indication that it is triggered (a pulsing border around the card edge). |
|
||||||
|
| `untrigger_reset` | `true` | :white_check_mark: | Whether or not to reset the view to the default after untriggering. |
|
||||||
|
| `untrigger_seconds` | `0` | :white_check_mark: | The number of seconds to wait after all entities are inactive before untriggering. |
|
||||||
|
|
||||||
### Menu Options
|
### Menu Options
|
||||||
|
|
||||||
@@ -1055,6 +1061,8 @@ view:
|
|||||||
scan:
|
scan:
|
||||||
enabled: false
|
enabled: false
|
||||||
show_trigger_status: true
|
show_trigger_status: true
|
||||||
|
untrigger_reset: true
|
||||||
|
untrigger_seconds: 0
|
||||||
actions:
|
actions:
|
||||||
entity: light.office_main_lights
|
entity: light.office_main_lights
|
||||||
tap_action:
|
tap_action:
|
||||||
|
|||||||
+41
-12
@@ -1,12 +1,5 @@
|
|||||||
import { getLovelace, HomeAssistant, LovelaceCardEditor } from 'custom-card-helpers';
|
import { getLovelace, HomeAssistant, LovelaceCardEditor } from 'custom-card-helpers';
|
||||||
import {
|
import { CSSResultGroup, html, LitElement, PropertyValues, TemplateResult, unsafeCSS } from 'lit';
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
unsafeCSS
|
|
||||||
} from 'lit';
|
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
||||||
@@ -197,6 +190,7 @@ export class FrigateCard extends LitElement {
|
|||||||
protected _initialized = false;
|
protected _initialized = false;
|
||||||
|
|
||||||
protected _triggers: Map<string, Date> = new Map();
|
protected _triggers: Map<string, Date> = new Map();
|
||||||
|
protected _untriggerTimerID: number | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the Home Assistant object.
|
* Set the Home Assistant object.
|
||||||
@@ -1003,8 +997,7 @@ export class FrigateCard extends LitElement {
|
|||||||
|
|
||||||
if (triggerChanges) {
|
if (triggerChanges) {
|
||||||
if (!this._triggers.size) {
|
if (!this._triggers.size) {
|
||||||
this._changeView();
|
this._startUntriggerTimer();
|
||||||
changedCamera = true;
|
|
||||||
} else {
|
} else {
|
||||||
const targetCamera = this._getMostRecentTrigger();
|
const targetCamera = this._getMostRecentTrigger();
|
||||||
if (
|
if (
|
||||||
@@ -1019,11 +1012,47 @@ export class FrigateCard extends LitElement {
|
|||||||
return changedCamera;
|
return changedCamera;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the scan mode is currently triggered.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected _isTriggered(): boolean {
|
||||||
|
return !!this._triggers.size || !!this._untriggerTimerID;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Untrigger the card.
|
* Untrigger the card.
|
||||||
*/
|
*/
|
||||||
protected _untrigger(): void {
|
protected _untrigger(): void {
|
||||||
this._triggers.clear();
|
this._triggers.clear();
|
||||||
|
this._clearUntriggerTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the untrigger timer.
|
||||||
|
*/
|
||||||
|
protected _startUntriggerTimer(): void {
|
||||||
|
this._clearUntriggerTimer();
|
||||||
|
|
||||||
|
this._untriggerTimerID = window.setTimeout(() => {
|
||||||
|
this._untrigger();
|
||||||
|
if (
|
||||||
|
this._isAutomatedViewUpdateAllowed() &&
|
||||||
|
this._getConfig().view.scan.untrigger_reset
|
||||||
|
) {
|
||||||
|
this._changeView();
|
||||||
|
}
|
||||||
|
}, this._getConfig().view.scan.untrigger_seconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the user interaction ('screensaver') timer.
|
||||||
|
*/
|
||||||
|
protected _clearUntriggerTimer(): void {
|
||||||
|
if (this._untriggerTimerID) {
|
||||||
|
window.clearTimeout(this._untriggerTimerID);
|
||||||
|
this._untriggerTimerID = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1385,7 +1414,7 @@ export class FrigateCard extends LitElement {
|
|||||||
*/
|
*/
|
||||||
protected _isAutomatedViewUpdateAllowed(ignoreTriggers?: boolean): boolean {
|
protected _isAutomatedViewUpdateAllowed(ignoreTriggers?: boolean): boolean {
|
||||||
return (
|
return (
|
||||||
(ignoreTriggers || !this._triggers.size) &&
|
(ignoreTriggers || !this._isTriggered()) &&
|
||||||
(this._getConfig().view.update_force || !this._interactionTimerID)
|
(this._getConfig().view.update_force || !this._interactionTimerID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1568,7 +1597,7 @@ export class FrigateCard extends LitElement {
|
|||||||
const mainClasses = {
|
const mainClasses = {
|
||||||
main: true,
|
main: true,
|
||||||
triggered:
|
triggered:
|
||||||
!!this._triggers.size && this._getConfig().view.scan.show_trigger_status,
|
!!this._isTriggered() && this._getConfig().view.scan.show_trigger_status,
|
||||||
};
|
};
|
||||||
|
|
||||||
const actions = this._getMergedActions();
|
const actions = this._getMergedActions();
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ export const CONF_VIEW_SCAN = `${CONF_VIEW}.scan` as const;
|
|||||||
export const CONF_VIEW_SCAN_ENABLED = `${CONF_VIEW_SCAN}.enabled` as const;
|
export const CONF_VIEW_SCAN_ENABLED = `${CONF_VIEW_SCAN}.enabled` as const;
|
||||||
export const CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS =
|
export const CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS =
|
||||||
`${CONF_VIEW_SCAN}.show_trigger_status` as const;
|
`${CONF_VIEW_SCAN}.show_trigger_status` as const;
|
||||||
|
export const CONF_VIEW_SCAN_UNTRIGGER_RESET =
|
||||||
|
`${CONF_VIEW_SCAN}.untrigger_reset` as const;
|
||||||
|
export const CONF_VIEW_SCAN_UNTRIGGER_SECONDS =
|
||||||
|
`${CONF_VIEW_SCAN}.untrigger_seconds` as const;
|
||||||
|
|
||||||
export const CONF_EVENT_GALLERY = 'event_gallery' as const;
|
export const CONF_EVENT_GALLERY = 'event_gallery' as const;
|
||||||
export const CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
export const CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS =
|
||||||
|
|||||||
+17
-5
@@ -32,7 +32,8 @@ import {
|
|||||||
CONF_DIMENSIONS_ASPECT_RATIO_MODE,
|
CONF_DIMENSIONS_ASPECT_RATIO_MODE,
|
||||||
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_CONTROLS,
|
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_CONTROLS,
|
||||||
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
||||||
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SIZE, CONF_IMAGE_MODE,
|
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SIZE,
|
||||||
|
CONF_IMAGE_MODE,
|
||||||
CONF_IMAGE_REFRESH_SECONDS,
|
CONF_IMAGE_REFRESH_SECONDS,
|
||||||
CONF_IMAGE_URL,
|
CONF_IMAGE_URL,
|
||||||
CONF_LIVE_AUTO_MUTE,
|
CONF_LIVE_AUTO_MUTE,
|
||||||
@@ -52,7 +53,8 @@ import {
|
|||||||
CONF_LIVE_LAZY_LOAD,
|
CONF_LIVE_LAZY_LOAD,
|
||||||
CONF_LIVE_LAZY_UNLOAD,
|
CONF_LIVE_LAZY_UNLOAD,
|
||||||
CONF_LIVE_PRELOAD,
|
CONF_LIVE_PRELOAD,
|
||||||
CONF_LIVE_TRANSITION_EFFECT, CONF_MEDIA_VIEWER_AUTO_MUTE,
|
CONF_LIVE_TRANSITION_EFFECT,
|
||||||
|
CONF_MEDIA_VIEWER_AUTO_MUTE,
|
||||||
CONF_MEDIA_VIEWER_AUTO_PAUSE,
|
CONF_MEDIA_VIEWER_AUTO_PAUSE,
|
||||||
CONF_MEDIA_VIEWER_AUTO_PLAY,
|
CONF_MEDIA_VIEWER_AUTO_PLAY,
|
||||||
CONF_MEDIA_VIEWER_AUTO_UNMUTE,
|
CONF_MEDIA_VIEWER_AUTO_UNMUTE,
|
||||||
@@ -66,7 +68,8 @@ import {
|
|||||||
CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE,
|
CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE,
|
||||||
CONF_MEDIA_VIEWER_DRAGGABLE,
|
CONF_MEDIA_VIEWER_DRAGGABLE,
|
||||||
CONF_MEDIA_VIEWER_LAZY_LOAD,
|
CONF_MEDIA_VIEWER_LAZY_LOAD,
|
||||||
CONF_MEDIA_VIEWER_TRANSITION_EFFECT, CONF_MENU_ALIGNMENT,
|
CONF_MEDIA_VIEWER_TRANSITION_EFFECT,
|
||||||
|
CONF_MENU_ALIGNMENT,
|
||||||
CONF_MENU_BUTTONS,
|
CONF_MENU_BUTTONS,
|
||||||
CONF_MENU_BUTTON_SIZE,
|
CONF_MENU_BUTTON_SIZE,
|
||||||
CONF_MENU_POSITION,
|
CONF_MENU_POSITION,
|
||||||
@@ -85,6 +88,8 @@ import {
|
|||||||
CONF_VIEW_SCAN,
|
CONF_VIEW_SCAN,
|
||||||
CONF_VIEW_SCAN_ENABLED,
|
CONF_VIEW_SCAN_ENABLED,
|
||||||
CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS,
|
CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS,
|
||||||
|
CONF_VIEW_SCAN_UNTRIGGER_RESET,
|
||||||
|
CONF_VIEW_SCAN_UNTRIGGER_SECONDS,
|
||||||
CONF_VIEW_TIMEOUT_SECONDS,
|
CONF_VIEW_TIMEOUT_SECONDS,
|
||||||
CONF_VIEW_UPDATE_CYCLE_CAMERA,
|
CONF_VIEW_UPDATE_CYCLE_CAMERA,
|
||||||
CONF_VIEW_UPDATE_FORCE,
|
CONF_VIEW_UPDATE_FORCE,
|
||||||
@@ -607,18 +612,25 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
? html` <div class="values">
|
? html` <div class="values">
|
||||||
${this._renderSwitch(
|
${this._renderSwitch(
|
||||||
CONF_VIEW_SCAN_ENABLED,
|
CONF_VIEW_SCAN_ENABLED,
|
||||||
frigateCardConfigDefaults.view.scan.enabled ?? true,
|
frigateCardConfigDefaults.view.scan.enabled,
|
||||||
{
|
{
|
||||||
label: localize(`config.${CONF_VIEW_SCAN_ENABLED}`),
|
label: localize(`config.${CONF_VIEW_SCAN_ENABLED}`),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderSwitch(
|
||||||
CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS,
|
CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS,
|
||||||
frigateCardConfigDefaults.view.scan.show_trigger_status ?? true,
|
frigateCardConfigDefaults.view.scan.show_trigger_status,
|
||||||
{
|
{
|
||||||
label: localize(`config.${CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS}`),
|
label: localize(`config.${CONF_VIEW_SCAN_SHOW_TRIGGER_STATUS}`),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
|
${this._renderSwitch(
|
||||||
|
CONF_VIEW_SCAN_UNTRIGGER_RESET,
|
||||||
|
frigateCardConfigDefaults.view.scan.untrigger_reset,
|
||||||
|
)}
|
||||||
|
${this._renderNumberInput(CONF_VIEW_SCAN_UNTRIGGER_SECONDS, {
|
||||||
|
default: frigateCardConfigDefaults.view.scan.untrigger_seconds,
|
||||||
|
})}
|
||||||
</div>`
|
</div>`
|
||||||
: ''}
|
: ''}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -73,7 +73,9 @@
|
|||||||
"scan": {
|
"scan": {
|
||||||
"scan_mode": "Scan mode",
|
"scan_mode": "Scan mode",
|
||||||
"enabled": "Scan mode enabled",
|
"enabled": "Scan mode enabled",
|
||||||
"show_trigger_status": "Show pulsing border when triggered"
|
"show_trigger_status": "Show pulsing border when triggered",
|
||||||
|
"untrigger_reset": "Reset the view to default after untrigger",
|
||||||
|
"untrigger_seconds": "Seconds after inactive state change to untrigger"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event_gallery": {
|
"event_gallery": {
|
||||||
|
|||||||
@@ -518,6 +518,8 @@ const viewConfigDefault = {
|
|||||||
scan: {
|
scan: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
show_trigger_status: true,
|
show_trigger_status: true,
|
||||||
|
untrigger_seconds: 0,
|
||||||
|
untrigger_reset: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const viewConfigSchema = z
|
const viewConfigSchema = z
|
||||||
@@ -541,6 +543,8 @@ const viewConfigSchema = z
|
|||||||
show_trigger_status: z
|
show_trigger_status: z
|
||||||
.boolean()
|
.boolean()
|
||||||
.default(viewConfigDefault.scan.show_trigger_status),
|
.default(viewConfigDefault.scan.show_trigger_status),
|
||||||
|
untrigger_seconds: z.number().default(viewConfigDefault.scan.untrigger_seconds),
|
||||||
|
untrigger_reset: z.boolean().default(viewConfigDefault.scan.untrigger_reset),
|
||||||
})
|
})
|
||||||
.default(viewConfigDefault.scan),
|
.default(viewConfigDefault.scan),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user