Allow per-camera trigger borders.
This commit is contained in:
@@ -433,7 +433,7 @@ Scan mode tracks Home Assistant state *changes* -- when the card is first starte
|
|||||||
| - | - | - | - |
|
| - | - | - | - |
|
||||||
| `enabled` | `false` | :white_check_mark: | Whether to enable scan mode. |
|
| `enabled` | `false` | :white_check_mark: | Whether to enable scan mode. |
|
||||||
| `filter_selected_camera` | `false` | :white_check_mark: | If set to `true` will only trigger on the currently selected camera.|
|
| `filter_selected_camera` | `false` | :white_check_mark: | If set to `true` will only trigger on the currently selected camera.|
|
||||||
| `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 `live` view should show a visual indication that it is triggered (a pulsing border around the camera edge). |
|
||||||
| `untrigger_seconds` | `0` | :white_check_mark: | The number of seconds to wait after all entities are inactive before untriggering. |
|
| `untrigger_seconds` | `0` | :white_check_mark: | The number of seconds to wait after all entities are inactive before untriggering. |
|
||||||
| `actions` | | :white_check_mark: | The actions to take when scan mode triggers (see below). |
|
| `actions` | | :white_check_mark: | The actions to take when scan mode triggers (see below). |
|
||||||
|
|
||||||
|
|||||||
+3
-6
@@ -238,11 +238,6 @@ class FrigateCard extends LitElement {
|
|||||||
const cardStyle = {
|
const cardStyle = {
|
||||||
'aspect-ratio': this._controller.getStyleManager().getAspectRatioStyle(),
|
'aspect-ratio': this._controller.getStyleManager().getAspectRatioStyle(),
|
||||||
};
|
};
|
||||||
const cardClasses = {
|
|
||||||
triggered:
|
|
||||||
!!this._controller.getTriggersManager().isTriggered() &&
|
|
||||||
!!this._config?.view.scan.show_trigger_status,
|
|
||||||
};
|
|
||||||
const mainClasses = {
|
const mainClasses = {
|
||||||
main: true,
|
main: true,
|
||||||
'curve-top':
|
'curve-top':
|
||||||
@@ -264,7 +259,6 @@ class FrigateCard extends LitElement {
|
|||||||
hasHold: frigateCardHasAction(actions.hold_action),
|
hasHold: frigateCardHasAction(actions.hold_action),
|
||||||
hasDoubleClick: frigateCardHasAction(actions.double_tap_action),
|
hasDoubleClick: frigateCardHasAction(actions.double_tap_action),
|
||||||
})}
|
})}
|
||||||
class="${classMap(cardClasses)}"
|
|
||||||
style="${styleMap(cardStyle)}"
|
style="${styleMap(cardStyle)}"
|
||||||
@frigate-card:message=${(ev: CustomEvent<Message>) =>
|
@frigate-card:message=${(ev: CustomEvent<Message>) =>
|
||||||
this._controller.getMessageManager().setMessageIfHigherPriority(ev.detail)}
|
this._controller.getMessageManager().setMessageIfHigherPriority(ev.detail)}
|
||||||
@@ -313,6 +307,9 @@ class FrigateCard extends LitElement {
|
|||||||
?.getEpoch()}
|
?.getEpoch()}
|
||||||
.hide=${!!this._controller.getMessageManager().hasMessage()}
|
.hide=${!!this._controller.getMessageManager().hasMessage()}
|
||||||
.microphoneStream=${this._controller.getMicrophoneManager()?.getStream()}
|
.microphoneStream=${this._controller.getMicrophoneManager()?.getStream()}
|
||||||
|
.triggeredCameraIDs=${this._config?.view.scan.show_trigger_status
|
||||||
|
? this._controller.getTriggersManager().getTriggeredCameraIDs()
|
||||||
|
: undefined}
|
||||||
></frigate-card-views>`}
|
></frigate-card-views>`}
|
||||||
${
|
${
|
||||||
// Keep message rendering to last to show messages that may have been
|
// Keep message rendering to last to show messages that may have been
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
import { localize } from '../../localize/localize.js';
|
import { localize } from '../../localize/localize.js';
|
||||||
import basicBlockStyle from '../../scss/basic-block.scss';
|
import basicBlockStyle from '../../scss/basic-block.scss';
|
||||||
import liveCarouselStyle from '../../scss/live-carousel.scss';
|
import liveCarouselStyle from '../../scss/live-carousel.scss';
|
||||||
|
import liveGridStyle from '../../scss/live-grid.scss';
|
||||||
import liveProviderStyle from '../../scss/live-provider.scss';
|
import liveProviderStyle from '../../scss/live-provider.scss';
|
||||||
import {
|
import {
|
||||||
ExtendedHomeAssistant,
|
ExtendedHomeAssistant,
|
||||||
@@ -114,6 +115,9 @@ export class FrigateCardLive extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public microphoneStream?: MediaStream;
|
public microphoneStream?: MediaStream;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public triggeredCameraIDs?: Set<string>;
|
||||||
|
|
||||||
// Whether or not the live view is currently in the background (i.e. preloaded
|
// Whether or not the live view is currently in the background (i.e. preloaded
|
||||||
// but not visible)
|
// but not visible)
|
||||||
@state()
|
@state()
|
||||||
@@ -219,6 +223,7 @@ export class FrigateCardLive extends LitElement {
|
|||||||
.cardWideConfig=${this.cardWideConfig}
|
.cardWideConfig=${this.cardWideConfig}
|
||||||
.cameraManager=${this.cameraManager}
|
.cameraManager=${this.cameraManager}
|
||||||
.microphoneStream=${this.microphoneStream}
|
.microphoneStream=${this.microphoneStream}
|
||||||
|
.triggeredCameraIDs=${this.triggeredCameraIDs}
|
||||||
@frigate-card:message=${(ev: CustomEvent<Message>) => {
|
@frigate-card:message=${(ev: CustomEvent<Message>) => {
|
||||||
this._renderKey++;
|
this._renderKey++;
|
||||||
this._messageReceivedPostRender = true;
|
this._messageReceivedPostRender = true;
|
||||||
@@ -283,7 +288,12 @@ export class FrigateCardLiveGrid extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public microphoneStream?: MediaStream;
|
public microphoneStream?: MediaStream;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public triggeredCameraIDs?: Set<string>;
|
||||||
|
|
||||||
protected _renderCarousel(cameraID?: string): TemplateResult {
|
protected _renderCarousel(cameraID?: string): TemplateResult {
|
||||||
|
const triggeredCameraID = cameraID ?? this.view?.camera;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<frigate-card-live-carousel
|
<frigate-card-live-carousel
|
||||||
grid-id=${ifDefined(cameraID)}
|
grid-id=${ifDefined(cameraID)}
|
||||||
@@ -297,6 +307,8 @@ export class FrigateCardLiveGrid extends LitElement {
|
|||||||
.cardWideConfig=${this.cardWideConfig}
|
.cardWideConfig=${this.cardWideConfig}
|
||||||
.cameraManager=${this.cameraManager}
|
.cameraManager=${this.cameraManager}
|
||||||
.microphoneStream=${this.microphoneStream}
|
.microphoneStream=${this.microphoneStream}
|
||||||
|
?triggered=${triggeredCameraID &&
|
||||||
|
!!this.triggeredCameraIDs?.has(triggeredCameraID)}
|
||||||
>
|
>
|
||||||
</frigate-card-live-carousel>
|
</frigate-card-live-carousel>
|
||||||
`;
|
`;
|
||||||
@@ -351,7 +363,7 @@ export class FrigateCardLiveGrid extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return unsafeCSS(basicBlockStyle);
|
return unsafeCSS(liveGridStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export class FrigateCardThumbnailCarousel extends LitElement {
|
|||||||
public fadeThumbnails = false;
|
public fadeThumbnails = false;
|
||||||
|
|
||||||
protected _thumbnailSlides: TemplateResult[] = [];
|
protected _thumbnailSlides: TemplateResult[] = [];
|
||||||
|
protected _plugins = [AutoSize()];
|
||||||
|
|
||||||
protected willUpdate(changedProps: PropertyValues): void {
|
protected willUpdate(changedProps: PropertyValues): void {
|
||||||
if (changedProps.has('config')) {
|
if (changedProps.has('config')) {
|
||||||
@@ -137,7 +138,7 @@ export class FrigateCardThumbnailCarousel extends LitElement {
|
|||||||
|
|
||||||
return html`<frigate-card-carousel
|
return html`<frigate-card-carousel
|
||||||
direction=${direction}
|
direction=${direction}
|
||||||
.plugins=${[AutoSize()]}
|
.plugins=${this._plugins}
|
||||||
.selected=${this._getSelectedSlide() ?? 0}
|
.selected=${this._getSelectedSlide() ?? 0}
|
||||||
.dragFree=${true}
|
.dragFree=${true}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ export class FrigateCardViews extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public microphoneStream?: MediaStream;
|
public microphoneStream?: MediaStream;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public triggeredCameraIDs?: Set<string>;
|
||||||
|
|
||||||
protected willUpdate(changedProps: PropertyValues): void {
|
protected willUpdate(changedProps: PropertyValues): void {
|
||||||
if (changedProps.has('view') || changedProps.has('config')) {
|
if (changedProps.has('view') || changedProps.has('config')) {
|
||||||
if (this.view?.is('live') || this._shouldLivePreload()) {
|
if (this.view?.is('live') || this._shouldLivePreload()) {
|
||||||
@@ -239,6 +242,7 @@ export class FrigateCardViews extends LitElement {
|
|||||||
.cameraManager=${this.cameraManager}
|
.cameraManager=${this.cameraManager}
|
||||||
.cardWideConfig=${this.cardWideConfig}
|
.cardWideConfig=${this.cardWideConfig}
|
||||||
.microphoneStream=${this.microphoneStream}
|
.microphoneStream=${this.microphoneStream}
|
||||||
|
.triggeredCameraIDs=${this.triggeredCameraIDs}
|
||||||
class="${classMap(liveClasses)}"
|
class="${classMap(liveClasses)}"
|
||||||
>
|
>
|
||||||
</frigate-card-live>
|
</frigate-card-live>
|
||||||
|
|||||||
@@ -52,10 +52,6 @@ div.main {
|
|||||||
// Necessary to get Safari to show border-radius correctly.
|
// Necessary to get Safari to show border-radius correctly.
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
|
|
||||||
// Include the borders in the sizing (to keep the triggered warning pulse
|
|
||||||
// visible in fullscreen).
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
// Hide scrollbar: Firefox
|
// Hide scrollbar: Firefox
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
// Hide scrollbar: IE and Edge
|
// Hide scrollbar: IE and Edge
|
||||||
@@ -112,20 +108,6 @@ ha-card {
|
|||||||
position: static;
|
position: static;
|
||||||
color: var(--secondary-text-color, white);
|
color: var(--secondary-text-color, white);
|
||||||
}
|
}
|
||||||
ha-card.triggered {
|
|
||||||
@keyframes warning-pulse {
|
|
||||||
0% {
|
|
||||||
border: solid 2px rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
border: solid 2px var(--warning-color);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
border: solid 2px rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
animation: warning-pulse 5s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/************
|
/************
|
||||||
* Fullscreen
|
* Fullscreen
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
:host {
|
:host {
|
||||||
|
display: block;
|
||||||
--video-max-height: none;
|
--video-max-height: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
@use 'basic-block.scss';
|
||||||
|
|
||||||
|
@keyframes warning-pulse {
|
||||||
|
0% {
|
||||||
|
border: solid 2px var(--frigate-card-triggered-color-1, rgba(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
border: solid 2px var(--frigate-card-triggered-color-2, var(--warning-color));
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
border: solid 2px var(--frigate-card-triggered-color-1, rgba(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frigate-card-live-carousel[triggered] {
|
||||||
|
animation: warning-pulse 5s infinite;
|
||||||
|
}
|
||||||
|
frigate-card-live-carousel[selected] {
|
||||||
|
--frigate-card-triggered-color-1: var(--primary-color);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user