Fix error condition rendering.
This commit is contained in:
+125
-130
@@ -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',
|
});
|
||||||
},
|
errorFree = false;
|
||||||
true,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
cameras.set(id, config);
|
cameras.set(id, config);
|
||||||
}
|
}
|
||||||
@@ -434,16 +433,16 @@ 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',
|
});
|
||||||
},
|
errorFree = false;
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cameras = cameras;
|
if (errorFree) {
|
||||||
|
this._cameras = cameras;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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 {
|
||||||
this._message = null;
|
if (args?.resetMessage ?? true) {
|
||||||
|
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,11 +617,12 @@ 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -628,7 +630,7 @@ export class FrigateCard extends LitElement {
|
|||||||
* @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)}">
|
||||||
${this._cameras === undefined
|
<div class="${classMap(pictureElementsClasses)}">
|
||||||
? until(
|
${this.config.elements
|
||||||
(async () => {
|
? // Always show elements to allow for custom menu items (etc.) to
|
||||||
await this._loadCameras();
|
// be present even if a particular view has an error.
|
||||||
this._changeView();
|
html` <frigate-card-elements
|
||||||
return this._render();
|
.hass=${this._hass}
|
||||||
})(),
|
.elements=${this.config.elements}
|
||||||
renderProgressIndicator(),
|
.conditionState=${this._conditionState}
|
||||||
)
|
@frigate-card:menu-add=${(e) => {
|
||||||
: this._render()}
|
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
|
||||||
|
? until(
|
||||||
|
(async () => {
|
||||||
|
await this._loadCameras();
|
||||||
|
// Don't reset messages as errors may have been generated
|
||||||
|
// during the camera load.
|
||||||
|
this._changeView({ resetMessage: false });
|
||||||
|
return this._render();
|
||||||
|
})(),
|
||||||
|
renderProgressIndicator(),
|
||||||
|
)
|
||||||
|
: // 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,93 +1147,56 @@ export class FrigateCard extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="${classMap(pictureElementsClasses)}">
|
${!this._message && this._view.is('image')
|
||||||
${this._message ? renderMessage(this._message) : ``}
|
? html` <frigate-card-image
|
||||||
${!this._message && this._view.is('image')
|
.imageConfig=${this.config.image}
|
||||||
? html` <frigate-card-image
|
class="${classMap(imageClasses)}"
|
||||||
.imageConfig=${this.config.image}
|
>
|
||||||
class="${classMap(imageClasses)}"
|
</frigate-card-image>`
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
: ``}
|
||||||
@frigate-card:message=${this._messageHandler}
|
${!this._message && this._view.isGalleryView()
|
||||||
>
|
? html` <frigate-card-gallery
|
||||||
</frigate-card-image>`
|
.hass=${this._hass}
|
||||||
: ``}
|
.view=${this._view}
|
||||||
${!this._message && this._view.isGalleryView()
|
.browseMediaQueryParameters=${BrowseMediaUtil.getBrowseMediaQueryParametersFromView(
|
||||||
? html` <frigate-card-gallery
|
this._view,
|
||||||
.hass=${this._hass}
|
cameraConfig,
|
||||||
.view=${this._view}
|
)}
|
||||||
.browseMediaQueryParameters=${BrowseMediaUtil.getBrowseMediaQueryParametersFromView(
|
class="${classMap(galleryClasses)}"
|
||||||
this._view,
|
>
|
||||||
cameraConfig,
|
</frigate-card-gallery>`
|
||||||
)}
|
: ``}
|
||||||
class="${classMap(galleryClasses)}"
|
${!this._message && this._view.isViewerView()
|
||||||
@frigate-card:change-view=${this._changeViewHandler}
|
? html` <frigate-card-viewer
|
||||||
@frigate-card:message=${this._messageHandler}
|
.hass=${this._hass}
|
||||||
>
|
.view=${this._view}
|
||||||
</frigate-card-gallery>`
|
.browseMediaQueryParameters=${BrowseMediaUtil.getBrowseMediaQueryParametersFromView(
|
||||||
: ``}
|
this._view,
|
||||||
${!this._message && this._view.isViewerView()
|
cameraConfig,
|
||||||
? html` <frigate-card-viewer
|
)}
|
||||||
.hass=${this._hass}
|
.viewerConfig=${this.config.event_viewer}
|
||||||
.view=${this._view}
|
.resolvedMediaCache=${this._resolvedMediaCache}
|
||||||
.browseMediaQueryParameters=${BrowseMediaUtil.getBrowseMediaQueryParametersFromView(
|
class="${classMap(viewerClasses)}"
|
||||||
this._view,
|
>
|
||||||
cameraConfig,
|
</frigate-card-viewer>`
|
||||||
)}
|
: ``}
|
||||||
.viewerConfig=${this.config.event_viewer}
|
${
|
||||||
.resolvedMediaCache=${this._resolvedMediaCache}
|
// Note the subtle difference in condition below vs the other views in order
|
||||||
class="${classMap(viewerClasses)}"
|
// to always render the live view for live.preload mode.
|
||||||
@frigate-card:change-view=${this._changeViewHandler}
|
(!this._message && this._view.is('live')) || this.config.live.preload
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
|
||||||
@frigate-card:pause=${this._pauseHandler}
|
|
||||||
@frigate-card:play=${this._playHandler}
|
|
||||||
@frigate-card:message=${this._messageHandler}
|
|
||||||
>
|
|
||||||
</frigate-card-viewer>`
|
|
||||||
: ``}
|
|
||||||
${
|
|
||||||
// Note the subtle difference in condition below vs the other views in order
|
|
||||||
// to always render the live view for live.preload mode.
|
|
||||||
(!this._message && this._view.is('live')) || this.config.live.preload
|
|
||||||
? html`
|
|
||||||
<frigate-card-live
|
|
||||||
.hass=${this._hass}
|
|
||||||
.view=${this._view}
|
|
||||||
.liveConfig=${this.config.live}
|
|
||||||
.cameras=${this._cameras}
|
|
||||||
.preload=${this.config.live.preload && !this._view.is('live')}
|
|
||||||
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>
|
|
||||||
`
|
|
||||||
: ``
|
|
||||||
}
|
|
||||||
${this.config.elements
|
|
||||||
? html`
|
? html`
|
||||||
<frigate-card-elements
|
<frigate-card-live
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
.elements=${this.config.elements}
|
.view=${this._view}
|
||||||
.conditionState=${this._conditionState}
|
.liveConfig=${this.config.live}
|
||||||
@frigate-card:message=${this._messageHandler}
|
.cameras=${this._cameras}
|
||||||
@frigate-card:menu-add=${(e) => {
|
.preload=${this.config.live.preload && !this._view.is('live')}
|
||||||
this._addDynamicMenuButton(e.detail);
|
class="${classMap(liveClasses)}"
|
||||||
}}
|
|
||||||
@frigate-card:menu-remove=${(e) => {
|
|
||||||
this._removeDynamicMenuButton(e.detail);
|
|
||||||
}}
|
|
||||||
@frigate-card:condition-state-request=${(ev) => {
|
|
||||||
conditionStateRequestHandler(ev, this._conditionState);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
</frigate-card-elements>
|
</frigate-card-live>
|
||||||
`
|
`
|
||||||
: ``}
|
: ``
|
||||||
</div>
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user