Rework the error message when entity is unavailable.
This commit is contained in:
Vendored
+15
-1
@@ -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'
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
+116
-47
@@ -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)
|
||||||
|
|
||||||
if (!preloaded && this._savedMediaShowInfo) {
|
|
||||||
dispatchExistingMediaShowInfoAsEvent(this, this._savedMediaShowInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whether or not the live view is currently being preloaded.
|
|
||||||
@state()
|
@state()
|
||||||
protected _preloaded?: boolean;
|
protected _inBackground?: boolean = true;
|
||||||
|
|
||||||
// MediaShowInfo object from the underlying live object. In the case of
|
// Intersection handler is used to detect when the live view flips between
|
||||||
// pre-loading it may be propagated upwards later.
|
// foreground and background (in preload mode).
|
||||||
protected _savedMediaShowInfo?: MediaShowInfo;
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for media show events that special cases preloaded live views.
|
* Called when the live view intersects with the viewport.
|
||||||
* @param e The media show event.
|
* @param entries The IntersectionObserverEntry entries (should be only 1).
|
||||||
*/
|
*/
|
||||||
protected _mediaShowHandler(e: CustomEvent<MediaShowInfo>): void {
|
protected _intersectionHandler(entries: IntersectionObserverEntry[]): void {
|
||||||
this._savedMediaShowInfo = e.detail;
|
this._inBackground = entries.every((entry) => !entry.isIntersecting);
|
||||||
if (this._preloaded) {
|
|
||||||
// If live is being pre-loaded, don't let the event propagate upwards yet
|
if (
|
||||||
// as the media is not really being shown.
|
!this._inBackground &&
|
||||||
e.stopPropagation();
|
!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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trigger a re-render which may be necessary if the prior render resulted
|
||||||
|
// in a message.
|
||||||
|
if (this._messageReceivedPostRender && !this._inBackground) {
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component connected callback.
|
||||||
|
*/
|
||||||
|
connectedCallback(): void {
|
||||||
|
this._intersectionObserver.observe(this);
|
||||||
|
super.connectedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component disconnected callback.
|
||||||
|
*/
|
||||||
|
disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this._intersectionObserver.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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
|
||||||
|
// card is in the background.
|
||||||
|
// - The entire returned template is keyed to allow for the whole template
|
||||||
|
// to be re-rendered in certain circumstances (specifically: if a message
|
||||||
|
// is received when the card is in the background).
|
||||||
|
const result = html`${keyed(
|
||||||
|
this._renderKey,
|
||||||
|
html`<frigate-card-surround-thumbnails
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.view=${this.view}
|
.view=${this.view}
|
||||||
.config=${config.controls.thumbnails}
|
.config=${config.controls.thumbnails}
|
||||||
.browseMediaParams=${browseMediaParams ?? undefined}
|
.browseMediaParams=${browseMediaParams ?? undefined}
|
||||||
.cameras=${this.cameras}
|
.cameras=${this.cameras}
|
||||||
?fetch=${!this._preloaded}
|
?fetch=${!this._inBackground}
|
||||||
|
@frigate-card:message=${(ev: CustomEvent<Message>) => {
|
||||||
|
this._renderKey++;
|
||||||
|
this._messageReceivedPostRender = true;
|
||||||
|
if (this._inBackground) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
@frigate-card:media-show=${(ev: CustomEvent<MediaShowInfo>) => {
|
||||||
|
this._savedMediaShowInfo = ev.detail;
|
||||||
|
if (this._inBackground) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
@frigate-card:change-view=${(ev: CustomEvent<View>) => {
|
||||||
|
if (this._inBackground) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<frigate-card-live-carousel
|
<frigate-card-live-carousel
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.view=${this.view}
|
.view=${this.view}
|
||||||
.cameras=${this.cameras}
|
.cameras=${this.cameras}
|
||||||
.liveConfig=${this.liveConfig}
|
.liveConfig=${this.liveConfig}
|
||||||
.preloaded=${this._preloaded}
|
.inBackground=${this._inBackground}
|
||||||
.conditionState=${this.conditionState}
|
.conditionState=${this.conditionState}
|
||||||
.liveOverrides=${this.liveOverrides}
|
.liveOverrides=${this.liveOverrides}
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
|
||||||
@frigate-card:change-view=${(ev: CustomEvent) => {
|
|
||||||
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
|
|
||||||
// could be triggered if the camera is switched and the carousel
|
|
||||||
// moves to focus on that camera -- as the card isn't actually being
|
|
||||||
// displayed, do not allow the view to actually be updated.
|
|
||||||
ev.stopPropagation();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
</frigate-card-live-carousel>
|
</frigate-card-live-carousel>
|
||||||
</frigate-card-surround-thumbnails>`;
|
</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;
|
||||||
}
|
}
|
||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user