Add option to choose view for newly selected cameras.

This commit is contained in:
Dermot Duffy
2022-01-29 17:17:56 -08:00
parent 65249fc82f
commit df93e572a2
6 changed files with 105 additions and 95 deletions
+2 -1
View File
@@ -138,6 +138,7 @@ view:
| Option | Default | Overridable | Description |
| - | - | - | - |
| `default` | `live` | :heavy_multiplication_x: | The view to show in the card by default. See [views](#views) below.|
| `camera_select` | `current` | :heavy_multiplication_x: | The view to show when a new camera is selected (e.g. in the camera menu). If `current` the view is unchanged when a new camera is selected. Other acceptable values may be seen at [views](#views) below.|
| `timeout_seconds` | `300` | :heavy_multiplication_x: | A numbers of seconds of inactivity after user 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 user interaction. See [card updates](#card-updates) below for behavior and usecases.|
@@ -564,7 +565,7 @@ Parameters for the `custom:frigate-card-conditional` element:
|`download`|Download the displayed media.|
|`frigate_ui`|Open the Frigate UI at the configured URL.|
|`fullscreen`|Toggle fullscreen.|
|`camera_select`|Select a given camera. Takes a single additional `camera` parameter with the [camera ID](#camera-ids) of the camera to select.|
|`camera_select`|Select a given camera. Takes a single additional `camera` parameter with the [camera ID](#camera-ids) of the camera to select. Respects the value of `view.camera_select` to choose the appropriate view on the new camera.|
|`menu_toggle` | Show/hide the menu (for `hidden-*` mode menus). |
<a name="views"></a>
+5 -6
View File
@@ -10,11 +10,7 @@ import { customElement, property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { StyleInfo, styleMap } from 'lit/directives/style-map.js';
import { until } from 'lit/directives/until.js';
import {
HomeAssistant,
LovelaceCardEditor,
getLovelace,
} from 'custom-card-helpers';
import { HomeAssistant, LovelaceCardEditor, getLovelace } from 'custom-card-helpers';
import screenfull from 'screenfull';
import { z } from 'zod';
@@ -22,6 +18,7 @@ import {
Actions,
ActionType,
CameraConfig,
FrigateCardView,
RawFrigateCardConfig,
entitySchema,
frigateCardConfigSchema,
@@ -826,7 +823,9 @@ export class FrigateCard extends LitElement {
if (this._cameras?.has(camera) && this._view) {
this._changeView({
view: new View({
view: this._view.view,
view: this._getConfig().view.camera_select === 'current'
? this._view.view
: this._getConfig().view.camera_select as FrigateCardView,
camera: camera,
}),
});
+1
View File
@@ -19,6 +19,7 @@ export const CONF_CAMERAS_ARRAY_WEBRTC_URL = `${CONF_CAMERAS}.#.webrtc.url` as c
export const CONF_CAMERAS_ARRAY_LIVE_PROVIDER = `${CONF_CAMERAS}.#.live_provider` as const;
export const CONF_VIEW = 'view' as const;
export const CONF_VIEW_CAMERA_SELECT = `${CONF_VIEW}.camera_select` as const;
export const CONF_VIEW_DEFAULT = `${CONF_VIEW}.default` as const;
export const CONF_VIEW_TIMEOUT_SECONDS = `${CONF_VIEW}.timeout_seconds` as const;
export const CONF_VIEW_UPDATE_CYCLE_CAMERA = `${CONF_VIEW}.update_cycle_camera` as const;
+88 -84
View File
@@ -56,6 +56,7 @@ import {
CONF_MENU_BUTTONS_SNAPSHOTS,
CONF_MENU_BUTTON_SIZE,
CONF_MENU_MODE,
CONF_VIEW_CAMERA_SELECT,
CONF_VIEW_DEFAULT,
CONF_VIEW_TIMEOUT_SECONDS,
CONF_VIEW_UPDATE_CYCLE_CAMERA,
@@ -238,12 +239,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
*/
protected _renderDropdown(
configPath: string,
dropdown: string[] | Record<string, string>,
dropdown: string[] | Map<string, string>,
): TemplateResult | void {
if (!this._config) {
return;
}
const keys = Array.isArray(dropdown) ? dropdown : Object.keys(dropdown);
const keys = Array.isArray(dropdown) ? dropdown : [...dropdown.keys()];
return html`
<paper-dropdown-menu
@@ -257,7 +258,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
>
${keys.map(
(key) => html` <paper-item .label="${key}">
${Array.isArray(dropdown) ? key : dropdown[key]}
${Array.isArray(dropdown) ? key : dropdown.get(key)}
</paper-item>`,
)}
</paper-listbox>
@@ -357,13 +358,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
cameraEntities: string[],
addNewCamera?: boolean,
): TemplateResult | void {
const liveProviders = {
'': '',
auto: localize('config.cameras.live_providers.auto'),
frigate: localize('config.cameras.live_providers.frigate'),
'frigate-jsmpeg': localize('config.cameras.live_providers.frigate-jsmpeg'),
webrtc: localize('config.cameras.live_providers.webrtc'),
} as const;
const liveProviders = new Map([
['', ''],
['auto', localize('config.cameras.live_providers.auto')],
['frigate', localize('config.cameras.live_providers.frigate')],
['frigate-jsmpeg', localize('config.cameras.live_providers.frigate-jsmpeg')],
['webrtc', localize('config.cameras.live_providers.webrtc')],
]);
// Make a new config and update the editor with changes on it,
const modifyConfig = (func: (config: RawFrigateCardConfig) => boolean): void => {
@@ -450,7 +451,8 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
)}
${this._renderDropdown(
getArrayConfigPath(CONF_CAMERAS_ARRAY_LIVE_PROVIDER, cameraIndex),
liveProviders)}
liveProviders,
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_URL, cameraIndex),
)}
@@ -497,7 +499,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
if (!this._config) {
return;
}
const allowedPattern = type == 'number' ? '[0-9]' : undefined
const allowedPattern = type == 'number' ? '[0-9]' : undefined;
return html` <paper-input
label=${this._getLabel(configPath)}
.value=${getConfigValue(this._config, configPath, '')}
@@ -551,70 +553,77 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
const cameraEntities = this._getEntities('camera');
const viewModes = {
'': '',
live: localize('config.view.views.live'),
clips: localize('config.view.views.clips'),
snapshots: localize('config.view.views.snapshots'),
clip: localize('config.view.views.clip'),
snapshot: localize('config.view.views.snapshot'),
image: localize('config.view.views.image'),
};
const viewModes = new Map([
['', ''],
['live', localize('config.view.views.live')],
['clips', localize('config.view.views.clips')],
['snapshots', localize('config.view.views.snapshots')],
['clip', localize('config.view.views.clip')],
['snapshot', localize('config.view.views.snapshot')],
['image', localize('config.view.views.image')],
]);
const menuModes = {
'': '',
none: localize('config.menu.modes.none'),
'hidden-top': localize('config.menu.modes.hidden-top'),
'hidden-left': localize('config.menu.modes.hidden-left'),
'hidden-bottom': localize('config.menu.modes.hidden-bottom'),
'hidden-right': localize('config.menu.modes.hidden-right'),
'overlay-top': localize('config.menu.modes.overlay-top'),
'overlay-left': localize('config.menu.modes.overlay-left'),
'overlay-bottom': localize('config.menu.modes.overlay-bottom'),
'overlay-right': localize('config.menu.modes.overlay-right'),
'hover-top': localize('config.menu.modes.hover-top'),
'hover-left': localize('config.menu.modes.hover-left'),
'hover-bottom': localize('config.menu.modes.hover-bottom'),
'hover-right': localize('config.menu.modes.hover-right'),
above: localize('config.menu.modes.above'),
below: localize('config.menu.modes.below'),
};
const cameraSelectViewModes = new Map(viewModes);
cameraSelectViewModes.set('current', localize('config.view.views.current'));
const eventViewerNextPreviousControlStyles = {
'': '',
thumbnails: localize(
'config.event_viewer.controls.next_previous.styles.thumbnails',
),
chevrons: localize('config.event_viewer.controls.next_previous.styles.chevrons'),
none: localize('config.event_viewer.controls.next_previous.styles.none'),
};
const menuModes = new Map([
['', ''],
['none', localize('config.menu.modes.none')],
['hidden-top', localize('config.menu.modes.hidden-top')],
['hidden-left', localize('config.menu.modes.hidden-left')],
['hidden-bottom', localize('config.menu.modes.hidden-bottom')],
['hidden-right', localize('config.menu.modes.hidden-right')],
['overlay-top', localize('config.menu.modes.overlay-top')],
['overlay-left', localize('config.menu.modes.overlay-left')],
['overlay-bottom', localize('config.menu.modes.overlay-bottom')],
['overlay-right', localize('config.menu.modes.overlay-right')],
['hover-top', localize('config.menu.modes.hover-top')],
['hover-left', localize('config.menu.modes.hover-left')],
['hover-bottom', localize('config.menu.modes.hover-bottom')],
['hover-right', localize('config.menu.modes.hover-right')],
['above', localize('config.menu.modes.above')],
['below', localize('config.menu.modes.below')],
]);
const liveNextPreviousControlStyles = {
'': '',
chevrons: localize('config.live.controls.next_previous.styles.chevrons'),
icons: localize('config.live.controls.next_previous.styles.icons'),
none: localize('config.live.controls.next_previous.styles.none'),
};
const eventViewerNextPreviousControlStyles = new Map([
['', ''],
[
'thumbnails',
localize('config.event_viewer.controls.next_previous.styles.thumbnails'),
],
[
'chevrons',
localize('config.event_viewer.controls.next_previous.styles.chevrons'),
],
['none', localize('config.event_viewer.controls.next_previous.styles.none')],
]);
const aspectRatioModes = {
'': '',
dynamic: localize('config.dimensions.aspect_ratio_modes.dynamic'),
static: localize('config.dimensions.aspect_ratio_modes.static'),
unconstrained: localize('config.dimensions.aspect_ratio_modes.unconstrained'),
};
const liveNextPreviousControlStyles = new Map([
['', ''],
['chevrons', localize('config.live.controls.next_previous.styles.chevrons')],
['icons', localize('config.live.controls.next_previous.styles.icons')],
['none', localize('config.live.controls.next_previous.styles.none')],
]);
const thumbnailModes = {
'': '',
none: localize('config.event_viewer.controls.thumbnails.modes.none'),
above: localize('config.event_viewer.controls.thumbnails.modes.above'),
below: localize('config.event_viewer.controls.thumbnails.modes.below'),
};
const aspectRatioModes = new Map([
['', ''],
['dynamic', localize('config.dimensions.aspect_ratio_modes.dynamic')],
['static', localize('config.dimensions.aspect_ratio_modes.static')],
['unconstrained', localize('config.dimensions.aspect_ratio_modes.unconstrained')],
]);
const thumbnailMedias = {
'': '',
clips: localize('config.live.controls.thumbnails.medias.clips'),
snapshots: localize('config.live.controls.thumbnails.medias.snapshots'),
};
const thumbnailModes = new Map([
['', ''],
['none', localize('config.event_viewer.controls.thumbnails.modes.none')],
['above', localize('config.event_viewer.controls.thumbnails.modes.above')],
['below', localize('config.event_viewer.controls.thumbnails.modes.below')],
]);
const thumbnailMedias = new Map([
['', ''],
['clips', localize('config.live.controls.thumbnails.medias.clips')],
['snapshots', localize('config.live.controls.thumbnails.medias.snapshots')],
]);
const defaults = frigateCardConfigDefaults;
@@ -660,10 +669,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
? html`
<div class="values">
${this._renderDropdown(CONF_VIEW_DEFAULT, viewModes)}
${this._renderDropdown(CONF_VIEW_CAMERA_SELECT, cameraSelectViewModes)}
${this._renderStringInput(CONF_VIEW_TIMEOUT_SECONDS, 'number')}
${this._renderStringInput(CONF_VIEW_UPDATE_SECONDS, 'number')}
${this._renderSwitch(CONF_VIEW_UPDATE_FORCE, defaults.view.update_force)}
${this._renderSwitch(CONF_VIEW_UPDATE_CYCLE_CAMERA, defaults.view.update_cycle_camera)}
${this._renderSwitch(
CONF_VIEW_UPDATE_CYCLE_CAMERA,
defaults.view.update_cycle_camera,
)}
</div>
`
: ''}
@@ -721,18 +734,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
? html`
<div class="values">
${this._renderSwitch(CONF_LIVE_PRELOAD, defaults.live.preload)}
${this._renderSwitch(
CONF_LIVE_DRAGGABLE,
defaults.live.draggable,
)}
${this._renderSwitch(
CONF_LIVE_LAZY_LOAD,
defaults.live.lazy_load,
)}
${this._renderSwitch(
CONF_LIVE_LAZY_UNLOAD,
defaults.live.lazy_unload,
)}
${this._renderSwitch(CONF_LIVE_DRAGGABLE, defaults.live.draggable)}
${this._renderSwitch(CONF_LIVE_LAZY_LOAD, defaults.live.lazy_load)}
${this._renderSwitch(CONF_LIVE_LAZY_UNLOAD, defaults.live.lazy_unload)}
${this._renderDropdown(
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_STYLE,
liveNextPreviousControlStyles,
@@ -756,7 +760,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
${this._renderSlider(
CONF_EVENT_GALLERY_MIN_COLUMNS,
defaults.event_gallery.min_columns,
"mdi:view-column",
'mdi:view-column',
1,
10,
)}
+3 -1
View File
@@ -32,6 +32,7 @@
"zone": "Frigate zone"
},
"view": {
"camera_select": "View for newly selected cameras",
"default": "Default view",
"views": {
"live": "Live view",
@@ -39,7 +40,8 @@
"clips": "Clips gallery",
"snapshot": "Most recent snapshot",
"snapshots": "Snapshots gallery",
"image": "Static image"
"image": "Static image",
"current": "Current view"
},
"timeout_seconds": "Reset to default view X seconds after user action (0=never)",
"update_cycle_camera": "Cycle through cameras when default view updates",
+6 -3
View File
@@ -377,7 +377,8 @@ export type PictureElements = z.infer<typeof pictureElementsSchema>;
*/
const viewConfigDefault = {
default: 'live' as const,
timeout_seconds: 300,
camera_select: 'current' as const,
timeout_seconds: 300,
update_seconds: 0,
update_force: false,
update_cycle_camera: false,
@@ -386,8 +387,10 @@ const viewConfigSchema = z
.object({
default: z
.enum(FRIGATE_CARD_VIEWS_USER_SPECIFIED)
.optional()
.default(viewConfigDefault.default),
camera_select: z
.enum([...FRIGATE_CARD_VIEWS_USER_SPECIFIED, 'current'])
.default(viewConfigDefault.camera_select),
timeout_seconds: z.number().default(viewConfigDefault.timeout_seconds),
update_seconds: z.number().default(viewConfigDefault.update_seconds),
update_force: z.boolean().default(viewConfigDefault.update_force),
@@ -407,7 +410,7 @@ const imageConfigDefault = {
const imageConfigSchema = z
.object({
src: z.string().optional(),
refresh_seconds: z.number().min(0).default(imageConfigDefault.refresh_seconds)
refresh_seconds: z.number().min(0).default(imageConfigDefault.refresh_seconds),
})
.merge(actionsSchema)
.default(imageConfigDefault);