Add option to choose view for newly selected cameras.
This commit is contained in:
@@ -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
@@ -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,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -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
@@ -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,
|
||||
)}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user