Improve card loading speed.
This commit is contained in:
+57
-26
@@ -709,24 +709,38 @@ export class FrigateCard extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const cache = new ExtendedEntityCache();
|
||||
const hasAutoTriggers = (config: CameraConfig): boolean => {
|
||||
return config.triggers.motion || config.triggers.occupancy;
|
||||
};
|
||||
const hasAnyTriggers = (config: CameraConfig): boolean => {
|
||||
return hasAutoTriggers(config) || !!config.triggers.entities.length;
|
||||
};
|
||||
const hasCameraName = (config: CameraConfig): boolean => {
|
||||
return !!config.frigate?.camera_name;
|
||||
};
|
||||
|
||||
// Loading cameras may require a number of calls to Home Assistant.
|
||||
//
|
||||
// - getAllEntities: Required if any camera has auto-triggers (motion or
|
||||
// occupancy sensors).
|
||||
// - getExtendedEntity: Per camera entity to autodetect the Frigate camera
|
||||
// name or to compute auto-triggers (motion or occupancy sensors).
|
||||
// - getExtendedEntities: Per binary sensor associated with a Frigate config
|
||||
// entry, to compute auto-triggers (motion or occupancy sensors).
|
||||
//
|
||||
// For loading performance these are only called when absolutely needed.
|
||||
|
||||
let entityList: EntityList | undefined;
|
||||
try {
|
||||
entityList = await getAllEntities(this._hass);
|
||||
} catch (e) {
|
||||
errorToConsole(e as Error);
|
||||
}
|
||||
const cache = new ExtendedEntityCache();
|
||||
const loadedCameras: CameraConfig[] = [];
|
||||
|
||||
const cameras: Map<string, CameraConfig> = new Map();
|
||||
let errorFree = true;
|
||||
|
||||
const addCameraConfig = async (config: CameraConfig) => {
|
||||
const addCameraConfig = async (config: CameraConfig, index: number) => {
|
||||
if (!this._hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
let entity: ExtendedEntity | null = null;
|
||||
if (config.camera_entity) {
|
||||
if (config.camera_entity && (hasAnyTriggers(config) || !hasCameraName(config))) {
|
||||
try {
|
||||
entity = await getExtendedEntity(this._hass, config.camera_entity, cache);
|
||||
} catch (e) {
|
||||
@@ -736,14 +750,15 @@ export class FrigateCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.frigate.camera_name && entity) {
|
||||
if (entity && !hasCameraName(config)) {
|
||||
const resolvedName = this._getFrigateCameraNameFromEntity(entity);
|
||||
if (resolvedName) {
|
||||
config.frigate.camera_name = resolvedName;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity && entityList) {
|
||||
if (entity && entityList && hasAnyTriggers(config)) {
|
||||
if (hasAutoTriggers(config)) {
|
||||
// Try to find the correct entities for the motion & occupancy sensors.
|
||||
// We know they are binary_sensors, and that they'll have the same
|
||||
// config entry ID as the camera. Searching via unique_id ensures this
|
||||
@@ -779,34 +794,50 @@ export class FrigateCard extends LitElement {
|
||||
}
|
||||
}
|
||||
}
|
||||
config.triggers.entities = [...new Set(config.triggers.entities)];
|
||||
|
||||
const id = getCameraID(config);
|
||||
config.triggers.entities = [...new Set(config.triggers.entities)];
|
||||
}
|
||||
|
||||
loadedCameras[index] = config;
|
||||
};
|
||||
|
||||
let errorFree = true;
|
||||
const cameras: Map<string, CameraConfig> = new Map();
|
||||
const configCameras = this._getConfig().cameras;
|
||||
|
||||
if (configCameras && Array.isArray(configCameras)) {
|
||||
if (configCameras.some((config) => hasAutoTriggers(config))) {
|
||||
try {
|
||||
entityList = await getAllEntities(this._hass);
|
||||
} catch (e) {
|
||||
errorToConsole(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
// Load all cameras in parallel, but remember the order they were provided
|
||||
// (they must be added to the cameraMap in this same order).
|
||||
await Promise.all(configCameras.map((configCamera, index) => addCameraConfig(configCamera, index)))
|
||||
|
||||
loadedCameras.forEach((loadedCamera: CameraConfig) => {
|
||||
const id = getCameraID(loadedCamera);
|
||||
if (!id) {
|
||||
this._setMessageAndUpdate({
|
||||
message: localize('error.no_camera_id'),
|
||||
type: 'error',
|
||||
context: config,
|
||||
context: loadedCamera,
|
||||
});
|
||||
errorFree = false;
|
||||
} else if (cameras.has(id)) {
|
||||
this._setMessageAndUpdate({
|
||||
message: localize('error.duplicate_camera_id'),
|
||||
type: 'error',
|
||||
context: config,
|
||||
context: loadedCamera,
|
||||
});
|
||||
errorFree = false;
|
||||
} else {
|
||||
cameras.set(id, config);
|
||||
}
|
||||
};
|
||||
|
||||
if (this._getConfig().cameras && Array.isArray(this._getConfig().cameras)) {
|
||||
// Cameras are loaded sequentially rather than in parallel to preserve the
|
||||
// order of the input camera array.
|
||||
for (const camera of this._getConfig().cameras) {
|
||||
await addCameraConfig(camera);
|
||||
cameras.set(id, loadedCamera);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!cameras.size) {
|
||||
|
||||
+3
-2
@@ -40,7 +40,7 @@ import {
|
||||
/**
|
||||
* Set a configuration value.
|
||||
* @param obj The configuration.
|
||||
* @param key The key to the property to set.
|
||||
* @param keys The key to the property to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
|
||||
@@ -55,7 +55,8 @@ export const setConfigValue = (
|
||||
/**
|
||||
* Get a configuration value.
|
||||
* @param obj The configuration.
|
||||
* @param key The key to the property to retrieve.
|
||||
* @param keys The key to the property to retrieve.
|
||||
* @param def Default if key not found.
|
||||
* @returns The property or undefined if not found.
|
||||
*/
|
||||
export const getConfigValue = (
|
||||
|
||||
+28
-6
@@ -9,8 +9,9 @@ import {
|
||||
FrigateCardConfig,
|
||||
RawFrigateCardConfig,
|
||||
} from './types';
|
||||
import { getConfigValue, setConfigValue } from './config-mgmt.js';
|
||||
import { getArrayConfigPath, getConfigValue, setConfigValue } from './config-mgmt.js';
|
||||
import {
|
||||
CONF_CAMERAS_ARRAY_TRIGGERS_OCCUPANCY,
|
||||
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
|
||||
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
||||
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
||||
@@ -64,7 +65,6 @@ const LOW_PROFILE_DEFAULTS = {
|
||||
[CONF_TIMELINE_SHOW_RECORDINGS]: false,
|
||||
|
||||
// Take no automatic media actions.
|
||||
[CONF_LIVE_AUTO_PLAY]: 'never' as const,
|
||||
[CONF_LIVE_AUTO_MUTE]: 'never' as const,
|
||||
[CONF_MEDIA_VIEWER_AUTO_PLAY]: 'never' as const,
|
||||
[CONF_MEDIA_VIEWER_AUTO_PAUSE]: 'never' as const,
|
||||
@@ -114,6 +114,10 @@ const LOW_PROFILE_DEFAULTS = {
|
||||
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
|
||||
};
|
||||
|
||||
const LOW_PROFILE_CAMERA_DEFAULTS = {
|
||||
[CONF_CAMERAS_ARRAY_TRIGGERS_OCCUPANCY]: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Set low performance profile mode. Sets flags as defined in
|
||||
* LOW_PROFILE_DEFAULTS unless they are explicitly overriden in the
|
||||
@@ -126,16 +130,34 @@ export const setLowPerformanceProfile = (
|
||||
inputConfig: RawFrigateCardConfig,
|
||||
parsedConfig: FrigateCardConfig,
|
||||
): FrigateCardConfig => {
|
||||
const setIfNotSpecified = (
|
||||
defaultLessConfig: RawFrigateCardConfig,
|
||||
parsedConfig: FrigateCardConfig,
|
||||
key: string,
|
||||
value: unknown,
|
||||
) => {
|
||||
if (getConfigValue(defaultLessConfig, key) === undefined) {
|
||||
setConfigValue(parsedConfig, key, value);
|
||||
}
|
||||
};
|
||||
|
||||
const defaultLessParseResult = deepRemoveDefaults(frigateCardConfigSchema).safeParse(
|
||||
inputConfig,
|
||||
);
|
||||
if (defaultLessParseResult.success) {
|
||||
const defaultLessConfig = defaultLessParseResult.data;
|
||||
Object.entries(LOW_PROFILE_DEFAULTS).forEach(([k, v]: [string, unknown]) => {
|
||||
if (getConfigValue(defaultLessConfig, k) === undefined) {
|
||||
setConfigValue(parsedConfig, k, v);
|
||||
}
|
||||
Object.entries(LOW_PROFILE_DEFAULTS).forEach(([k, v]: [string, unknown]) =>
|
||||
setIfNotSpecified(defaultLessConfig, parsedConfig, k, v),
|
||||
);
|
||||
|
||||
Object.entries(LOW_PROFILE_CAMERA_DEFAULTS).forEach(
|
||||
([rawKey, v]: [string, unknown]) => {
|
||||
defaultLessConfig.cameras.forEach((_, index: number) => {
|
||||
const indexedKey = getArrayConfigPath(rawKey, index);
|
||||
setIfNotSpecified(defaultLessConfig, parsedConfig, indexedKey, v);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
return parsedConfig;
|
||||
};
|
||||
|
||||
@@ -1213,6 +1213,11 @@ const performanceConfigDefault = {
|
||||
const performanceConfigSchema = z.object({
|
||||
profile: z.enum(['low', 'high'])
|
||||
}).default(performanceConfigDefault);
|
||||
export type PerformanceConfig = z.infer<typeof performanceConfigSchema>;
|
||||
|
||||
export interface CardWideConfig {
|
||||
performance?: PerformanceConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main card config.
|
||||
|
||||
Reference in New Issue
Block a user