Fix error condition rendering.

This commit is contained in:
Dermot Duffy
2022-01-14 21:31:16 -08:00
parent c40c63862e
commit 915053261e
5 changed files with 138 additions and 137 deletions
+65 -70
View File
@@ -402,6 +402,7 @@ export class FrigateCard extends LitElement {
*/ */
protected async _loadCameras(): Promise<void> { protected async _loadCameras(): Promise<void> {
const cameras: Map<string, CameraConfig> = new Map(); const cameras: Map<string, CameraConfig> = new Map();
let errorFree = true;
const addCameraConfig = async (config: CameraConfig) => { const addCameraConfig = async (config: CameraConfig) => {
if (!config.camera_name && config.camera_entity) { if (!config.camera_name && config.camera_entity) {
@@ -416,13 +417,11 @@ export class FrigateCard extends LitElement {
if (config.camera_name) { if (config.camera_name) {
const id = config.card_id || config.camera_entity || config.camera_name; const id = config.card_id || config.camera_entity || config.camera_name;
if (cameras.has(id)) { if (cameras.has(id)) {
this._setMessageAndUpdate( this._setMessageAndUpdate({
{
message: localize('error.duplicate_frigate_camera_name'), message: localize('error.duplicate_frigate_camera_name'),
type: 'error', type: 'error',
}, });
true, errorFree = false;
);
} else { } else {
cameras.set(id, config); cameras.set(id, config);
} }
@@ -434,17 +433,17 @@ export class FrigateCard extends LitElement {
} }
if (!cameras.size) { if (!cameras.size) {
return this._setMessageAndUpdate( return this._setMessageAndUpdate({
{
message: localize('error.no_cameras'), message: localize('error.no_cameras'),
type: 'error', type: 'error',
}, });
true, errorFree = false;
);
} }
if (errorFree) {
this._cameras = cameras; this._cameras = cameras;
} }
}
/** /**
* Get the camera configuration for the selected camera. * Get the camera configuration for the selected camera.
@@ -602,10 +601,12 @@ export class FrigateCard extends LitElement {
this._changeView(); this._changeView();
} }
protected _changeView(view?: View): void { protected _changeView(args?: { view?: View; resetMessage?: boolean }): void {
if (args?.resetMessage ?? true) {
this._message = null; this._message = null;
}
if (view === undefined) { if (args?.view === undefined) {
let camera = this._view?.camera; let camera = this._view?.camera;
if (!camera && this._cameras?.size) { if (!camera && this._cameras?.size) {
camera = this._cameras.keys().next().value; camera = this._cameras.keys().next().value;
@@ -616,19 +617,20 @@ export class FrigateCard extends LitElement {
view: this.config.view.default, view: this.config.view.default,
camera: camera, camera: camera,
}); });
this._generateConditionState();
} }
} else { } else {
this._view = view; this._view = args.view;
}
this._generateConditionState(); this._generateConditionState();
} }
}
/** /**
* Handle a change view event. * Handle a change view event.
* @param e The change view event. * @param e The change view event.
*/ */
protected _changeViewHandler(e: CustomEvent<View>): void { protected _changeViewHandler(e: CustomEvent<View>): void {
this._changeView(e.detail); this._changeView({ view: e.detail });
} }
/** /**
@@ -762,12 +764,12 @@ export class FrigateCard extends LitElement {
case 'snapshot': case 'snapshot':
case 'snapshots': case 'snapshots':
if (this._view) { if (this._view) {
this._changeView( this._changeView({
new View({ view: new View({
view: action, view: action,
camera: this._view.camera, camera: this._view.camera,
}), }),
); });
} }
break; break;
case 'download': case 'download':
@@ -787,20 +789,14 @@ export class FrigateCard extends LitElement {
case 'camera_select': case 'camera_select':
const camera = frigateCardAction.camera; const camera = frigateCardAction.camera;
if (this._cameras?.has(camera) && this._view) { if (this._cameras?.has(camera) && this._view) {
this._changeView( this._changeView({
new View({ view: new View({
view: this._view.view, view: this._view.view,
camera: camera, camera: camera,
}), }),
); });
} }
break; break;
// case 'next_camera':
// this._changeCamera({ next: true });
// break;
// case 'previous_camera':
// this._changeCamera({ previous: true });
// break;
default: default:
console.warn(`Frigate card received unknown card action: ${action}`); console.warn(`Frigate card received unknown card action: ${action}`);
} }
@@ -1058,6 +1054,11 @@ export class FrigateCard extends LitElement {
absolute: padding != null, absolute: padding != null,
}; };
const pictureElementsClasses = {
'picture-elements': true,
gallery: !!this._view?.isGalleryView(),
};
const actions = this._getMergedActions(); const actions = this._getMergedActions();
return html` <ha-card return html` <ha-card
@@ -1067,20 +1068,55 @@ export class FrigateCard extends LitElement {
})} })}
@action=${(ev: CustomEvent) => this._actionHandler(ev, actions)} @action=${(ev: CustomEvent) => this._actionHandler(ev, actions)}
@ll-custom=${this._cardActionHandler.bind(this)} @ll-custom=${this._cardActionHandler.bind(this)}
@frigate-card:message=${this._messageHandler}
@frigate-card:change-view=${this._changeViewHandler}
@frigate-card:media-show=${this._mediaShowHandler}
@frigate-card:pause=${this._pauseHandler}
@frigate-card:play=${this._playHandler}
> >
${this.config.menu.mode == 'above' ? this._renderMenu() : ''} ${this.config.menu.mode == 'above' ? this._renderMenu() : ''}
<div class="container outer" style="${styleMap(outerStyle)}"> <div class="container outer" style="${styleMap(outerStyle)}">
<div class="${classMap(contentClasses)}"> <div class="${classMap(contentClasses)}">
<div class="${classMap(pictureElementsClasses)}">
${this.config.elements
? // Always show elements to allow for custom menu items (etc.) to
// be present even if a particular view has an error.
html` <frigate-card-elements
.hass=${this._hass}
.elements=${this.config.elements}
.conditionState=${this._conditionState}
@frigate-card:menu-add=${(e) => {
this._addDynamicMenuButton(e.detail);
}}
@frigate-card:menu-remove=${(e) => {
this._removeDynamicMenuButton(e.detail);
}}
@frigate-card:condition-state-request=${(ev) => {
conditionStateRequestHandler(ev, this._conditionState);
}}
>
</frigate-card-elements>`
: ``}
${this._cameras === undefined ${this._cameras === undefined
? until( ? until(
(async () => { (async () => {
await this._loadCameras(); await this._loadCameras();
this._changeView(); // Don't reset messages as errors may have been generated
// during the camera load.
this._changeView({ resetMessage: false });
return this._render(); return this._render();
})(), })(),
renderProgressIndicator(), renderProgressIndicator(),
) )
: this._render()} : // Always want to call render even if there's a message, to
// ensure live preload is always present (even if not displayed).
this._render()}
${
// Keep message rendering to last to show messages that may have
// been generated during the render.
this._message ? renderMessage(this._message) : ''
}
</div>
</div> </div>
</div> </div>
${this.config.menu.mode != 'above' ? this._renderMenu() : ''} ${this.config.menu.mode != 'above' ? this._renderMenu() : ''}
@@ -1097,10 +1133,6 @@ export class FrigateCard extends LitElement {
return html``; return html``;
} }
const pictureElementsClasses = {
'picture-elements': true,
gallery: this._view.isGalleryView(),
};
const galleryClasses = { const galleryClasses = {
hidden: this.config.live.preload && !this._view.isGalleryView(), hidden: this.config.live.preload && !this._view.isGalleryView(),
}; };
@@ -1115,14 +1147,10 @@ export class FrigateCard extends LitElement {
}; };
return html` return html`
<div class="${classMap(pictureElementsClasses)}">
${this._message ? renderMessage(this._message) : ``}
${!this._message && this._view.is('image') ${!this._message && this._view.is('image')
? html` <frigate-card-image ? html` <frigate-card-image
.imageConfig=${this.config.image} .imageConfig=${this.config.image}
class="${classMap(imageClasses)}" class="${classMap(imageClasses)}"
@frigate-card:media-show=${this._mediaShowHandler}
@frigate-card:message=${this._messageHandler}
> >
</frigate-card-image>` </frigate-card-image>`
: ``} : ``}
@@ -1135,8 +1163,6 @@ export class FrigateCard extends LitElement {
cameraConfig, cameraConfig,
)} )}
class="${classMap(galleryClasses)}" class="${classMap(galleryClasses)}"
@frigate-card:change-view=${this._changeViewHandler}
@frigate-card:message=${this._messageHandler}
> >
</frigate-card-gallery>` </frigate-card-gallery>`
: ``} : ``}
@@ -1151,11 +1177,6 @@ export class FrigateCard extends LitElement {
.viewerConfig=${this.config.event_viewer} .viewerConfig=${this.config.event_viewer}
.resolvedMediaCache=${this._resolvedMediaCache} .resolvedMediaCache=${this._resolvedMediaCache}
class="${classMap(viewerClasses)}" class="${classMap(viewerClasses)}"
@frigate-card:change-view=${this._changeViewHandler}
@frigate-card:media-show=${this._mediaShowHandler}
@frigate-card:pause=${this._pauseHandler}
@frigate-card:play=${this._playHandler}
@frigate-card:message=${this._messageHandler}
> >
</frigate-card-viewer>` </frigate-card-viewer>`
: ``} : ``}
@@ -1171,37 +1192,11 @@ export class FrigateCard extends LitElement {
.cameras=${this._cameras} .cameras=${this._cameras}
.preload=${this.config.live.preload && !this._view.is('live')} .preload=${this.config.live.preload && !this._view.is('live')}
class="${classMap(liveClasses)}" class="${classMap(liveClasses)}"
@frigate-card:change-view=${this._changeViewHandler}
@frigate-card:media-show=${this._mediaShowHandler}
@frigate-card:pause=${this._pauseHandler}
@frigate-card:play=${this._playHandler}
@frigate-card:message=${this._messageHandler}
> >
</frigate-card-live> </frigate-card-live>
` `
: `` : ``
} }
${this.config.elements
? html`
<frigate-card-elements
.hass=${this._hass}
.elements=${this.config.elements}
.conditionState=${this._conditionState}
@frigate-card:message=${this._messageHandler}
@frigate-card:menu-add=${(e) => {
this._addDynamicMenuButton(e.detail);
}}
@frigate-card:menu-remove=${(e) => {
this._removeDynamicMenuButton(e.detail);
}}
@frigate-card:condition-state-request=${(ev) => {
conditionStateRequestHandler(ev, this._conditionState);
}}
>
</frigate-card-elements>
`
: ``}
</div>
`; `;
} }
+4 -1
View File
@@ -1,6 +1,9 @@
// TODO editor
// TODO editor: fill in a new camera and keep focus // TODO editor: fill in a new camera and keep focus
// TODO editor: fill in a new camera then backspace it away // TODO editor: fill in a new camera then backspace it away
// TODO editor: render correctly when name title is really long
// TODO editor: should not be able to move up a new camera before it's not new.
// TODO reconsider card_id, and using entity (which is unique) as that id
// TODO rename card_id to id?
// TODO webrtc entities in camera section? // TODO webrtc entities in camera section?
// TODO conditional elements based on camera name (requires event changed to propagate upwards) // TODO conditional elements based on camera name (requires event changed to propagate upwards)
// TODO change url to frigate_url? Would need to also fix upgrade logic to refer to new name. // TODO change url to frigate_url? Would need to also fix upgrade logic to refer to new name.
+6 -3
View File
@@ -255,7 +255,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
*/ */
protected _renderCameraHeader( protected _renderCameraHeader(
cameraIndex: number, cameraIndex: number,
cameraConfig: RawFrigateCardConfig | undefined, cameraConfig?: RawFrigateCardConfig,
addNewCamera?: boolean, addNewCamera?: boolean,
): TemplateResult { ): TemplateResult {
return html` return html`
@@ -275,11 +275,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
// starting with the most likely to be useful and working our // starting with the most likely to be useful and working our
// ways towards the least useful. // ways towards the least useful.
html` <span> html` <span>
Camera:
${cameraConfig?.title || ${cameraConfig?.title ||
cameraConfig?.camera_entity ||
cameraConfig?.card_id || cameraConfig?.card_id ||
[ [
cameraConfig?.camera_entity,
cameraConfig?.client_id, cameraConfig?.client_id,
cameraConfig?.camera_name, cameraConfig?.camera_name,
cameraConfig?.label, cameraConfig?.label,
@@ -771,6 +770,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
return; return;
} }
if (getConfigValue(this._config, key) === value) {
return;
}
const newConfig = copyConfig(this._config); const newConfig = copyConfig(this._config);
if (value === '' || typeof value === 'undefined') { if (value === '' || typeof value === 'undefined') {
deleteConfigValue(newConfig, key); deleteConfigValue(newConfig, key);
+2 -2
View File
@@ -155,8 +155,8 @@
"invalid_configuration_no_hint": "No location hint available (bad or missing type?)", "invalid_configuration_no_hint": "No location hint available (bad or missing type?)",
"upgrade_available": "An automated card configuration upgrade is available, please visit the visual card editor", "upgrade_available": "An automated card configuration upgrade is available, please visit the visual card editor",
"missing_webrtc": "WebRTC component not found", "missing_webrtc": "WebRTC component not found",
"no_cameras": "No cameras found, you must configure at least one camera configured with a camera_entity or camera_name", "no_cameras": "No valid cameras found, you must configure at least one camera with either a camera_entity or camera_name",
"duplicate_frigate_camera_name": "Duplicate Frigate camera name, use the 'id' parameter to uniquely identify them", "duplicate_frigate_camera_name": "Duplicate Frigate camera name, use the 'card_id' parameter to uniquely identify them",
"could_not_render_elements": "Could not render picture elements", "could_not_render_elements": "Could not render picture elements",
"invalid_elements_config": "Invalid picture elements configuration", "invalid_elements_config": "Invalid picture elements configuration",
"jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path", "jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path",
+1 -1
View File
@@ -20,7 +20,6 @@
pointer-events: none; pointer-events: none;
} }
.values { .values {
margin-left: 30px;
padding: 10px; padding: 10px;
background: var(--secondary-background-color); background: var(--secondary-background-color);
display: grid; display: grid;
@@ -54,6 +53,7 @@ div.upgrade span {
} }
.camera-header .new-camera { .camera-header .new-camera {
font-style: italic; font-style: italic;
color: var(--secondary-text-color, 'black');
} }
.cameras { .cameras {
margin: 5px 5px 10px 20px; margin: 5px 5px 10px 20px;