Rework the error message when entity is unavailable.

This commit is contained in:
Dermot Duffy
2022-07-24 19:36:32 -07:00
parent 53c262c056
commit d6f0282886
6 changed files with 148 additions and 60 deletions
+15 -1
View File
@@ -1,3 +1,17 @@
# Review comments generated by i18n-ally. Please commit this file. # Review comments generated by i18n-ally. Please commit this file.
{} reviews:
error.live_camera_not_found:
locales:
pt-BR:
translation_candidate:
source: en
text: A camera_entity configurada não foi encontrada
time: '2022-07-24T05:32:49.977Z'
error.live_camera_unavailable:
locales:
pt-BR:
translation_candidate:
source: en
text: Câmera indisponível
time: '2022-07-24T05:34:22.088Z'
-1
View File
@@ -1904,7 +1904,6 @@ export class FrigateCard extends LitElement {
.conditionState=${this._conditionState} .conditionState=${this._conditionState}
.liveOverrides=${getOverridesByKey(this._getConfig().overrides, 'live')} .liveOverrides=${getOverridesByKey(this._getConfig().overrides, 'live')}
.cameras=${this._cameras} .cameras=${this._cameras}
.preloaded=${this._getConfig().live.preload && !this._view.is('live')}
class="${classMap(liveClasses)}" class="${classMap(liveClasses)}"
> >
</frigate-card-live> </frigate-card-live>
+123 -54
View File
@@ -14,10 +14,12 @@ import {
import { customElement, property, state } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import { createRef, Ref, ref } from 'lit/directives/ref.js'; import { createRef, Ref, ref } from 'lit/directives/ref.js';
import { guard } from 'lit/directives/guard.js'; import { guard } from 'lit/directives/guard.js';
import { keyed } from 'lit/directives/keyed.js';
import { until } from 'lit/directives/until.js'; import { until } from 'lit/directives/until.js';
import { ConditionState, getOverriddenConfig } from '../card-condition.js'; import { ConditionState, getOverriddenConfig } from '../card-condition.js';
import { import {
dispatchFrigateCardErrorEvent, dispatchFrigateCardErrorEvent,
dispatchMessageEvent,
renderProgressIndicator, renderProgressIndicator,
} from '../components/message.js'; } from '../components/message.js';
import { localize } from '../localize/localize.js'; import { localize } from '../localize/localize.js';
@@ -37,6 +39,7 @@ import {
LiveOverrides, LiveOverrides,
LiveProvider, LiveProvider,
MediaShowInfo, MediaShowInfo,
Message,
TransitionEffect, TransitionEffect,
WebRTCCardConfig, WebRTCCardConfig,
} from '../types.js'; } from '../types.js';
@@ -89,33 +92,66 @@ export class FrigateCardLive extends LitElement {
@property({ attribute: false, hasChanged: contentsChanged }) @property({ attribute: false, hasChanged: contentsChanged })
public liveOverrides?: LiveOverrides; public liveOverrides?: LiveOverrides;
set preloaded(preloaded: boolean) { // Whether or not the live view is currently in the background (i.e. preloaded
this._preloaded = preloaded; // but not visible)
@state()
protected _inBackground?: boolean = true;
if (!preloaded && this._savedMediaShowInfo) { // Intersection handler is used to detect when the live view flips between
// foreground and background (in preload mode).
protected _intersectionObserver: IntersectionObserver;
// MediaShowInfo object and message from the underlying live object. In the
// case of pre-loading these may be propagated upwards later.
protected _savedMediaShowInfo: MediaShowInfo | null = null;
protected _messageReceivedPostRender = false;
protected _renderKey = 0;
constructor() {
super();
this._intersectionObserver = new IntersectionObserver(
this._intersectionHandler.bind(this),
);
}
/**
* Called when the live view intersects with the viewport.
* @param entries The IntersectionObserverEntry entries (should be only 1).
*/
protected _intersectionHandler(entries: IntersectionObserverEntry[]): void {
this._inBackground = entries.every((entry) => !entry.isIntersecting);
if (
!this._inBackground &&
!this._messageReceivedPostRender &&
this._savedMediaShowInfo
) {
// If this isn't being rendered in the background, the last render did not
// generate a message and there's a saved MediaInfo, dispatch it upwards.
dispatchExistingMediaShowInfoAsEvent(this, this._savedMediaShowInfo); dispatchExistingMediaShowInfoAsEvent(this, this._savedMediaShowInfo);
} }
// Trigger a re-render which may be necessary if the prior render resulted
// in a message.
if (this._messageReceivedPostRender && !this._inBackground) {
this.requestUpdate();
}
} }
// Whether or not the live view is currently being preloaded. /**
@state() * Component connected callback.
protected _preloaded?: boolean; */
connectedCallback(): void {
// MediaShowInfo object from the underlying live object. In the case of this._intersectionObserver.observe(this);
// pre-loading it may be propagated upwards later. super.connectedCallback();
protected _savedMediaShowInfo?: MediaShowInfo; }
/** /**
* Handler for media show events that special cases preloaded live views. * Component disconnected callback.
* @param e The media show event.
*/ */
protected _mediaShowHandler(e: CustomEvent<MediaShowInfo>): void { disconnectedCallback(): void {
this._savedMediaShowInfo = e.detail; super.disconnectedCallback();
if (this._preloaded) { this._intersectionObserver.disconnect();
// If live is being pre-loaded, don't let the event propagate upwards yet
// as the media is not really being shown.
e.stopPropagation();
}
} }
/** /**
@@ -148,37 +184,55 @@ export class FrigateCardLive extends LitElement {
// independently override the liveConfig to reflect the camera in the // independently override the liveConfig to reflect the camera in the
// carousel (not necessarily the selected camera). // carousel (not necessarily the selected camera).
// - Fetching of thumbnails is disabled as long as live view is the // - Fetching of thumbnails is disabled as long as live view is the
// background (preloaded) rather than foreground (not preloaded). // background.
return html` <frigate-card-surround-thumbnails // - Various events are captured to prevent them propagating upwards if the
.hass=${this.hass} // card is in the background.
.view=${this.view} // - The entire returned template is keyed to allow for the whole template
.config=${config.controls.thumbnails} // to be re-rendered in certain circumstances (specifically: if a message
.browseMediaParams=${browseMediaParams ?? undefined} // is received when the card is in the background).
.cameras=${this.cameras} const result = html`${keyed(
?fetch=${!this._preloaded} this._renderKey,
> html`<frigate-card-surround-thumbnails
<frigate-card-live-carousel
.hass=${this.hass} .hass=${this.hass}
.view=${this.view} .view=${this.view}
.config=${config.controls.thumbnails}
.browseMediaParams=${browseMediaParams ?? undefined}
.cameras=${this.cameras} .cameras=${this.cameras}
.liveConfig=${this.liveConfig} ?fetch=${!this._inBackground}
.preloaded=${this._preloaded} @frigate-card:message=${(ev: CustomEvent<Message>) => {
.conditionState=${this.conditionState} this._renderKey++;
.liveOverrides=${this.liveOverrides} this._messageReceivedPostRender = true;
@frigate-card:media-show=${this._mediaShowHandler} if (this._inBackground) {
@frigate-card:change-view=${(ev: CustomEvent) => { ev.stopPropagation();
if (this._preloaded) { }
// Don't allow change-view events to propagate upwards if the card }}
// is only preloaded rather than being live displayed. These events @frigate-card:media-show=${(ev: CustomEvent<MediaShowInfo>) => {
// could be triggered if the camera is switched and the carousel this._savedMediaShowInfo = ev.detail;
// moves to focus on that camera -- as the card isn't actually being if (this._inBackground) {
// displayed, do not allow the view to actually be updated. ev.stopPropagation();
}
}}
@frigate-card:change-view=${(ev: CustomEvent<View>) => {
if (this._inBackground) {
ev.stopPropagation(); ev.stopPropagation();
} }
}} }}
> >
</frigate-card-live-carousel> <frigate-card-live-carousel
</frigate-card-surround-thumbnails>`; .hass=${this.hass}
.view=${this.view}
.cameras=${this.cameras}
.liveConfig=${this.liveConfig}
.inBackground=${this._inBackground}
.conditionState=${this.conditionState}
.liveOverrides=${this.liveOverrides}
>
</frigate-card-live-carousel>
</frigate-card-surround-thumbnails>`,
)}`;
this._messageReceivedPostRender = false;
return result;
} }
/** /**
@@ -207,7 +261,7 @@ export class FrigateCardLiveCarousel extends LitElement {
public liveOverrides?: LiveOverrides; public liveOverrides?: LiveOverrides;
@property({ attribute: false }) @property({ attribute: false })
public preloaded?: boolean; public inBackground?: boolean;
@property({ attribute: false }) @property({ attribute: false })
public conditionState?: ConditionState; public conditionState?: ConditionState;
@@ -235,7 +289,10 @@ export class FrigateCardLiveCarousel extends LitElement {
this.view?.camera != oldView.camera this.view?.camera != oldView.camera
) { ) {
const slide: number | undefined = this._cameraToSlide[this.view.camera]; const slide: number | undefined = this._cameraToSlide[this.view.camera];
if (slide !== undefined && slide !== frigateCardCarousel.getCarouselSelected()?.index) { if (
slide !== undefined &&
slide !== frigateCardCarousel.getCarouselSelected()?.index
) {
frigateCardCarousel.carouselScrollTo(slide); frigateCardCarousel.carouselScrollTo(slide);
} }
} }
@@ -244,11 +301,11 @@ export class FrigateCardLiveCarousel extends LitElement {
if ( if (
frigateCardMediaCarousel && frigateCardMediaCarousel &&
frigateCardCarousel && frigateCardCarousel &&
changedProperties.has('preloaded') changedProperties.has('inBackground')
) { ) {
// If this has changed to preloaded (i.e. is now loaded but in the // If this has changed to be in the background (i.e. preloaded but not
// background) take the appropriate play/pause/mute/unmute actions. // visible) take the appropriate play/pause/mute/unmute actions.
if (this.preloaded) { if (this.inBackground) {
frigateCardMediaCarousel.autoPause(); frigateCardMediaCarousel.autoPause();
frigateCardMediaCarousel.autoMute(); frigateCardMediaCarousel.autoMute();
} else { } else {
@@ -369,8 +426,7 @@ export class FrigateCardLiveCarousel extends LitElement {
protected _setViewHandler(): void { protected _setViewHandler(): void {
const selectedCameraIndex = this._refMediaCarousel.value const selectedCameraIndex = this._refMediaCarousel.value
?.frigateCardCarousel() ?.frigateCardCarousel()
?.getCarouselSelected() ?.getCarouselSelected()?.index;
?.index;
if (selectedCameraIndex === undefined || !this.view || !this.cameras) { if (selectedCameraIndex === undefined || !this.view || !this.cameras) {
return; return;
} }
@@ -514,7 +570,7 @@ export class FrigateCardLiveCarousel extends LitElement {
.titlePopupConfig=${config.controls.title} .titlePopupConfig=${config.controls.title}
transitionEffect=${this._getTransitionEffect()} transitionEffect=${this._getTransitionEffect()}
@frigate-card:carousel:settle=${this._setViewHandler.bind(this)} @frigate-card:carousel:settle=${this._setViewHandler.bind(this)}
> >
<frigate-card-next-previous-control <frigate-card-next-previous-control
slot="previous" slot="previous"
.direction=${'previous'} .direction=${'previous'}
@@ -735,12 +791,25 @@ export class FrigateCardLiveFrigate extends LitElement {
} }
const stateObj = this.hass.states[this.cameraConfig.camera_entity]; const stateObj = this.hass.states[this.cameraConfig.camera_entity];
if (!stateObj || stateObj.state === 'unavailable') { if (!stateObj) {
return dispatchErrorMessageEvent(this, localize('error.live_camera_unavailable'), { return dispatchErrorMessageEvent(this, localize('error.live_camera_not_found'), {
context: this.cameraConfig, context: this.cameraConfig,
}); });
} }
if (stateObj.state === 'unavailable') {
// Don't treat state unavailability as an error per se.
return dispatchMessageEvent(
this,
localize('error.live_camera_unavailable'),
'info',
{
icon: 'mdi:connection',
context: getCameraTitle(this.hass, this.cameraConfig),
},
);
}
return html` <frigate-card-ha-camera-stream return html` <frigate-card-ha-camera-stream
${ref(this._playerRef)} ${ref(this._playerRef)}
.hass=${this.hass} .hass=${this.hass}
+6 -2
View File
@@ -34,9 +34,13 @@ export class FrigateCardMessage extends LitElement {
</div> </div>
<div class="contents"> <div class="contents">
<span class="${classMap(classes)}"> <span class="${classMap(classes)}">
${this.message ? html`${this.message}` : ''} ${this.message
? html`${this.message}${this.context && typeof this.context === 'string'
? ': ' + this.context
: ''}`
: ''}
</span> </span>
${this.context ${this.context && typeof this.context !== 'string'
? html`<pre>${JSON.stringify(this.context, null, 2)}</pre>` ? html`<pre>${JSON.stringify(this.context, null, 2)}</pre>`
: ''} : ''}
</div> </div>
+2 -1
View File
@@ -328,7 +328,8 @@
"invalid_response": "Received invalid response from Home Assistant for request", "invalid_response": "Received invalid response from Home Assistant for request",
"jsmpeg_no_player": "Could not start JSMPEG player", "jsmpeg_no_player": "Could not start JSMPEG player",
"jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path", "jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path",
"live_camera_unavailable": "The configured camera_entity is unavailable", "live_camera_not_found": "The configured camera_entity was not found",
"live_camera_unavailable": "Camera unavailable",
"no_camera_id": "Could not determine camera id for the following camera, may need to set 'id' parameter manually", "no_camera_id": "Could not determine camera id for the following camera, may need to set 'id' parameter manually",
"no_camera_name": "Could not determine a Frigate camera name for camera (or one of its dependents), please specify either 'camera_entity' or 'camera_name'", "no_camera_name": "Could not determine a Frigate camera name for camera (or one of its dependents), please specify either 'camera_entity' or 'camera_name'",
"no_cameras": "No valid cameras found, you must configure at least one camera entry", "no_cameras": "No valid cameras found, you must configure at least one camera entry",
+2 -1
View File
@@ -328,7 +328,8 @@
"invalid_response": "Resposta inválida recebida do Home Assistant para a solicitação", "invalid_response": "Resposta inválida recebida do Home Assistant para a solicitação",
"jsmpeg_no_player": "Não foi possível iniciar o player JSMPEG", "jsmpeg_no_player": "Não foi possível iniciar o player JSMPEG",
"jsmpeg_no_sign": "Não foi possível recuperar ou assinar o caminho do websocket JSMPEG", "jsmpeg_no_sign": "Não foi possível recuperar ou assinar o caminho do websocket JSMPEG",
"live_camera_unavailable": "camera_entity configurada não está disponível", "live_camera_not_found": "",
"live_camera_unavailable": "",
"no_camera_id": "Não foi possível determinar o ID da câmera para a câmera a seguir, pode ser necessário definir o parâmetro 'id' manualmente", "no_camera_id": "Não foi possível determinar o ID da câmera para a câmera a seguir, pode ser necessário definir o parâmetro 'id' manualmente",
"no_camera_name": "Não foi possível determinar o nome da câmera da Frigate, especifique 'camera_entity' ou 'camera_name' para a câmera a seguir", "no_camera_name": "Não foi possível determinar o nome da câmera da Frigate, especifique 'camera_entity' ou 'camera_name' para a câmera a seguir",
"no_cameras": "Nenhuma câmera válida encontrada, você deve configurar pelo menos uma câmera", "no_cameras": "Nenhuma câmera válida encontrada, você deve configurar pelo menos uma câmera",