Add support for disabling zoom.
This commit is contained in:
@@ -463,6 +463,7 @@ See the [fully expanded live configuration example](#config-expanded-live) for h
|
|||||||
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load cameras in the camera carousel. Setting this will `false` will cause all cameras to load simultaneously when the `live` carousel is opened (or cause all cameras to load continually if both `lazy_load` and `preload` are `true`). This will result in a smoother carousel experience at a cost of (potentially) a substantial amount of continually streamed data. |
|
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load cameras in the camera carousel. Setting this will `false` will cause all cameras to load simultaneously when the `live` carousel is opened (or cause all cameras to load continually if both `lazy_load` and `preload` are `true`). This will result in a smoother carousel experience at a cost of (potentially) a substantial amount of continually streamed data. |
|
||||||
| `lazy_unload` | `never` | :heavy_multiplication_x: | When to lazily **un**load lazyily-loaded cameras. `never` will never lazily-unload, `unselected` will lazy-unload a camera when it is unselected in the carousel, `hidden` will lazy-unload all cameras when the browser/tab becomes hidden or `all` on any opportunity to lazily unload (i.e. either case). This will cause a reloading delay on revisiting that camera in the carousel but will save the streaming network resources that are otherwise consumed. This option has no effect if `lazy_load` is false. Some live providers (e.g. `webrtc-card`) implement their own lazy unloading independently which may occur regardless of the value of this setting.|
|
| `lazy_unload` | `never` | :heavy_multiplication_x: | When to lazily **un**load lazyily-loaded cameras. `never` will never lazily-unload, `unselected` will lazy-unload a camera when it is unselected in the carousel, `hidden` will lazy-unload all cameras when the browser/tab becomes hidden or `all` on any opportunity to lazily unload (i.e. either case). This will cause a reloading delay on revisiting that camera in the carousel but will save the streaming network resources that are otherwise consumed. This option has no effect if `lazy_load` is false. Some live providers (e.g. `webrtc-card`) implement their own lazy unloading independently which may occur regardless of the value of this setting.|
|
||||||
| `draggable` | `true` | :heavy_multiplication_x: | Whether or not the live carousel can be dragged left or right, via touch/swipe and mouse dragging. |
|
| `draggable` | `true` | :heavy_multiplication_x: | Whether or not the live carousel can be dragged left or right, via touch/swipe and mouse dragging. |
|
||||||
|
| `zoomable` | `true` | :white_check_mark: | Whether or not the live carousel can be zoomed and panned, via touch/pinch and mouse scroll wheel with `ctrl` held. |
|
||||||
| `transition_effect` | `slide` | :heavy_multiplication_x: | Effect to apply as a transition between live cameras. Accepted values: `slide` or `none`. |
|
| `transition_effect` | `slide` | :heavy_multiplication_x: | Effect to apply as a transition between live cameras. Accepted values: `slide` or `none`. |
|
||||||
| `show_image_during_load` | `true` | :white_check_mark: | If `true`, during the initial stream load, the `image` live provider will be shown instead of the loading video stream. This still image will auto-refresh and is replaced with the live stream once loaded. |
|
| `show_image_during_load` | `true` | :white_check_mark: | If `true`, during the initial stream load, the `image` live provider will be shown instead of the loading video stream. This still image will auto-refresh and is replaced with the live stream once loaded. |
|
||||||
| `actions` | | :white_check_mark: | Actions to use for the `live` view. See [actions](#actions) below.|
|
| `actions` | | :white_check_mark: | Actions to use for the `live` view. See [actions](#actions) below.|
|
||||||
@@ -580,6 +581,7 @@ See the [fully expanded Media viewer configuration example](#config-expanded-med
|
|||||||
| `auto_unmute` | `never` | :heavy_multiplication_x: | Whether to automatically unmute events. `never` will never automatically unmute, `selected` will automatically unmute when an event is selected in the carousel, `visible` will automatically unmute when the browser/tab becomes visible or `all` on any opportunity to automatically unmute (i.e. either case). Note that some browsers will not allow automated unmute until the user has interacted with the page in some way -- if the user has not then the browser may pause the media instead.|
|
| `auto_unmute` | `never` | :heavy_multiplication_x: | Whether to automatically unmute events. `never` will never automatically unmute, `selected` will automatically unmute when an event is selected in the carousel, `visible` will automatically unmute when the browser/tab becomes visible or `all` on any opportunity to automatically unmute (i.e. either case). Note that some browsers will not allow automated unmute until the user has interacted with the page in some way -- if the user has not then the browser may pause the media instead.|
|
||||||
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load media in the Media viewer carousel. Setting this will false will fetch all media immediately which may make the carousel experience smoother at a cost of (potentially) a substantial number of simultaneous media fetches on load. |
|
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load media in the Media viewer carousel. Setting this will false will fetch all media immediately which may make the carousel experience smoother at a cost of (potentially) a substantial number of simultaneous media fetches on load. |
|
||||||
| `draggable` | `true` | :heavy_multiplication_x: | Whether or not the Media viewer carousel can be dragged left or right, via touch/swipe and mouse dragging. |
|
| `draggable` | `true` | :heavy_multiplication_x: | Whether or not the Media viewer carousel can be dragged left or right, via touch/swipe and mouse dragging. |
|
||||||
|
| `zoomable` | `true` | :heavy_multiplication_x: | Whether or not the Media Viewer can be zoomed and panned, via touch/pinch and mouse scroll wheel with `ctrl` held. |
|
||||||
| `snapshot_click_plays_clip` | `true` | :heavy_multiplication_x: | Whether clicking on a snapshot in the media viewer should play a related clip. |
|
| `snapshot_click_plays_clip` | `true` | :heavy_multiplication_x: | Whether clicking on a snapshot in the media viewer should play a related clip. |
|
||||||
| `transition_effect` | `slide` | :heavy_multiplication_x: | Effect to apply as a transition between event media. Accepted values: `slide` or `none`. |
|
| `transition_effect` | `slide` | :heavy_multiplication_x: | Effect to apply as a transition between event media. Accepted values: `slide` or `none`. |
|
||||||
| `controls` | | :heavy_multiplication_x: | Configuration for the Media viewer controls. See below. |
|
| `controls` | | :heavy_multiplication_x: | Configuration for the Media viewer controls. See below. |
|
||||||
@@ -722,6 +724,7 @@ See the [fully expanded image configuration example](#config-expanded-image) for
|
|||||||
| `mode` | `url` | :white_check_mark: | Mode of the the `image` [view](#views). Value must be one of `url` (to fetch an arbitrary image URL), `camera` (to show a still of the currently selected camera using either `camera_entity` or `webrtc_card.entity` in that order of precedence), or `screensaver` (to show an [embedded stock Frigate card logo](https://github.com/dermotduffy/frigate-hass-card/blob/main/src/images/frigate-bird-in-sky.jpg)). In either `url` or `camera` mode, the `screensaver` content is used as a fallback if a URL is not specified or cannot be derived. |
|
| `mode` | `url` | :white_check_mark: | Mode of the the `image` [view](#views). Value must be one of `url` (to fetch an arbitrary image URL), `camera` (to show a still of the currently selected camera using either `camera_entity` or `webrtc_card.entity` in that order of precedence), or `screensaver` (to show an [embedded stock Frigate card logo](https://github.com/dermotduffy/frigate-hass-card/blob/main/src/images/frigate-bird-in-sky.jpg)). In either `url` or `camera` mode, the `screensaver` content is used as a fallback if a URL is not specified or cannot be derived. |
|
||||||
| `url` | | :white_check_mark: | A static image URL to be used when the `mode` is set to `url` or when a temporary image is required (e.g. may appear momentarily prior to load of a camera snapshot in the `camera` mode). Note that a `_t=[timestsamp]` query parameter will be automatically added to all URLs such that the image will not be cached by the browser.|
|
| `url` | | :white_check_mark: | A static image URL to be used when the `mode` is set to `url` or when a temporary image is required (e.g. may appear momentarily prior to load of a camera snapshot in the `camera` mode). Note that a `_t=[timestsamp]` query parameter will be automatically added to all URLs such that the image will not be cached by the browser.|
|
||||||
| `refresh_seconds` | 0 | :white_check_mark: | The image will be refreshed at least every `refresh_seconds` (it may refresh more frequently, e.g. whenever Home Assistant updates its camera security token). `0` implies no refreshing. |
|
| `refresh_seconds` | 0 | :white_check_mark: | The image will be refreshed at least every `refresh_seconds` (it may refresh more frequently, e.g. whenever Home Assistant updates its camera security token). `0` implies no refreshing. |
|
||||||
|
| `zoomable` | `true` | :white_check_mark: | Whether or not the image can be zoomed and panned, via touch/pinch and mouse scroll wheel with `ctrl` held. |
|
||||||
| `actions` | | :white_check_mark: | Actions to use for the `image` view. See [actions](#actions) below.|
|
| `actions` | | :white_check_mark: | Actions to use for the `image` view. See [actions](#actions) below.|
|
||||||
|
|
||||||
**Note**: When `mode` is set to `camera` this is effectively providing the same image as the `image` live provider would show in the live camera carousel.
|
**Note**: When `mode` is set to `camera` this is effectively providing the same image as the `image` live provider would show in the live camera carousel.
|
||||||
@@ -1844,6 +1847,7 @@ live:
|
|||||||
lazy_load: true
|
lazy_load: true
|
||||||
lazy_unload: never
|
lazy_unload: never
|
||||||
draggable: true
|
draggable: true
|
||||||
|
zoomable: true
|
||||||
transition_effect: slide
|
transition_effect: slide
|
||||||
controls:
|
controls:
|
||||||
next_previous:
|
next_previous:
|
||||||
@@ -1905,6 +1909,7 @@ media_viewer:
|
|||||||
auto_unmute: never
|
auto_unmute: never
|
||||||
lazy_load: true
|
lazy_load: true
|
||||||
draggable: true
|
draggable: true
|
||||||
|
zoomable: true
|
||||||
snapshot_click_plays_clip: true
|
snapshot_click_plays_clip: true
|
||||||
transition_effect: slide
|
transition_effect: slide
|
||||||
controls:
|
controls:
|
||||||
@@ -1992,6 +1997,7 @@ Reference: [Image Options](#image-options).
|
|||||||
image:
|
image:
|
||||||
mode: url
|
mode: url
|
||||||
refresh_seconds: 0
|
refresh_seconds: 0
|
||||||
|
zoomable: true
|
||||||
layout:
|
layout:
|
||||||
fit: contain
|
fit: contain
|
||||||
position:
|
position:
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import { View } from '../view/view.js';
|
|||||||
import { dispatchErrorMessageEvent } from './message.js';
|
import { dispatchErrorMessageEvent } from './message.js';
|
||||||
import { contentsChanged } from '../utils/basic.js';
|
import { contentsChanged } from '../utils/basic.js';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
import './zoomer.js';
|
|
||||||
|
|
||||||
// See TOKEN_CHANGE_INTERVAL in https://github.com/home-assistant/core/blob/dev/homeassistant/components/camera/__init__.py .
|
// See TOKEN_CHANGE_INTERVAL in https://github.com/home-assistant/core/blob/dev/homeassistant/components/camera/__init__.py .
|
||||||
const HASS_REJECTION_CUTOFF_MS = 5 * 60 * 1000;
|
const HASS_REJECTION_CUTOFF_MS = 5 * 60 * 1000;
|
||||||
@@ -42,9 +41,6 @@ export class FrigateCardImage extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public cameraConfig?: CameraConfig;
|
public cameraConfig?: CameraConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
public supportZoom = false;
|
|
||||||
|
|
||||||
// Using contentsChanged to ensure overridden configs (e.g. when the
|
// Using contentsChanged to ensure overridden configs (e.g. when the
|
||||||
// 'show_image_during_load' option is true for live views, an overridden
|
// 'show_image_during_load' option is true for live views, an overridden
|
||||||
// config may be used here).
|
// config may be used here).
|
||||||
@@ -115,6 +111,10 @@ export class FrigateCardImage extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
updateElementStyleFromMediaLayoutConfig(this, this.imageConfig?.layout);
|
updateElementStyleFromMediaLayoutConfig(this, this.imageConfig?.layout);
|
||||||
|
|
||||||
|
if (changedProps.has('imageConfig') && this.imageConfig?.zoomable) {
|
||||||
|
import('./zoomer.js');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the camera or view changed, immediately discard the old value (view to
|
// If the camera or view changed, immediately discard the old value (view to
|
||||||
@@ -235,10 +235,10 @@ export class FrigateCardImage extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _renderZoom(contents: TemplateResult): TemplateResult {
|
protected _useZoomIfRequired(template: TemplateResult): TemplateResult {
|
||||||
return this.supportZoom
|
return this.imageConfig?.zoomable
|
||||||
? html` <frigate-card-zoomer>${contents}</frigate-card-zoomer>`
|
? html` <frigate-card-zoomer> ${template} </frigate-card-zoomer>`
|
||||||
: contents;
|
: template;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
@@ -246,7 +246,7 @@ export class FrigateCardImage extends LitElement {
|
|||||||
// Note the use of live() below to ensure the update will restore the image
|
// Note the use of live() below to ensure the update will restore the image
|
||||||
// src if it's been changed via _forceSafeImage().
|
// src if it's been changed via _forceSafeImage().
|
||||||
return src
|
return src
|
||||||
? this._renderZoom(html` <img
|
? this._useZoomIfRequired(html` <img
|
||||||
${ref(this._refImage)}
|
${ref(this._refImage)}
|
||||||
src=${live(src)}
|
src=${live(src)}
|
||||||
@load=${(ev: Event) => {
|
@load=${(ev: Event) => {
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ export class FrigateCardLiveImage extends LitElement implements FrigateCardMedia
|
|||||||
mode: this.cameraConfig.image.url ? ('url' as const) : ('camera' as const),
|
mode: this.cameraConfig.image.url ? ('url' as const) : ('camera' as const),
|
||||||
refresh_seconds: this._playing ? this.cameraConfig.image.refresh_seconds : 0,
|
refresh_seconds: this._playing ? this.cameraConfig.image.refresh_seconds : 0,
|
||||||
url: this.cameraConfig.image.url,
|
url: this.cameraConfig.image.url,
|
||||||
|
|
||||||
|
// The live provider will take care of zoom.
|
||||||
|
zoomable: false,
|
||||||
|
|
||||||
// Don't need to pass layout options as FrigateCardLiveProvider has
|
// Don't need to pass layout options as FrigateCardLiveProvider has
|
||||||
// already taken care of this for us.
|
// already taken care of this for us.
|
||||||
}}
|
}}
|
||||||
|
|||||||
+77
-64
@@ -54,7 +54,6 @@ import { dispatchErrorMessageEvent, dispatchMessageEvent } from '../message.js';
|
|||||||
import '../next-prev-control.js';
|
import '../next-prev-control.js';
|
||||||
import '../surround.js';
|
import '../surround.js';
|
||||||
import '../title-control.js';
|
import '../title-control.js';
|
||||||
import '../zoomer.js';
|
|
||||||
import { AutoMediaPlugin } from './../embla-plugins/automedia.js';
|
import { AutoMediaPlugin } from './../embla-plugins/automedia.js';
|
||||||
import { Lazyload } from './../embla-plugins/lazyload.js';
|
import { Lazyload } from './../embla-plugins/lazyload.js';
|
||||||
|
|
||||||
@@ -525,7 +524,12 @@ export class FrigateCardLiveCarousel extends LitElement {
|
|||||||
cameraConfig: CameraConfig,
|
cameraConfig: CameraConfig,
|
||||||
slideIndex: number,
|
slideIndex: number,
|
||||||
): TemplateResult | void {
|
): TemplateResult | void {
|
||||||
if (!this.liveConfig || !this.hass || !this.cameraManager || !this.conditionControllerEpoch) {
|
if (
|
||||||
|
!this.liveConfig ||
|
||||||
|
!this.hass ||
|
||||||
|
!this.cameraManager ||
|
||||||
|
!this.conditionControllerEpoch
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// The condition controller object contains the currently live camera, which
|
// The condition controller object contains the currently live camera, which
|
||||||
@@ -545,7 +549,9 @@ export class FrigateCardLiveCarousel extends LitElement {
|
|||||||
<div class="embla__slide">
|
<div class="embla__slide">
|
||||||
<frigate-card-live-provider
|
<frigate-card-live-provider
|
||||||
?disabled=${this.liveConfig.lazy_load}
|
?disabled=${this.liveConfig.lazy_load}
|
||||||
.microphoneStream=${this.view?.camera === cameraID ? this.microphoneStream : undefined}
|
.microphoneStream=${this.view?.camera === cameraID
|
||||||
|
? this.microphoneStream
|
||||||
|
: undefined}
|
||||||
.cameraConfig=${cameraConfig}
|
.cameraConfig=${cameraConfig}
|
||||||
.cameraEndpoints=${guard(
|
.cameraEndpoints=${guard(
|
||||||
[this.cameraManager, cameraID],
|
[this.cameraManager, cameraID],
|
||||||
@@ -848,6 +854,9 @@ export class FrigateCardLiveProvider
|
|||||||
if (this.liveConfig?.show_image_during_load) {
|
if (this.liveConfig?.show_image_during_load) {
|
||||||
this._importPromises.push(import('./live-image.js'));
|
this._importPromises.push(import('./live-image.js'));
|
||||||
}
|
}
|
||||||
|
if (this.liveConfig?.zoomable) {
|
||||||
|
this._importPromises.push(import('./../zoomer.js'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (changedProps.has('cameraConfig')) {
|
if (changedProps.has('cameraConfig')) {
|
||||||
const provider = this._getResolvedProvider();
|
const provider = this._getResolvedProvider();
|
||||||
@@ -874,6 +883,17 @@ export class FrigateCardLiveProvider
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _useZoomIfRequired(template: TemplateResult): TemplateResult {
|
||||||
|
return this.liveConfig?.zoomable
|
||||||
|
? html` <frigate-card-zoomer
|
||||||
|
@frigate-card:zoom:zoomed=${() => this.setControls(false)}
|
||||||
|
@frigate-card:zoom:unzoomed=${() => this.setControls(true)}
|
||||||
|
>
|
||||||
|
${template}
|
||||||
|
</frigate-card-zoomer>`
|
||||||
|
: template;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Master render method.
|
* Master render method.
|
||||||
* @returns A rendered template.
|
* @returns A rendered template.
|
||||||
@@ -894,42 +914,36 @@ export class FrigateCardLiveProvider
|
|||||||
hidden: showImageDuringLoading,
|
hidden: showImageDuringLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
return html`
|
return this._useZoomIfRequired(html`
|
||||||
<frigate-card-zoomer
|
${showImageDuringLoading || provider === 'image'
|
||||||
@frigate-card:zoom:zoomed=${() => this.setControls(false)}
|
? html` <frigate-card-live-image
|
||||||
@frigate-card:zoom:unzoomed=${() => this.setControls(true)}
|
${ref(this._refProvider)}
|
||||||
>
|
.hass=${this.hass}
|
||||||
${showImageDuringLoading || provider === 'image'
|
.cameraConfig=${this.cameraConfig}
|
||||||
? html`
|
@frigate-card:media:loaded=${(ev: Event) => {
|
||||||
<frigate-card-live-image
|
if (provider === 'image') {
|
||||||
${ref(this._refProvider)}
|
// Only count the media has loaded if the required provider is
|
||||||
.hass=${this.hass}
|
// the image (not just the temporary image shown during
|
||||||
.cameraConfig=${this.cameraConfig}
|
// loading).
|
||||||
@frigate-card:media:loaded=${(ev: Event) => {
|
this._videoMediaShowHandler();
|
||||||
if (provider === 'image') {
|
} else {
|
||||||
// Only count the media has loaded if the required provider is
|
ev.stopPropagation();
|
||||||
// the image (not just the temporary image shown during
|
}
|
||||||
// loading).
|
}}
|
||||||
this._videoMediaShowHandler();
|
>
|
||||||
} else {
|
</frigate-card-live-image>`
|
||||||
ev.stopPropagation();
|
: html``}
|
||||||
}
|
${provider === 'ha'
|
||||||
}}
|
? html` <frigate-card-live-ha
|
||||||
>
|
${ref(this._refProvider)}
|
||||||
</frigate-card-live-image
|
class=${classMap(providerClasses)}
|
||||||
></frigate-card-zoomer>`
|
.hass=${this.hass}
|
||||||
: html``}
|
.cameraConfig=${this.cameraConfig}
|
||||||
${provider === 'ha'
|
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
|
||||||
? html` <frigate-card-live-ha
|
>
|
||||||
${ref(this._refProvider)}
|
</frigate-card-live-ha>`
|
||||||
class=${classMap(providerClasses)}
|
: provider === 'go2rtc'
|
||||||
.hass=${this.hass}
|
? html`<frigate-card-live-go2rtc
|
||||||
.cameraConfig=${this.cameraConfig}
|
|
||||||
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
|
|
||||||
>
|
|
||||||
</frigate-card-live-ha>`
|
|
||||||
: provider === 'go2rtc'
|
|
||||||
? html`<frigate-card-live-go2rtc
|
|
||||||
${ref(this._refProvider)}
|
${ref(this._refProvider)}
|
||||||
class=${classMap(providerClasses)}
|
class=${classMap(providerClasses)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -940,31 +954,30 @@ export class FrigateCardLiveProvider
|
|||||||
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
|
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
|
||||||
>
|
>
|
||||||
</frigate-card-live-webrtc-card>`
|
</frigate-card-live-webrtc-card>`
|
||||||
: provider === 'webrtc-card'
|
: provider === 'webrtc-card'
|
||||||
? html`<frigate-card-live-webrtc-card
|
? html`<frigate-card-live-webrtc-card
|
||||||
${ref(this._refProvider)}
|
${ref(this._refProvider)}
|
||||||
class=${classMap(providerClasses)}
|
class=${classMap(providerClasses)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.cameraConfig=${this.cameraConfig}
|
.cameraConfig=${this.cameraConfig}
|
||||||
.cameraEndpoints=${this.cameraEndpoints}
|
.cameraEndpoints=${this.cameraEndpoints}
|
||||||
.cardWideConfig=${this.cardWideConfig}
|
.cardWideConfig=${this.cardWideConfig}
|
||||||
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
|
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
|
||||||
>
|
>
|
||||||
</frigate-card-live-webrtc-card>`
|
</frigate-card-live-webrtc-card>`
|
||||||
: provider === 'jsmpeg'
|
: provider === 'jsmpeg'
|
||||||
? html` <frigate-card-live-jsmpeg
|
? html` <frigate-card-live-jsmpeg
|
||||||
${ref(this._refProvider)}
|
${ref(this._refProvider)}
|
||||||
class=${classMap(providerClasses)}
|
class=${classMap(providerClasses)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.cameraConfig=${this.cameraConfig}
|
.cameraConfig=${this.cameraConfig}
|
||||||
.cameraEndpoints=${this.cameraEndpoints}
|
.cameraEndpoints=${this.cameraEndpoints}
|
||||||
.cardWideConfig=${this.cardWideConfig}
|
.cardWideConfig=${this.cardWideConfig}
|
||||||
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
|
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
|
||||||
>
|
>
|
||||||
</frigate-card-live-jsmpeg>`
|
</frigate-card-live-jsmpeg>`
|
||||||
: html``}
|
: html``}
|
||||||
</frigate-card-zoomer>
|
`);
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
|||||||
+70
-61
@@ -47,7 +47,6 @@ import { MediaQueriesClassifier } from '../view/media-queries-classifier';
|
|||||||
import { MediaQueriesResults } from '../view/media-queries-results.js';
|
import { MediaQueriesResults } from '../view/media-queries-results.js';
|
||||||
import { VideoContentType, ViewMedia } from '../view/media.js';
|
import { VideoContentType, ViewMedia } from '../view/media.js';
|
||||||
import { View } from '../view/view.js';
|
import { View } from '../view/view.js';
|
||||||
import './zoomer.js';
|
|
||||||
import type { CarouselSelect } from './carousel.js';
|
import type { CarouselSelect } from './carousel.js';
|
||||||
import { AutoMediaPlugin } from './embla-plugins/automedia.js';
|
import { AutoMediaPlugin } from './embla-plugins/automedia.js';
|
||||||
import { Lazyload } from './embla-plugins/lazyload.js';
|
import { Lazyload } from './embla-plugins/lazyload.js';
|
||||||
@@ -683,6 +682,21 @@ export class FrigateCardViewerProvider
|
|||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changedProps.has('viewerConfig') && this.viewerConfig?.zoomable) {
|
||||||
|
import('./zoomer.js');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _useZoomIfRequired(template: TemplateResult): TemplateResult {
|
||||||
|
return this.viewerConfig?.zoomable
|
||||||
|
? html` <frigate-card-zoomer
|
||||||
|
@frigate-card:zoom:zoomed=${() => this.setControls(false)}
|
||||||
|
@frigate-card:zoom:unzoomed=${() => this.setControls(true)}
|
||||||
|
>
|
||||||
|
${template}
|
||||||
|
</frigate-card-zoomer>`
|
||||||
|
: template;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
@@ -702,68 +716,63 @@ export class FrigateCardViewerProvider
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return this._useZoomIfRequired(html`
|
||||||
<frigate-card-zoomer
|
${ViewMediaClassifier.isVideo(this.media)
|
||||||
@frigate-card:zoom:zoomed=${() => this.setControls(false)}
|
? this.media.getVideoContentType() === VideoContentType.HLS
|
||||||
@frigate-card:zoom:unzoomed=${() => this.setControls(true)}
|
? html`<frigate-card-ha-hls-player
|
||||||
>
|
${ref(this._refFrigateCardMediaPlayer)}
|
||||||
${ViewMediaClassifier.isVideo(this.media)
|
allow-exoplayer
|
||||||
? this.media.getVideoContentType() === VideoContentType.HLS
|
|
||||||
? html`<frigate-card-ha-hls-player
|
|
||||||
${ref(this._refFrigateCardMediaPlayer)}
|
|
||||||
allow-exoplayer
|
|
||||||
aria-label="${this.media.getTitle() ?? ''}"
|
|
||||||
?autoplay=${false}
|
|
||||||
controls
|
|
||||||
muted
|
|
||||||
playsinline
|
|
||||||
title="${this.media.getTitle() ?? ''}"
|
|
||||||
url=${canonicalizeHAURL(this.hass, resolvedMedia?.url) ?? ''}
|
|
||||||
.hass=${this.hass}
|
|
||||||
>
|
|
||||||
</frigate-card-ha-hls-player>`
|
|
||||||
: html`
|
|
||||||
<video
|
|
||||||
${ref(this._refVideoProvider)}
|
|
||||||
aria-label="${this.media.getTitle() ?? ''}"
|
|
||||||
title="${this.media.getTitle() ?? ''}"
|
|
||||||
muted
|
|
||||||
controls
|
|
||||||
playsinline
|
|
||||||
?autoplay=${false}
|
|
||||||
@loadedmetadata=${(ev: Event) => {
|
|
||||||
if (ev.target) {
|
|
||||||
hideMediaControlsTemporarily(
|
|
||||||
ev.target as HTMLVideoElement,
|
|
||||||
MEDIA_LOAD_CONTROLS_HIDE_SECONDS,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
@loadeddata=${(ev: Event) => {
|
|
||||||
dispatchMediaLoadedEvent(this, ev);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<source
|
|
||||||
src=${canonicalizeHAURL(this.hass, resolvedMedia?.url) ?? ''}
|
|
||||||
type="video/mp4"
|
|
||||||
/>
|
|
||||||
</video>
|
|
||||||
`
|
|
||||||
: html`<img
|
|
||||||
aria-label="${this.media.getTitle() ?? ''}"
|
aria-label="${this.media.getTitle() ?? ''}"
|
||||||
src="${canonicalizeHAURL(this.hass, resolvedMedia?.url) ?? ''}"
|
?autoplay=${false}
|
||||||
|
controls
|
||||||
|
muted
|
||||||
|
playsinline
|
||||||
title="${this.media.getTitle() ?? ''}"
|
title="${this.media.getTitle() ?? ''}"
|
||||||
@click=${() => {
|
url=${canonicalizeHAURL(this.hass, resolvedMedia?.url) ?? ''}
|
||||||
if (this.viewerConfig?.snapshot_click_plays_clip) {
|
.hass=${this.hass}
|
||||||
this._dispatchRelatedClipView();
|
>
|
||||||
}
|
</frigate-card-ha-hls-player>`
|
||||||
}}
|
: html`
|
||||||
@load=${(e: Event) => {
|
<video
|
||||||
dispatchMediaLoadedEvent(this, e);
|
${ref(this._refVideoProvider)}
|
||||||
}}
|
aria-label="${this.media.getTitle() ?? ''}"
|
||||||
/>`}
|
title="${this.media.getTitle() ?? ''}"
|
||||||
</frigate-card-zoomer>
|
muted
|
||||||
`;
|
controls
|
||||||
|
playsinline
|
||||||
|
?autoplay=${false}
|
||||||
|
@loadedmetadata=${(ev: Event) => {
|
||||||
|
if (ev.target) {
|
||||||
|
hideMediaControlsTemporarily(
|
||||||
|
ev.target as HTMLVideoElement,
|
||||||
|
MEDIA_LOAD_CONTROLS_HIDE_SECONDS,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
@loadeddata=${(ev: Event) => {
|
||||||
|
dispatchMediaLoadedEvent(this, ev);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<source
|
||||||
|
src=${canonicalizeHAURL(this.hass, resolvedMedia?.url) ?? ''}
|
||||||
|
type="video/mp4"
|
||||||
|
/>
|
||||||
|
</video>
|
||||||
|
`
|
||||||
|
: html`<img
|
||||||
|
aria-label="${this.media.getTitle() ?? ''}"
|
||||||
|
src="${canonicalizeHAURL(this.hass, resolvedMedia?.url) ?? ''}"
|
||||||
|
title="${this.media.getTitle() ?? ''}"
|
||||||
|
@click=${() => {
|
||||||
|
if (this.viewerConfig?.snapshot_click_plays_clip) {
|
||||||
|
this._dispatchRelatedClipView();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
@load=${(e: Event) => {
|
||||||
|
dispatchMediaLoadedEvent(this, e);
|
||||||
|
}}
|
||||||
|
/>`}
|
||||||
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE =
|
|||||||
`${CONF_MEDIA_VIEWER}.controls.timeline.style` as const;
|
`${CONF_MEDIA_VIEWER}.controls.timeline.style` as const;
|
||||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS =
|
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS =
|
||||||
`${CONF_MEDIA_VIEWER}.controls.timeline.window_seconds` as const;
|
`${CONF_MEDIA_VIEWER}.controls.timeline.window_seconds` as const;
|
||||||
|
export const CONF_MEDIA_VIEWER_ZOOMABLE = `${CONF_MEDIA_VIEWER}.zoomable` as const;
|
||||||
|
|
||||||
export const CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE =
|
export const CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE =
|
||||||
`${CONF_MEDIA_VIEWER}.controls.title.mode` as const;
|
`${CONF_MEDIA_VIEWER}.controls.title.mode` as const;
|
||||||
@@ -193,6 +194,7 @@ export const CONF_LIVE_SHOW_IMAGE_DURING_LOAD =
|
|||||||
`${CONF_LIVE}.show_image_during_load` as const;
|
`${CONF_LIVE}.show_image_during_load` as const;
|
||||||
export const CONF_LIVE_MICROPHONE_DISCONNECT_SECONDS =
|
export const CONF_LIVE_MICROPHONE_DISCONNECT_SECONDS =
|
||||||
`${CONF_LIVE}.microphone.disconnect_seconds` as const;
|
`${CONF_LIVE}.microphone.disconnect_seconds` as const;
|
||||||
|
export const CONF_LIVE_ZOOMABLE = `${CONF_LIVE}.zoomable` as const;
|
||||||
|
|
||||||
const CONF_IMAGE = 'image' as const;
|
const CONF_IMAGE = 'image' as const;
|
||||||
export const CONF_IMAGE_LAYOUT_FIT = `${CONF_IMAGE}.layout.fit` as const;
|
export const CONF_IMAGE_LAYOUT_FIT = `${CONF_IMAGE}.layout.fit` as const;
|
||||||
@@ -201,6 +203,7 @@ export const CONF_IMAGE_LAYOUT_POSITION_Y = `${CONF_IMAGE}.layout.position.y` as
|
|||||||
export const CONF_IMAGE_MODE = `${CONF_IMAGE}.mode` as const;
|
export const CONF_IMAGE_MODE = `${CONF_IMAGE}.mode` as const;
|
||||||
export const CONF_IMAGE_REFRESH_SECONDS = `${CONF_IMAGE}.refresh_seconds` as const;
|
export const CONF_IMAGE_REFRESH_SECONDS = `${CONF_IMAGE}.refresh_seconds` as const;
|
||||||
export const CONF_IMAGE_URL = `${CONF_IMAGE}.url` as const;
|
export const CONF_IMAGE_URL = `${CONF_IMAGE}.url` as const;
|
||||||
|
export const CONF_IMAGE_ZOOMABLE = `${CONF_IMAGE}.zoomable` as const;
|
||||||
|
|
||||||
const CONF_TIMELINE = 'timeline' as const;
|
const CONF_TIMELINE = 'timeline' as const;
|
||||||
export const CONF_TIMELINE_WINDOW_SECONDS = `${CONF_TIMELINE}.window_seconds` as const;
|
export const CONF_TIMELINE_WINDOW_SECONDS = `${CONF_TIMELINE}.window_seconds` as const;
|
||||||
|
|||||||
+24
-15
@@ -2,6 +2,11 @@ import { fireEvent, HomeAssistant, LovelaceCardEditor } from 'custom-card-helper
|
|||||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
import { CSSResultGroup, html, LitElement, 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 { FRIGATE_ICON_SVG_PATH } from './camera-manager/frigate/icon.js';
|
||||||
|
import {
|
||||||
|
MOTIONEYE_ICON_SVG_PATH,
|
||||||
|
MOTIONEYE_ICON_SVG_VIEWBOX,
|
||||||
|
} from './camera-manager/motioneye/icon.js';
|
||||||
import {
|
import {
|
||||||
copyConfig,
|
copyConfig,
|
||||||
deleteConfigValue,
|
deleteConfigValue,
|
||||||
@@ -12,6 +17,7 @@ import {
|
|||||||
upgradeConfig,
|
upgradeConfig,
|
||||||
} from './config-mgmt.js';
|
} from './config-mgmt.js';
|
||||||
import {
|
import {
|
||||||
|
CONF_CAMERAS,
|
||||||
CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
|
CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
|
||||||
CONF_CAMERAS_ARRAY_DEPENDENCIES_ALL_CAMERAS,
|
CONF_CAMERAS_ARRAY_DEPENDENCIES_ALL_CAMERAS,
|
||||||
CONF_CAMERAS_ARRAY_DEPENDENCIES_CAMERAS,
|
CONF_CAMERAS_ARRAY_DEPENDENCIES_CAMERAS,
|
||||||
@@ -39,15 +45,17 @@ import {
|
|||||||
CONF_CAMERAS_ARRAY_TRIGGERS_OCCUPANCY,
|
CONF_CAMERAS_ARRAY_TRIGGERS_OCCUPANCY,
|
||||||
CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY,
|
CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY,
|
||||||
CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL,
|
CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL,
|
||||||
CONF_CAMERAS,
|
|
||||||
CONF_DIMENSIONS_ASPECT_RATIO_MODE,
|
|
||||||
CONF_DIMENSIONS_ASPECT_RATIO,
|
CONF_DIMENSIONS_ASPECT_RATIO,
|
||||||
|
CONF_DIMENSIONS_ASPECT_RATIO_MODE,
|
||||||
|
CONF_DIMENSIONS_MAX_HEIGHT,
|
||||||
|
CONF_DIMENSIONS_MIN_HEIGHT,
|
||||||
CONF_IMAGE_LAYOUT_FIT,
|
CONF_IMAGE_LAYOUT_FIT,
|
||||||
CONF_IMAGE_LAYOUT_POSITION_X,
|
CONF_IMAGE_LAYOUT_POSITION_X,
|
||||||
CONF_IMAGE_LAYOUT_POSITION_Y,
|
CONF_IMAGE_LAYOUT_POSITION_Y,
|
||||||
CONF_IMAGE_MODE,
|
CONF_IMAGE_MODE,
|
||||||
CONF_IMAGE_REFRESH_SECONDS,
|
CONF_IMAGE_REFRESH_SECONDS,
|
||||||
CONF_IMAGE_URL,
|
CONF_IMAGE_URL,
|
||||||
|
CONF_IMAGE_ZOOMABLE,
|
||||||
CONF_LIVE_AUTO_MUTE,
|
CONF_LIVE_AUTO_MUTE,
|
||||||
CONF_LIVE_AUTO_PAUSE,
|
CONF_LIVE_AUTO_PAUSE,
|
||||||
CONF_LIVE_AUTO_PLAY,
|
CONF_LIVE_AUTO_PLAY,
|
||||||
@@ -65,6 +73,7 @@ import {
|
|||||||
CONF_LIVE_CONTROLS_TIMELINE_MEDIA,
|
CONF_LIVE_CONTROLS_TIMELINE_MEDIA,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_MODE,
|
CONF_LIVE_CONTROLS_TIMELINE_MODE,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||||
|
CONF_LIVE_CONTROLS_TIMELINE_STYLE,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||||
CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS,
|
CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS,
|
||||||
CONF_LIVE_CONTROLS_TITLE_MODE,
|
CONF_LIVE_CONTROLS_TITLE_MODE,
|
||||||
@@ -74,9 +83,11 @@ import {
|
|||||||
CONF_LIVE_LAYOUT_POSITION_Y,
|
CONF_LIVE_LAYOUT_POSITION_Y,
|
||||||
CONF_LIVE_LAZY_LOAD,
|
CONF_LIVE_LAZY_LOAD,
|
||||||
CONF_LIVE_LAZY_UNLOAD,
|
CONF_LIVE_LAZY_UNLOAD,
|
||||||
|
CONF_LIVE_MICROPHONE_DISCONNECT_SECONDS,
|
||||||
CONF_LIVE_PRELOAD,
|
CONF_LIVE_PRELOAD,
|
||||||
CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
|
CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
|
||||||
CONF_LIVE_TRANSITION_EFFECT,
|
CONF_LIVE_TRANSITION_EFFECT,
|
||||||
|
CONF_LIVE_ZOOMABLE,
|
||||||
CONF_MEDIA_GALLERY_CONTROLS_FILTER_MODE,
|
CONF_MEDIA_GALLERY_CONTROLS_FILTER_MODE,
|
||||||
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
||||||
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
|
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
|
||||||
@@ -99,6 +110,7 @@ import {
|
|||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MEDIA,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MEDIA,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||||
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TITLE_DURATION_SECONDS,
|
CONF_MEDIA_VIEWER_CONTROLS_TITLE_DURATION_SECONDS,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE,
|
CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE,
|
||||||
@@ -109,6 +121,7 @@ import {
|
|||||||
CONF_MEDIA_VIEWER_LAZY_LOAD,
|
CONF_MEDIA_VIEWER_LAZY_LOAD,
|
||||||
CONF_MEDIA_VIEWER_SNAPSHOT_CLICK_PLAYS_CLIP,
|
CONF_MEDIA_VIEWER_SNAPSHOT_CLICK_PLAYS_CLIP,
|
||||||
CONF_MEDIA_VIEWER_TRANSITION_EFFECT,
|
CONF_MEDIA_VIEWER_TRANSITION_EFFECT,
|
||||||
|
CONF_MEDIA_VIEWER_ZOOMABLE,
|
||||||
CONF_MENU_ALIGNMENT,
|
CONF_MENU_ALIGNMENT,
|
||||||
CONF_MENU_BUTTON_SIZE,
|
CONF_MENU_BUTTON_SIZE,
|
||||||
CONF_MENU_BUTTONS,
|
CONF_MENU_BUTTONS,
|
||||||
@@ -128,28 +141,24 @@ import {
|
|||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SIZE,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_SIZE,
|
||||||
CONF_TIMELINE_MEDIA,
|
CONF_TIMELINE_MEDIA,
|
||||||
CONF_TIMELINE_SHOW_RECORDINGS,
|
CONF_TIMELINE_SHOW_RECORDINGS,
|
||||||
|
CONF_TIMELINE_STYLE,
|
||||||
CONF_TIMELINE_WINDOW_SECONDS,
|
CONF_TIMELINE_WINDOW_SECONDS,
|
||||||
CONF_VIEW_CAMERA_SELECT,
|
CONF_VIEW_CAMERA_SELECT,
|
||||||
CONF_VIEW_DARK_MODE,
|
CONF_VIEW_DARK_MODE,
|
||||||
CONF_VIEW_DEFAULT,
|
CONF_VIEW_DEFAULT,
|
||||||
|
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_RESET,
|
||||||
CONF_VIEW_SCAN_UNTRIGGER_SECONDS,
|
CONF_VIEW_SCAN_UNTRIGGER_SECONDS,
|
||||||
CONF_VIEW_SCAN,
|
|
||||||
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,
|
||||||
CONF_VIEW_UPDATE_SECONDS,
|
CONF_VIEW_UPDATE_SECONDS,
|
||||||
MEDIA_CHUNK_SIZE_MAX,
|
MEDIA_CHUNK_SIZE_MAX,
|
||||||
CONF_DIMENSIONS_MAX_HEIGHT,
|
|
||||||
CONF_DIMENSIONS_MIN_HEIGHT,
|
|
||||||
CONF_TIMELINE_STYLE,
|
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_STYLE,
|
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE,
|
|
||||||
CONF_LIVE_MICROPHONE_DISCONNECT_SECONDS,
|
|
||||||
} from './const.js';
|
} from './const.js';
|
||||||
import { localize } from './localize/localize.js';
|
import { localize } from './localize/localize.js';
|
||||||
|
import { setLowPerformanceProfile } from './performance.js';
|
||||||
import frigate_card_editor_style from './scss/editor.scss';
|
import frigate_card_editor_style from './scss/editor.scss';
|
||||||
import {
|
import {
|
||||||
BUTTON_SIZE_MIN,
|
BUTTON_SIZE_MIN,
|
||||||
@@ -163,17 +172,11 @@ import {
|
|||||||
} from './types.js';
|
} from './types.js';
|
||||||
import { arrayMove, prettifyTitle } from './utils/basic.js';
|
import { arrayMove, prettifyTitle } from './utils/basic.js';
|
||||||
import { getCameraID } from './utils/camera.js';
|
import { getCameraID } from './utils/camera.js';
|
||||||
import { FRIGATE_ICON_SVG_PATH } from './camera-manager/frigate/icon.js';
|
|
||||||
import {
|
import {
|
||||||
getEntitiesFromHASS,
|
getEntitiesFromHASS,
|
||||||
getEntityTitle,
|
getEntityTitle,
|
||||||
sideLoadHomeAssistantElements,
|
sideLoadHomeAssistantElements,
|
||||||
} from './utils/ha';
|
} from './utils/ha';
|
||||||
import { setLowPerformanceProfile } from './performance.js';
|
|
||||||
import {
|
|
||||||
MOTIONEYE_ICON_SVG_PATH,
|
|
||||||
MOTIONEYE_ICON_SVG_VIEWBOX,
|
|
||||||
} from './camera-manager/motioneye/icon.js';
|
|
||||||
|
|
||||||
const MENU_BUTTONS = 'buttons';
|
const MENU_BUTTONS = 'buttons';
|
||||||
const MENU_CAMERAS = 'cameras';
|
const MENU_CAMERAS = 'cameras';
|
||||||
@@ -1770,6 +1773,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<div class="values">
|
<div class="values">
|
||||||
${this._renderSwitch(CONF_LIVE_PRELOAD, this._defaults.live.preload)}
|
${this._renderSwitch(CONF_LIVE_PRELOAD, this._defaults.live.preload)}
|
||||||
${this._renderSwitch(CONF_LIVE_DRAGGABLE, this._defaults.live.draggable)}
|
${this._renderSwitch(CONF_LIVE_DRAGGABLE, this._defaults.live.draggable)}
|
||||||
|
${this._renderSwitch(CONF_LIVE_ZOOMABLE, this._defaults.live.zoomable)}
|
||||||
${this._renderSwitch(CONF_LIVE_LAZY_LOAD, this._defaults.live.lazy_load)}
|
${this._renderSwitch(CONF_LIVE_LAZY_LOAD, this._defaults.live.lazy_load)}
|
||||||
${this._renderOptionSelector(
|
${this._renderOptionSelector(
|
||||||
CONF_LIVE_LAZY_UNLOAD,
|
CONF_LIVE_LAZY_UNLOAD,
|
||||||
@@ -1903,6 +1907,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
CONF_MEDIA_VIEWER_DRAGGABLE,
|
CONF_MEDIA_VIEWER_DRAGGABLE,
|
||||||
this._defaults.media_viewer.draggable,
|
this._defaults.media_viewer.draggable,
|
||||||
)}
|
)}
|
||||||
|
${this._renderSwitch(
|
||||||
|
CONF_MEDIA_VIEWER_ZOOMABLE,
|
||||||
|
this._defaults.media_viewer.zoomable,
|
||||||
|
)}
|
||||||
${this._renderSwitch(
|
${this._renderSwitch(
|
||||||
CONF_MEDIA_VIEWER_LAZY_LOAD,
|
CONF_MEDIA_VIEWER_LAZY_LOAD,
|
||||||
this._defaults.media_viewer.lazy_load,
|
this._defaults.media_viewer.lazy_load,
|
||||||
@@ -1973,6 +1981,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
${this._renderOptionSelector(CONF_IMAGE_MODE, this._imageModes)}
|
${this._renderOptionSelector(CONF_IMAGE_MODE, this._imageModes)}
|
||||||
${this._renderStringInput(CONF_IMAGE_URL)}
|
${this._renderStringInput(CONF_IMAGE_URL)}
|
||||||
${this._renderNumberInput(CONF_IMAGE_REFRESH_SECONDS)}
|
${this._renderNumberInput(CONF_IMAGE_REFRESH_SECONDS)}
|
||||||
|
${this._renderSwitch(CONF_IMAGE_ZOOMABLE, this._defaults.image.zoomable)}
|
||||||
${this._renderMediaLayout(
|
${this._renderMediaLayout(
|
||||||
MENU_IMAGE_LAYOUT,
|
MENU_IMAGE_LAYOUT,
|
||||||
'config.image.layout',
|
'config.image.layout',
|
||||||
|
|||||||
@@ -204,7 +204,8 @@
|
|||||||
"url": "Arbitrary image specified by URL"
|
"url": "Arbitrary image specified by URL"
|
||||||
},
|
},
|
||||||
"refresh_seconds": "Number of seconds after which to refresh (0=never)",
|
"refresh_seconds": "Number of seconds after which to refresh (0=never)",
|
||||||
"url": "Static image URL for image view"
|
"url": "Static image URL for image view",
|
||||||
|
"zoomable": "Image can be zoomed/panned"
|
||||||
},
|
},
|
||||||
"live": {
|
"live": {
|
||||||
"auto_mute": "Automatically mute live cameras",
|
"auto_mute": "Automatically mute live cameras",
|
||||||
@@ -225,7 +226,8 @@
|
|||||||
},
|
},
|
||||||
"preload": "Preload live view in the background",
|
"preload": "Preload live view in the background",
|
||||||
"show_image_during_load": "Show still image while the live stream is loading",
|
"show_image_during_load": "Show still image while the live stream is loading",
|
||||||
"transition_effect": "Live camera transition effect"
|
"transition_effect": "Live camera transition effect",
|
||||||
|
"zoomable": "Live cameras can be zoomed/panned"
|
||||||
},
|
},
|
||||||
"media_viewer": {
|
"media_viewer": {
|
||||||
"auto_mute": "Automatically mute media",
|
"auto_mute": "Automatically mute media",
|
||||||
@@ -243,7 +245,8 @@
|
|||||||
"transition_effects": {
|
"transition_effects": {
|
||||||
"none": "No transition",
|
"none": "No transition",
|
||||||
"slide": "Slide transition"
|
"slide": "Slide transition"
|
||||||
}
|
},
|
||||||
|
"zoomable": "Media Viewer can be zoomed/panned"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"alignment": "Menu alignment",
|
"alignment": "Menu alignment",
|
||||||
|
|||||||
@@ -204,7 +204,8 @@
|
|||||||
"url": "Immagine arbitraria specificata dall'URL"
|
"url": "Immagine arbitraria specificata dall'URL"
|
||||||
},
|
},
|
||||||
"refresh_seconds": "Numero di secondi dopo i quali aggiornare (0 = mai)",
|
"refresh_seconds": "Numero di secondi dopo i quali aggiornare (0 = mai)",
|
||||||
"url": "URL di immagine statica per la vista dell'immagine"
|
"url": "URL di immagine statica per la vista dell'immagine",
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"live": {
|
"live": {
|
||||||
"auto_mute": "Muta automaticamente le telecamere in diretta",
|
"auto_mute": "Muta automaticamente le telecamere in diretta",
|
||||||
@@ -225,7 +226,8 @@
|
|||||||
},
|
},
|
||||||
"preload": "Precarica Live View in background",
|
"preload": "Precarica Live View in background",
|
||||||
"show_image_during_load": "Mostra un'immagine fissa durante il caricamento del live streaming",
|
"show_image_during_load": "Mostra un'immagine fissa durante il caricamento del live streaming",
|
||||||
"transition_effect": "Effetto di transizione della telecamera dal vivo"
|
"transition_effect": "Effetto di transizione della telecamera dal vivo",
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"media_viewer": {
|
"media_viewer": {
|
||||||
"auto_mute": "Muta automaticamente i media",
|
"auto_mute": "Muta automaticamente i media",
|
||||||
@@ -242,7 +244,8 @@
|
|||||||
"transition_effects": {
|
"transition_effects": {
|
||||||
"none": "Nessuna transizione",
|
"none": "Nessuna transizione",
|
||||||
"slide": "Transizione diapositiva"
|
"slide": "Transizione diapositiva"
|
||||||
}
|
},
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"alignment": "Allineamento dei menu",
|
"alignment": "Allineamento dei menu",
|
||||||
|
|||||||
@@ -204,7 +204,8 @@
|
|||||||
"url": "Imagem arbitrária especificada por URL"
|
"url": "Imagem arbitrária especificada por URL"
|
||||||
},
|
},
|
||||||
"refresh_seconds": "Número de segundos após o qual atualizar (0 = nunca)",
|
"refresh_seconds": "Número de segundos após o qual atualizar (0 = nunca)",
|
||||||
"url": "Imagem arbitrária especificada por URL"
|
"url": "Imagem arbitrária especificada por URL",
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"live": {
|
"live": {
|
||||||
"auto_mute": "Silenciar câmeras ao vivo automaticamente",
|
"auto_mute": "Silenciar câmeras ao vivo automaticamente",
|
||||||
@@ -225,7 +226,8 @@
|
|||||||
},
|
},
|
||||||
"preload": "Pré-carregar a visualização ao vivo em segundo plano",
|
"preload": "Pré-carregar a visualização ao vivo em segundo plano",
|
||||||
"show_image_during_load": "Mostrar imagem estática enquanto a transmissão ao vivo está carregando",
|
"show_image_during_load": "Mostrar imagem estática enquanto a transmissão ao vivo está carregando",
|
||||||
"transition_effect": "Efeito de transição de câmera ao vivo"
|
"transition_effect": "Efeito de transição de câmera ao vivo",
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"media_viewer": {
|
"media_viewer": {
|
||||||
"auto_mute": "Silenciar mídia automaticamente",
|
"auto_mute": "Silenciar mídia automaticamente",
|
||||||
@@ -243,7 +245,8 @@
|
|||||||
"transition_effects": {
|
"transition_effects": {
|
||||||
"none": "Sem transição",
|
"none": "Sem transição",
|
||||||
"slide": "Transição de slides"
|
"slide": "Transição de slides"
|
||||||
}
|
},
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"alignment": "Alinhamento do menu",
|
"alignment": "Alinhamento do menu",
|
||||||
|
|||||||
@@ -197,7 +197,8 @@
|
|||||||
"url": "Imagem arbitrária especificada por URL"
|
"url": "Imagem arbitrária especificada por URL"
|
||||||
},
|
},
|
||||||
"refresh_seconds": "Número de segundos após o qual atualizar (0 = nunca)",
|
"refresh_seconds": "Número de segundos após o qual atualizar (0 = nunca)",
|
||||||
"url": "Imagem arbitrária especificada por URL"
|
"url": "Imagem arbitrária especificada por URL",
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"live": {
|
"live": {
|
||||||
"auto_mute": "Silenciar câmeras ao vivo automaticamente",
|
"auto_mute": "Silenciar câmeras ao vivo automaticamente",
|
||||||
@@ -213,7 +214,8 @@
|
|||||||
"lazy_unload": "As câmeras ao vivo são descarregadas preguiçosamente",
|
"lazy_unload": "As câmeras ao vivo são descarregadas preguiçosamente",
|
||||||
"preload": "Pré-carregar a visualização ao vivo em segundo plano",
|
"preload": "Pré-carregar a visualização ao vivo em segundo plano",
|
||||||
"show_image_during_load": "Mostar imagem durante o carregamento",
|
"show_image_during_load": "Mostar imagem durante o carregamento",
|
||||||
"transition_effect": "Efeito de transição de câmera ao vivo"
|
"transition_effect": "Efeito de transição de câmera ao vivo",
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"media_viewer": {
|
"media_viewer": {
|
||||||
"auto_mute": "Silenciar mídia automaticamente",
|
"auto_mute": "Silenciar mídia automaticamente",
|
||||||
@@ -230,7 +232,8 @@
|
|||||||
"transition_effects": {
|
"transition_effects": {
|
||||||
"none": "Sem transição",
|
"none": "Sem transição",
|
||||||
"slide": "Transição de slides"
|
"slide": "Transição de slides"
|
||||||
}
|
},
|
||||||
|
"zoomable": ""
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"alignment": "Alinhamento do menu",
|
"alignment": "Alinhamento do menu",
|
||||||
|
|||||||
@@ -783,12 +783,14 @@ const viewConfigSchema = z
|
|||||||
const IMAGE_MODES = ['screensaver', 'camera', 'url'] as const;
|
const IMAGE_MODES = ['screensaver', 'camera', 'url'] as const;
|
||||||
const imageConfigDefault = {
|
const imageConfigDefault = {
|
||||||
mode: 'url' as const,
|
mode: 'url' as const,
|
||||||
|
zoomable: true,
|
||||||
...imageBaseConfigDefault,
|
...imageBaseConfigDefault,
|
||||||
};
|
};
|
||||||
const imageConfigSchema = imageBaseConfigSchema
|
const imageConfigSchema = imageBaseConfigSchema
|
||||||
.extend({
|
.extend({
|
||||||
mode: z.enum(IMAGE_MODES).default(imageConfigDefault.mode),
|
mode: z.enum(IMAGE_MODES).default(imageConfigDefault.mode),
|
||||||
layout: mediaLayoutConfigSchema.optional(),
|
layout: mediaLayoutConfigSchema.optional(),
|
||||||
|
zoomable: z.boolean().default(imageConfigDefault.zoomable),
|
||||||
})
|
})
|
||||||
.merge(actionsSchema)
|
.merge(actionsSchema)
|
||||||
.default(imageConfigDefault);
|
.default(imageConfigDefault);
|
||||||
@@ -936,6 +938,7 @@ const liveConfigDefault = {
|
|||||||
lazy_load: true,
|
lazy_load: true,
|
||||||
lazy_unload: 'never' as const,
|
lazy_unload: 'never' as const,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
|
zoomable: true,
|
||||||
transition_effect: 'slide' as const,
|
transition_effect: 'slide' as const,
|
||||||
show_image_during_load: true,
|
show_image_during_load: true,
|
||||||
controls: {
|
controls: {
|
||||||
@@ -997,6 +1000,7 @@ const liveOverridableConfigSchema = z
|
|||||||
.default(liveConfigDefault.show_image_during_load),
|
.default(liveConfigDefault.show_image_during_load),
|
||||||
layout: mediaLayoutConfigSchema.optional(),
|
layout: mediaLayoutConfigSchema.optional(),
|
||||||
microphone: microphoneConfigSchema.default(liveConfigDefault.microphone),
|
microphone: microphoneConfigSchema.default(liveConfigDefault.microphone),
|
||||||
|
zoomable: z.boolean().default(liveConfigDefault.zoomable),
|
||||||
})
|
})
|
||||||
.merge(actionsSchema);
|
.merge(actionsSchema);
|
||||||
|
|
||||||
@@ -1115,6 +1119,7 @@ const viewerConfigDefault = {
|
|||||||
auto_unmute: 'never' as const,
|
auto_unmute: 'never' as const,
|
||||||
lazy_load: true,
|
lazy_load: true,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
|
zoomable: true,
|
||||||
transition_effect: 'slide' as const,
|
transition_effect: 'slide' as const,
|
||||||
snapshot_click_plays_clip: true,
|
snapshot_click_plays_clip: true,
|
||||||
controls: {
|
controls: {
|
||||||
@@ -1155,6 +1160,7 @@ const viewerConfigSchema = z
|
|||||||
.default(viewerConfigDefault.auto_unmute),
|
.default(viewerConfigDefault.auto_unmute),
|
||||||
lazy_load: z.boolean().default(viewerConfigDefault.lazy_load),
|
lazy_load: z.boolean().default(viewerConfigDefault.lazy_load),
|
||||||
draggable: z.boolean().default(viewerConfigDefault.draggable),
|
draggable: z.boolean().default(viewerConfigDefault.draggable),
|
||||||
|
zoomable: z.boolean().default(viewerConfigDefault.zoomable),
|
||||||
transition_effect: transitionEffectConfigSchema.default(
|
transition_effect: transitionEffectConfigSchema.default(
|
||||||
viewerConfigDefault.transition_effect,
|
viewerConfigDefault.transition_effect,
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user