Make error handling and console logging consistent
This commit is contained in:
+12
-8
@@ -75,7 +75,7 @@ import {
|
|||||||
frigateCardHasAction,
|
frigateCardHasAction,
|
||||||
getActionConfigGivenAction,
|
getActionConfigGivenAction,
|
||||||
} from './utils/action.js';
|
} from './utils/action.js';
|
||||||
import { contentsChanged } from './utils/basic.js';
|
import { contentsChanged, errorToConsole } from './utils/basic.js';
|
||||||
import { getCameraIcon, getCameraID, getCameraTitle } from './utils/camera.js';
|
import { getCameraIcon, getCameraID, getCameraTitle } from './utils/camera.js';
|
||||||
import {
|
import {
|
||||||
getEntityIcon,
|
getEntityIcon,
|
||||||
@@ -673,7 +673,7 @@ export class FrigateCard extends LitElement {
|
|||||||
try {
|
try {
|
||||||
entityList = await getAllEntities(this._hass);
|
entityList = await getAllEntities(this._hass);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
errorToConsole(e as Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cameras: Map<string, CameraConfig> = new Map();
|
const cameras: Map<string, CameraConfig> = new Map();
|
||||||
@@ -688,7 +688,11 @@ export class FrigateCard extends LitElement {
|
|||||||
if (config.camera_entity) {
|
if (config.camera_entity) {
|
||||||
try {
|
try {
|
||||||
entity = await getExtendedEntity(this._hass, config.camera_entity, cache);
|
entity = await getExtendedEntity(this._hass, config.camera_entity, cache);
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
// Silently ignore errors here, as non-Frigate camera entities may not
|
||||||
|
// necessarily have a registry entry and otherwise this would cause
|
||||||
|
// log spam for those cases.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.frigate.camera_name && entity) {
|
if (!config.frigate.camera_name && entity) {
|
||||||
@@ -717,7 +721,7 @@ export class FrigateCard extends LitElement {
|
|||||||
cache,
|
cache,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
errorToConsole(e as Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.triggers.motion) {
|
if (config.triggers.motion) {
|
||||||
@@ -1180,7 +1184,7 @@ export class FrigateCard extends LitElement {
|
|||||||
try {
|
try {
|
||||||
response = await homeAssistantSignPath(this._hass, path);
|
response = await homeAssistantSignPath(this._hass, path);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
errorToConsole(e as Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
@@ -1860,7 +1864,7 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"frigate-card": FrigateCard
|
'frigate-card': FrigateCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-18
@@ -9,7 +9,7 @@ import {
|
|||||||
LitElement,
|
LitElement,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
unsafeCSS
|
unsafeCSS,
|
||||||
} from 'lit';
|
} from 'lit';
|
||||||
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';
|
||||||
@@ -33,16 +33,16 @@ import {
|
|||||||
LiveProvider,
|
LiveProvider,
|
||||||
MediaShowInfo,
|
MediaShowInfo,
|
||||||
TransitionEffect,
|
TransitionEffect,
|
||||||
WebRTCCardConfig
|
WebRTCCardConfig,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { stopEventFromActivatingCardWideActions } from '../utils/action.js';
|
import { stopEventFromActivatingCardWideActions } from '../utils/action.js';
|
||||||
import { contentsChanged } from '../utils/basic.js';
|
import { contentsChanged, errorToConsole } from '../utils/basic.js';
|
||||||
import { getCameraIcon, getCameraTitle } from '../utils/camera.js';
|
import { getCameraIcon, getCameraTitle } from '../utils/camera.js';
|
||||||
import { homeAssistantSignPath } from '../utils/ha';
|
import { homeAssistantSignPath } from '../utils/ha';
|
||||||
import { getFullDependentBrowseMediaQueryParameters } from '../utils/ha/browse-media.js';
|
import { getFullDependentBrowseMediaQueryParameters } from '../utils/ha/browse-media.js';
|
||||||
import {
|
import {
|
||||||
dispatchExistingMediaShowInfoAsEvent,
|
dispatchExistingMediaShowInfoAsEvent,
|
||||||
dispatchMediaShowEvent
|
dispatchMediaShowEvent,
|
||||||
} from '../utils/media-info.js';
|
} from '../utils/media-info.js';
|
||||||
import { View } from '../view.js';
|
import { View } from '../view.js';
|
||||||
import { AutoMediaPlugin, AutoMediaPluginType } from './embla-plugins/automedia.js';
|
import { AutoMediaPlugin, AutoMediaPluginType } from './embla-plugins/automedia.js';
|
||||||
@@ -52,8 +52,8 @@ import { dispatchErrorMessageEvent } from './message.js';
|
|||||||
import './next-prev-control.js';
|
import './next-prev-control.js';
|
||||||
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
||||||
import './title-control.js';
|
import './title-control.js';
|
||||||
import "./surround-thumbnails";
|
import './surround-thumbnails';
|
||||||
import "../patches/ha-camera-stream";
|
import '../patches/ha-camera-stream';
|
||||||
|
|
||||||
// Number of seconds a signed URL is valid for.
|
// Number of seconds a signed URL is valid for.
|
||||||
const URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60;
|
const URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60;
|
||||||
@@ -913,7 +913,7 @@ export class FrigateCardLiveWebRTCCard extends LitElement {
|
|||||||
// Set the id to ensure that the relevant CSS styles will have
|
// Set the id to ensure that the relevant CSS styles will have
|
||||||
// sufficient specifity to overcome some styles that are otherwise
|
// sufficient specifity to overcome some styles that are otherwise
|
||||||
// applied to <ha-card> in Safari.
|
// applied to <ha-card> in Safari.
|
||||||
webrtcElement.id = "webrtc";
|
webrtcElement.id = 'webrtc';
|
||||||
}
|
}
|
||||||
return html`${webrtcElement}`;
|
return html`${webrtcElement}`;
|
||||||
};
|
};
|
||||||
@@ -1021,7 +1021,11 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
* @returns A URL or null.
|
* @returns A URL or null.
|
||||||
*/
|
*/
|
||||||
protected async _getURL(): Promise<string | null> {
|
protected async _getURL(): Promise<string | null> {
|
||||||
if (!this.hass || !this.cameraConfig?.frigate.client_id || !this.cameraConfig?.frigate.camera_name) {
|
if (
|
||||||
|
!this.hass ||
|
||||||
|
!this.cameraConfig?.frigate.client_id ||
|
||||||
|
!this.cameraConfig?.frigate.camera_name
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1033,8 +1037,8 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
`/jsmpeg/${this.cameraConfig.frigate.camera_name}`,
|
`/jsmpeg/${this.cameraConfig.frigate.camera_name}`,
|
||||||
URL_SIGN_EXPIRY_SECONDS,
|
URL_SIGN_EXPIRY_SECONDS,
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
console.warn(err);
|
errorToConsole(e as Error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!response) {
|
if (!response) {
|
||||||
@@ -1182,12 +1186,12 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"frigate-card-live-jsmpeg": FrigateCardLiveJSMPEG
|
'frigate-card-live-jsmpeg': FrigateCardLiveJSMPEG;
|
||||||
"frigate-card-live-webrtc-card": FrigateCardLiveWebRTCCard
|
'frigate-card-live-webrtc-card': FrigateCardLiveWebRTCCard;
|
||||||
"frigate-card-live-ha": FrigateCardLiveFrigate
|
'frigate-card-live-ha': FrigateCardLiveFrigate;
|
||||||
"frigate-card-live-provider": FrigateCardLiveProvider
|
'frigate-card-live-provider': FrigateCardLiveProvider;
|
||||||
"frigate-card-live-carousel": FrigateCardLiveCarousel
|
'frigate-card-live-carousel': FrigateCardLiveCarousel;
|
||||||
"frigate-card-live": FrigateCardLive
|
'frigate-card-live': FrigateCardLive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-16
@@ -6,7 +6,7 @@ import {
|
|||||||
fromUnixTime,
|
fromUnixTime,
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
startOfHour,
|
startOfHour,
|
||||||
sub
|
sub,
|
||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
LitElement,
|
LitElement,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
unsafeCSS
|
unsafeCSS,
|
||||||
} from 'lit';
|
} from 'lit';
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
TimelineItem,
|
TimelineItem,
|
||||||
TimelineOptions,
|
TimelineOptions,
|
||||||
TimelineOptionsCluster,
|
TimelineOptionsCluster,
|
||||||
TimelineWindow
|
TimelineWindow,
|
||||||
} from 'vis-timeline/esnext';
|
} from 'vis-timeline/esnext';
|
||||||
import { CAMERA_BIRDSEYE } from '../const';
|
import { CAMERA_BIRDSEYE } from '../const';
|
||||||
import { localize } from '../localize/localize';
|
import { localize } from '../localize/localize';
|
||||||
@@ -42,10 +42,10 @@ import {
|
|||||||
frigateCardConfigDefaults,
|
frigateCardConfigDefaults,
|
||||||
FrigateCardError,
|
FrigateCardError,
|
||||||
FrigateEvent,
|
FrigateEvent,
|
||||||
TimelineConfig
|
TimelineConfig,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { stopEventFromActivatingCardWideActions } from '../utils/action';
|
import { stopEventFromActivatingCardWideActions } from '../utils/action';
|
||||||
import { dispatchFrigateCardEvent, prettifyTitle } from '../utils/basic';
|
import { dispatchFrigateCardEvent, errorToConsole, prettifyTitle } from '../utils/basic';
|
||||||
import { getCameraTitle } from '../utils/camera.js';
|
import { getCameraTitle } from '../utils/camera.js';
|
||||||
import {
|
import {
|
||||||
getRecordingSegments,
|
getRecordingSegments,
|
||||||
@@ -53,7 +53,7 @@ import {
|
|||||||
getUniqueFrigateCameraEventsID,
|
getUniqueFrigateCameraEventsID,
|
||||||
getUniqueFrigateCameraID,
|
getUniqueFrigateCameraID,
|
||||||
RecordingSegments,
|
RecordingSegments,
|
||||||
RecordingSummary
|
RecordingSummary,
|
||||||
} from '../utils/frigate';
|
} from '../utils/frigate';
|
||||||
import {
|
import {
|
||||||
createEventParentForChildren,
|
createEventParentForChildren,
|
||||||
@@ -61,7 +61,7 @@ import {
|
|||||||
generateRecordingIdentifier,
|
generateRecordingIdentifier,
|
||||||
getBrowseMediaQueryParameters,
|
getBrowseMediaQueryParameters,
|
||||||
isTrueMedia,
|
isTrueMedia,
|
||||||
multipleBrowseMediaQuery
|
multipleBrowseMediaQuery,
|
||||||
} from '../utils/ha/browse-media';
|
} from '../utils/ha/browse-media';
|
||||||
import { View, ViewContext } from '../view';
|
import { View, ViewContext } from '../view';
|
||||||
import { dispatchFrigateCardErrorEvent, dispatchMessageEvent } from './message.js';
|
import { dispatchFrigateCardErrorEvent, dispatchMessageEvent } from './message.js';
|
||||||
@@ -300,7 +300,7 @@ class TimelineDataManager {
|
|||||||
this._dateStart,
|
this._dateStart,
|
||||||
this._dateEnd,
|
this._dateEnd,
|
||||||
),
|
),
|
||||||
...(recordings ? [this._fetchRecordings(element, hass, cameras)] : []),
|
...(recordings ? [this._fetchRecordings(hass, cameras)] : []),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -315,7 +315,6 @@ class TimelineDataManager {
|
|||||||
* @param end Fetch events that start earlier than this date.
|
* @param end Fetch events that start earlier than this date.
|
||||||
*/
|
*/
|
||||||
protected async _fetchRecordings(
|
protected async _fetchRecordings(
|
||||||
element: HTMLElement,
|
|
||||||
hass: ExtendedHomeAssistant,
|
hass: ExtendedHomeAssistant,
|
||||||
cameras: Map<string, CameraConfig>,
|
cameras: Map<string, CameraConfig>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -329,7 +328,7 @@ class TimelineDataManager {
|
|||||||
if (!config.frigate.camera_name) {
|
if (!config.frigate.camera_name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let summary: RecordingSummary;
|
let summary: RecordingSummary = [];
|
||||||
try {
|
try {
|
||||||
summary = await getRecordingsSummary(
|
summary = await getRecordingsSummary(
|
||||||
hass,
|
hass,
|
||||||
@@ -337,7 +336,9 @@ class TimelineDataManager {
|
|||||||
config.frigate.camera_name,
|
config.frigate.camera_name,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return dispatchFrigateCardErrorEvent(element, e as FrigateCardError);
|
// Recording failure should not disrupt the rest of the timeline
|
||||||
|
// experience.
|
||||||
|
errorToConsole(e as Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const dayData of summary) {
|
for (const dayData of summary) {
|
||||||
@@ -691,7 +692,9 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
results.set(camera, { segments: cameraResults[0], summary: cameraResults[1] });
|
results.set(camera, { segments: cameraResults[0], summary: cameraResults[1] });
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
errorToConsole(e as Error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const cameras = camera ? [camera] : [...(this.cameras?.keys() ?? [])];
|
const cameras = camera ? [camera] : [...(this.cameras?.keys() ?? [])];
|
||||||
await Promise.all(cameras.map((camera) => fetch(camera, this.cameras?.get(camera))));
|
await Promise.all(cameras.map((camera) => fetch(camera, this.cameras?.get(camera))));
|
||||||
@@ -1239,8 +1242,8 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"frigate-card-timeline-core": FrigateCardTimelineCore
|
'frigate-card-timeline-core': FrigateCardTimelineCore;
|
||||||
"frigate-card-timeline": FrigateCardTimeline
|
'frigate-card-timeline': FrigateCardTimeline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,7 +320,6 @@
|
|||||||
"invalid_configuration": "Invalid configuration",
|
"invalid_configuration": "Invalid configuration",
|
||||||
"invalid_configuration_no_hint": "No location hint available (bad or missing type?)",
|
"invalid_configuration_no_hint": "No location hint available (bad or missing type?)",
|
||||||
"invalid_elements_config": "Invalid picture elements configuration",
|
"invalid_elements_config": "Invalid picture elements configuration",
|
||||||
"invalid_keys": "Invalid keys",
|
|
||||||
"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",
|
||||||
|
|||||||
@@ -322,7 +322,6 @@
|
|||||||
"invalid_configuration": "Configuração inválida",
|
"invalid_configuration": "Configuração inválida",
|
||||||
"invalid_configuration_no_hint": "Nenhuma dica de local disponível (tipo incorreto ou ausente?)",
|
"invalid_configuration_no_hint": "Nenhuma dica de local disponível (tipo incorreto ou ausente?)",
|
||||||
"invalid_elements_config": "Configuração de elementos de imagem inválida",
|
"invalid_elements_config": "Configuração de elementos de imagem inválida",
|
||||||
"invalid_keys": "Chaves inválidas",
|
|
||||||
"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",
|
||||||
|
|||||||
+20
-18
@@ -9,7 +9,7 @@ import {
|
|||||||
NoActionConfig,
|
NoActionConfig,
|
||||||
Themes,
|
Themes,
|
||||||
ToggleActionConfig,
|
ToggleActionConfig,
|
||||||
UrlActionConfig
|
UrlActionConfig,
|
||||||
} from 'custom-card-helpers';
|
} from 'custom-card-helpers';
|
||||||
import { StyleInfo } from 'lit/directives/style-map.js';
|
import { StyleInfo } from 'lit/directives/style-map.js';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -44,7 +44,8 @@ const FRIGATE_CARD_VIEWS = [
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type FrigateCardView = typeof FRIGATE_CARD_VIEWS[number];
|
export type FrigateCardView = typeof FRIGATE_CARD_VIEWS[number];
|
||||||
export type FrigateCardUserSpecifiedView = typeof FRIGATE_CARD_VIEWS_USER_SPECIFIED[number];
|
export type FrigateCardUserSpecifiedView =
|
||||||
|
typeof FRIGATE_CARD_VIEWS_USER_SPECIFIED[number];
|
||||||
export const FRIGATE_CARD_VIEW_DEFAULT = 'live' as const;
|
export const FRIGATE_CARD_VIEW_DEFAULT = 'live' as const;
|
||||||
|
|
||||||
const FRIGATE_MENU_STYLES = ['none', 'hidden', 'overlay', 'hover', 'outside'] as const;
|
const FRIGATE_MENU_STYLES = ['none', 'hidden', 'overlay', 'hover', 'outside'] as const;
|
||||||
@@ -395,14 +396,16 @@ const cameraConfigSchema = z
|
|||||||
// this card.
|
// this card.
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
|
|
||||||
frigate: z.object({
|
frigate: z
|
||||||
// No URL validation to allow relative URLs within HA (e.g. Frigate addon).
|
.object({
|
||||||
url: z.string().optional(),
|
// No URL validation to allow relative URLs within HA (e.g. Frigate addon).
|
||||||
client_id: z.string().default(cameraConfigDefault.frigate.client_id),
|
url: z.string().optional(),
|
||||||
camera_name: z.string().optional(),
|
client_id: z.string().default(cameraConfigDefault.frigate.client_id),
|
||||||
label: z.string().optional(),
|
camera_name: z.string().optional(),
|
||||||
zone: z.string().optional(),
|
label: z.string().optional(),
|
||||||
}).default(cameraConfigDefault.frigate),
|
zone: z.string().optional(),
|
||||||
|
})
|
||||||
|
.default(cameraConfigDefault.frigate),
|
||||||
|
|
||||||
// Camera identifiers for WebRTC.
|
// Camera identifiers for WebRTC.
|
||||||
webrtc_card: webrtcCardCameraConfigSchema.optional(),
|
webrtc_card: webrtcCardCameraConfigSchema.optional(),
|
||||||
@@ -414,14 +417,13 @@ const cameraConfigSchema = z
|
|||||||
})
|
})
|
||||||
.default(cameraConfigDefault.dependencies),
|
.default(cameraConfigDefault.dependencies),
|
||||||
|
|
||||||
triggers: z.object({
|
triggers: z
|
||||||
motion: z.boolean().default(cameraConfigDefault.triggers.motion),
|
.object({
|
||||||
occupancy: z.boolean().default(cameraConfigDefault.triggers.occupancy),
|
motion: z.boolean().default(cameraConfigDefault.triggers.motion),
|
||||||
entities: z
|
occupancy: z.boolean().default(cameraConfigDefault.triggers.occupancy),
|
||||||
.string()
|
entities: z.string().array().default(cameraConfigDefault.triggers.entities),
|
||||||
.array()
|
})
|
||||||
.default(cameraConfigDefault.triggers.entities),
|
.default(cameraConfigDefault.triggers),
|
||||||
}).default(cameraConfigDefault.triggers),
|
|
||||||
})
|
})
|
||||||
.default(cameraConfigDefault);
|
.default(cameraConfigDefault);
|
||||||
export type CameraConfig = z.infer<typeof cameraConfigSchema>;
|
export type CameraConfig = z.infer<typeof cameraConfigSchema>;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
|
import { FrigateCardError } from '../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch a Frigate Card event.
|
* Dispatch a Frigate Card event.
|
||||||
@@ -60,3 +61,19 @@ export function arrayMove(target: unknown[], from: number, to: number): void {
|
|||||||
export function contentsChanged(n: unknown, o: unknown): boolean {
|
export function contentsChanged(n: unknown, o: unknown): boolean {
|
||||||
return !isEqual(n, o);
|
return !isEqual(n, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an error as a warning to the console.
|
||||||
|
* @param e The Error object.
|
||||||
|
* @param func The Console func to call.
|
||||||
|
*/
|
||||||
|
export function errorToConsole(e: Error, func?: CallableFunction): void {
|
||||||
|
if (!func) {
|
||||||
|
func = console.warn;
|
||||||
|
}
|
||||||
|
if (e instanceof FrigateCardError && e.context) {
|
||||||
|
func(e, e.context);
|
||||||
|
} else {
|
||||||
|
func(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ const recordingSegmentsSchema = recordingSegmentSchema.array();
|
|||||||
export type RecordingSegments = z.infer<typeof recordingSegmentsSchema>;
|
export type RecordingSegments = z.infer<typeof recordingSegmentsSchema>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the recordings summary.
|
* Get the recordings summary. May throw.
|
||||||
* @param hass The Home Assistant object.
|
* @param hass The Home Assistant object.
|
||||||
* @param client_id The Frigate client_id.
|
* @param client_id The Frigate client_id.
|
||||||
* @param camera_name The Frigate camera name.
|
* @param camera_name The Frigate camera name.
|
||||||
@@ -70,7 +70,7 @@ export const getRecordingsSummary = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the recording segments..
|
* Get the recording segments. May throw.
|
||||||
* @param hass The Home Assistant object.
|
* @param hass The Home Assistant object.
|
||||||
* @param client_id The Frigate client_id.
|
* @param client_id The Frigate client_id.
|
||||||
* @param camera_name The Frigate camera name.
|
* @param camera_name The Frigate camera name.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
EntityList,
|
EntityList,
|
||||||
entityListSchema,
|
entityListSchema,
|
||||||
ExtendedEntity,
|
ExtendedEntity,
|
||||||
extendedEntitySchema
|
extendedEntitySchema,
|
||||||
} from '../../types.js';
|
} from '../../types.js';
|
||||||
|
|
||||||
export class ExtendedEntityCache {
|
export class ExtendedEntityCache {
|
||||||
@@ -78,7 +78,7 @@ export const getExtendedEntity = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the extended entity information for an array of entities.
|
* Get the extended entity information for an array of entities. May throw.
|
||||||
* @param hass The Home Assistant object.
|
* @param hass The Home Assistant object.
|
||||||
* @param entities An array of entity ids.
|
* @param entities An array of entity ids.
|
||||||
* @param cache An optional ExtendedEntityCache.
|
* @param cache An optional ExtendedEntityCache.
|
||||||
@@ -98,7 +98,7 @@ export const getExtendedEntities = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of all entities from the entity registry.
|
* Get a list of all entities from the entity registry. May throw.
|
||||||
* @param hass The Home Assistant object.
|
* @param hass The Home Assistant object.
|
||||||
* @returns An entity list object.
|
* @returns An entity list object.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+25
-25
@@ -9,7 +9,7 @@ import {
|
|||||||
FrigateCardError,
|
FrigateCardError,
|
||||||
SignedPath,
|
SignedPath,
|
||||||
signedPathSchema,
|
signedPathSchema,
|
||||||
StateParameters
|
StateParameters,
|
||||||
} from '../../types.js';
|
} from '../../types.js';
|
||||||
import { stateIcon } from '../icons/state-icon.js';
|
import { stateIcon } from '../icons/state-icon.js';
|
||||||
import { getParseErrorKeys } from '../zod.js';
|
import { getParseErrorKeys } from '../zod.js';
|
||||||
@@ -26,27 +26,30 @@ export async function homeAssistantWSRequest<T>(
|
|||||||
schema: ZodSchema<T>,
|
schema: ZodSchema<T>,
|
||||||
request: MessageBase,
|
request: MessageBase,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const response = await hass.callWS<T>(request);
|
let response;
|
||||||
|
try {
|
||||||
|
response = await hass.callWS<T>(request);
|
||||||
|
} catch (e) {
|
||||||
|
if (!(e instanceof Error)) {
|
||||||
|
throw new FrigateCardError(localize('error.failed_response'), {
|
||||||
|
request: request,
|
||||||
|
response: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
const error_message = `${localize('error.empty_response')}: ${JSON.stringify(
|
throw new FrigateCardError(localize('error.empty_response'), {
|
||||||
request,
|
request: request,
|
||||||
)}`;
|
});
|
||||||
console.warn(error_message);
|
|
||||||
throw new Error(error_message);
|
|
||||||
}
|
}
|
||||||
const parseResult = schema.safeParse(response);
|
const parseResult = schema.safeParse(response);
|
||||||
if (!parseResult.success) {
|
if (!parseResult.success) {
|
||||||
const keys = getParseErrorKeys<T>(parseResult.error);
|
throw new FrigateCardError(localize('error.invalid_response'), {
|
||||||
const error_message = localize('error.invalid_response');
|
|
||||||
console.warn(
|
|
||||||
`${error_message}: ${JSON.stringify(request)}. ${localize(
|
|
||||||
'error.invalid_keys',
|
|
||||||
)}: ${keys}`,
|
|
||||||
);
|
|
||||||
throw new FrigateCardError(error_message, {
|
|
||||||
request: request,
|
request: request,
|
||||||
invalid_keys: keys,
|
response: response,
|
||||||
|
invalid_keys: getParseErrorKeys<T>(parseResult.error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return parseResult.data;
|
return parseResult.data;
|
||||||
@@ -96,9 +99,7 @@ export async function homeAssistantHTTPRequest<T>(
|
|||||||
let signResponse: string | null | undefined;
|
let signResponse: string | null | undefined;
|
||||||
try {
|
try {
|
||||||
signResponse = await homeAssistantSignPath(hass, url);
|
signResponse = await homeAssistantSignPath(hass, url);
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
console.warn(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!signResponse) {
|
if (!signResponse) {
|
||||||
throw new FrigateCardError(localize('error.failed_sign'), {
|
throw new FrigateCardError(localize('error.failed_sign'), {
|
||||||
@@ -127,21 +128,20 @@ export async function homeAssistantHTTPRequest<T>(
|
|||||||
try {
|
try {
|
||||||
raw_json = await response.json();
|
raw_json = await response.json();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
|
||||||
throw new FrigateCardError(localize('error.undecodable_response'), {
|
throw new FrigateCardError(localize('error.undecodable_response'), {
|
||||||
url: signedURL.toString(),
|
url: signedURL.toString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const parseResult = schema.safeParse(raw_json);
|
||||||
return schema.parse(raw_json);
|
if (!parseResult.success) {
|
||||||
} catch (e) {
|
|
||||||
console.warn(e);
|
|
||||||
throw new FrigateCardError(localize('error.invalid_response'), {
|
throw new FrigateCardError(localize('error.invalid_response'), {
|
||||||
url: signedURL.toString(),
|
url: signedURL.toString(),
|
||||||
response: raw_json,
|
raw_json: raw_json,
|
||||||
|
invalid_keys: getParseErrorKeys<T>(parseResult.error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return parseResult.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HassStateDifference {
|
interface HassStateDifference {
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import { HomeAssistant } from 'custom-card-helpers';
|
|||||||
import QuickLRU from 'quick-lru';
|
import QuickLRU from 'quick-lru';
|
||||||
import { homeAssistantWSRequest } from '.';
|
import { homeAssistantWSRequest } from '.';
|
||||||
import {
|
import {
|
||||||
FrigateBrowseMediaSource,
|
FrigateBrowseMediaSource,
|
||||||
ResolvedMedia,
|
ResolvedMedia,
|
||||||
resolvedMediaSchema
|
resolvedMediaSchema,
|
||||||
} from '../../types.js';
|
} from '../../types.js';
|
||||||
|
import { errorToConsole } from '../basic';
|
||||||
|
|
||||||
// It's important the cache size be at least as large as the largest likely
|
// It's important the cache size be at least as large as the largest likely
|
||||||
// media query or media items will from a given query will be evicted for other
|
// media query or media items will from a given query will be evicted for other
|
||||||
@@ -30,7 +31,6 @@ export class ResolvedMediaCache {
|
|||||||
return this._cache.has(id);
|
return this._cache.has(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get resolved media information given an id.
|
* Get resolved media information given an id.
|
||||||
* @param id The id.
|
* @param id The id.
|
||||||
@@ -55,7 +55,7 @@ export class ResolvedMediaCache {
|
|||||||
* @param hass The Home Assistant object.
|
* @param hass The Home Assistant object.
|
||||||
* @param mediaSource The media source object.
|
* @param mediaSource The media source object.
|
||||||
* @param cache An optional ResolvedMediaCache object.
|
* @param cache An optional ResolvedMediaCache object.
|
||||||
* @returns
|
* @returns The resolved media or `null`.
|
||||||
*/
|
*/
|
||||||
export const resolveMedia = async (
|
export const resolveMedia = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@@ -73,7 +73,12 @@ export const resolveMedia = async (
|
|||||||
type: 'media_source/resolve_media',
|
type: 'media_source/resolve_media',
|
||||||
media_content_id: mediaSource.media_content_id,
|
media_content_id: mediaSource.media_content_id,
|
||||||
};
|
};
|
||||||
const resolvedMedia = await homeAssistantWSRequest(hass, resolvedMediaSchema, request);
|
let resolvedMedia: ResolvedMedia | null = null;
|
||||||
|
try {
|
||||||
|
resolvedMedia = await homeAssistantWSRequest(hass, resolvedMediaSchema, request);
|
||||||
|
} catch (e) {
|
||||||
|
errorToConsole(e as Error);
|
||||||
|
}
|
||||||
if (cache && resolvedMedia) {
|
if (cache && resolvedMedia) {
|
||||||
cache.set(mediaSource.media_content_id, resolvedMedia);
|
cache.set(mediaSource.media_content_id, resolvedMedia);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user