refactor: Refactor entity register manager (#1597)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { localize } from '../../localize/localize';
|
import { localize } from '../../localize/localize';
|
||||||
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../../utils/ha/registry/entity';
|
||||||
import { Entity } from '../../utils/ha/entity-registry/types';
|
import { Entity } from '../../utils/ha/registry/entity/types';
|
||||||
import { Camera, CameraInitializationOptions } from '../camera';
|
import { Camera, CameraInitializationOptions } from '../camera';
|
||||||
import { CameraInitializationError } from '../error';
|
import { CameraInitializationError } from '../error';
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
MEDIA_CLASS_VIDEO,
|
MEDIA_CLASS_VIDEO,
|
||||||
RichBrowseMedia,
|
RichBrowseMedia,
|
||||||
} from '../../utils/ha/browse-media/types';
|
} from '../../utils/ha/browse-media/types';
|
||||||
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../../utils/ha/registry/entity';
|
||||||
import { ResolvedMediaCache, resolveMedia } from '../../utils/ha/resolved-media';
|
import { ResolvedMediaCache, resolveMedia } from '../../utils/ha/resolved-media';
|
||||||
import { ViewMedia } from '../../view/media';
|
import { ViewMedia } from '../../view/media';
|
||||||
import { RequestCache } from '../cache';
|
import { RequestCache } from '../cache';
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import { CameraConfig } from '../config/types';
|
|||||||
import { localize } from '../localize/localize';
|
import { localize } from '../localize/localize';
|
||||||
import { BrowseMediaManager } from '../utils/ha/browse-media/browse-media-manager';
|
import { BrowseMediaManager } from '../utils/ha/browse-media/browse-media-manager';
|
||||||
import { BrowseMedia } from '../utils/ha/browse-media/types';
|
import { BrowseMedia } from '../utils/ha/browse-media/types';
|
||||||
import { EntityRegistryManager } from '../utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../utils/ha/registry/entity';
|
||||||
import { Entity } from '../utils/ha/entity-registry/types';
|
|
||||||
import { ResolvedMediaCache } from '../utils/ha/resolved-media';
|
import { ResolvedMediaCache } from '../utils/ha/resolved-media';
|
||||||
import { MemoryRequestCache, RecordingSegmentsCache, RequestCache } from './cache';
|
import { MemoryRequestCache, RecordingSegmentsCache, RequestCache } from './cache';
|
||||||
import { CameraManagerEngine } from './engine';
|
import { CameraManagerEngine } from './engine';
|
||||||
@@ -81,10 +80,8 @@ export class CameraManagerEngineFactory {
|
|||||||
const cameraEntity = getCameraEntityFromConfig(cameraConfig);
|
const cameraEntity = getCameraEntityFromConfig(cameraConfig);
|
||||||
|
|
||||||
if (cameraEntity) {
|
if (cameraEntity) {
|
||||||
let entity: Entity | null;
|
const entity = await this._entityRegistryManager.getEntity(hass, cameraEntity);
|
||||||
try {
|
if (!entity) {
|
||||||
entity = await this._entityRegistryManager.getEntity(hass, cameraEntity);
|
|
||||||
} catch (e) {
|
|
||||||
// If the camera is not in the registry, but is in the HA states it is
|
// If the camera is not in the registry, but is in the HA states it is
|
||||||
// assumed to be a generic camera.
|
// assumed to be a generic camera.
|
||||||
if (hass.states[cameraEntity]) {
|
if (hass.states[cameraEntity]) {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { CameraConfig } from '../../config/types';
|
|||||||
import { localize } from '../../localize/localize';
|
import { localize } from '../../localize/localize';
|
||||||
import { PTZCapabilities, PTZMovementType } from '../../types';
|
import { PTZCapabilities, PTZMovementType } from '../../types';
|
||||||
import { errorToConsole } from '../../utils/basic';
|
import { errorToConsole } from '../../utils/basic';
|
||||||
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../../utils/ha/registry/entity';
|
||||||
import { Entity } from '../../utils/ha/entity-registry/types';
|
import { Entity } from '../../utils/ha/registry/entity/types';
|
||||||
import { Camera, CameraInitializationOptions } from '../camera';
|
import { Camera, CameraInitializationOptions } from '../camera';
|
||||||
import { Capabilities } from '../capabilities';
|
import { Capabilities } from '../capabilities';
|
||||||
import { CameraManagerEngine } from '../engine';
|
import { CameraManagerEngine } from '../engine';
|
||||||
@@ -67,9 +67,8 @@ export class FrigateCamera extends Camera {
|
|||||||
// Entity information is required if the Frigate camera name is missing, or
|
// Entity information is required if the Frigate camera name is missing, or
|
||||||
// if the entity requires automatic resolution of motion/occupancy sensors.
|
// if the entity requires automatic resolution of motion/occupancy sensors.
|
||||||
if (cameraEntity && (!hasCameraName || hasAutoTriggers)) {
|
if (cameraEntity && (!hasCameraName || hasAutoTriggers)) {
|
||||||
try {
|
entity = await entityRegistryManager.getEntity(hass, cameraEntity);
|
||||||
entity = await entityRegistryManager.getEntity(hass, cameraEntity);
|
if (!entity) {
|
||||||
} catch (e) {
|
|
||||||
throw new CameraInitializationError(localize('error.no_camera_entity'), config);
|
throw new CameraInitializationError(localize('error.no_camera_entity'), config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
runWhenIdleIfSupported,
|
runWhenIdleIfSupported,
|
||||||
} from '../../utils/basic';
|
} from '../../utils/basic';
|
||||||
import { getEntityTitle } from '../../utils/ha';
|
import { getEntityTitle } from '../../utils/ha';
|
||||||
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../../utils/ha/registry/entity';
|
||||||
import { ViewMedia } from '../../view/media';
|
import { ViewMedia } from '../../view/media';
|
||||||
import { ViewMediaClassifier } from '../../view/media-classifier';
|
import { ViewMediaClassifier } from '../../view/media-classifier';
|
||||||
import { RecordingSegmentsCache, RequestCache } from '../cache';
|
import { RecordingSegmentsCache, RequestCache } from '../cache';
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import { LovelaceCardEditor } from '@dermotduffy/custom-card-helpers';
|
|||||||
import { ReactiveController } from 'lit';
|
import { ReactiveController } from 'lit';
|
||||||
import { CameraManager } from '../camera-manager/manager';
|
import { CameraManager } from '../camera-manager/manager';
|
||||||
import { FrigateCardConfig } from '../config/types';
|
import { FrigateCardConfig } from '../config/types';
|
||||||
import { EntityRegistryManager } from '../utils/ha/entity-registry';
|
import {
|
||||||
import { EntityCache } from '../utils/ha/entity-registry/cache';
|
createEntityRegistryCache,
|
||||||
|
EntityRegistryManager,
|
||||||
|
} from '../utils/ha/registry/entity';
|
||||||
import { ResolvedMediaCache } from '../utils/ha/resolved-media';
|
import { ResolvedMediaCache } from '../utils/ha/resolved-media';
|
||||||
import { ActionsManager } from './actions/actions-manager';
|
import { ActionsManager } from './actions/actions-manager';
|
||||||
import { DefaultManager } from './default-manager';
|
|
||||||
import { AutomationsManager } from './automations-manager';
|
import { AutomationsManager } from './automations-manager';
|
||||||
import { CameraURLManager } from './camera-url-manager';
|
import { CameraURLManager } from './camera-url-manager';
|
||||||
import {
|
import {
|
||||||
@@ -17,12 +18,14 @@ import {
|
|||||||
} from './card-element-manager';
|
} from './card-element-manager';
|
||||||
import { ConditionsManager, ConditionsManagerListener } from './conditions-manager';
|
import { ConditionsManager, ConditionsManagerListener } from './conditions-manager';
|
||||||
import { ConfigManager } from './config/config-manager';
|
import { ConfigManager } from './config/config-manager';
|
||||||
|
import { DefaultManager } from './default-manager';
|
||||||
import { DownloadManager } from './download-manager';
|
import { DownloadManager } from './download-manager';
|
||||||
import { ExpandManager } from './expand-manager';
|
import { ExpandManager } from './expand-manager';
|
||||||
import { FullscreenManager } from './fullscreen-manager';
|
import { FullscreenManager } from './fullscreen-manager';
|
||||||
import { HASSManager } from './hass/hass-manager';
|
import { HASSManager } from './hass/hass-manager';
|
||||||
import { InitializationManager } from './initialization-manager';
|
import { InitializationManager } from './initialization-manager';
|
||||||
import { InteractionManager } from './interaction-manager';
|
import { InteractionManager } from './interaction-manager';
|
||||||
|
import { KeyboardStateManager } from './keyboard-state-manager';
|
||||||
import { MediaLoadedInfoManager } from './media-info-manager';
|
import { MediaLoadedInfoManager } from './media-info-manager';
|
||||||
import { MediaPlayerManager } from './media-player-manager';
|
import { MediaPlayerManager } from './media-player-manager';
|
||||||
import { MessageManager } from './message-manager';
|
import { MessageManager } from './message-manager';
|
||||||
@@ -57,7 +60,6 @@ import {
|
|||||||
CardViewAPI,
|
CardViewAPI,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { ViewManager } from './view/view-manager';
|
import { ViewManager } from './view/view-manager';
|
||||||
import { KeyboardStateManager } from './keyboard-state-manager';
|
|
||||||
|
|
||||||
export class CardController
|
export class CardController
|
||||||
implements
|
implements
|
||||||
@@ -88,7 +90,9 @@ export class CardController
|
|||||||
{
|
{
|
||||||
// These properties may be used in the construction of 'managers' (and should
|
// These properties may be used in the construction of 'managers' (and should
|
||||||
// be created first).
|
// be created first).
|
||||||
protected _entityRegistryManager = new EntityRegistryManager(new EntityCache());
|
protected _entityRegistryManager = new EntityRegistryManager(
|
||||||
|
createEntityRegistryCache(),
|
||||||
|
);
|
||||||
protected _resolvedMediaCache = new ResolvedMediaCache();
|
protected _resolvedMediaCache = new ResolvedMediaCache();
|
||||||
|
|
||||||
protected _actionsManager = new ActionsManager(this);
|
protected _actionsManager = new ActionsManager(this);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { CameraConfig, FrigateCardConfig } from '../config/types';
|
|||||||
import { MEDIA_PLAYER_SUPPORT_BROWSE_MEDIA } from '../const';
|
import { MEDIA_PLAYER_SUPPORT_BROWSE_MEDIA } from '../const';
|
||||||
import { localize } from '../localize/localize';
|
import { localize } from '../localize/localize';
|
||||||
import { errorToConsole } from '../utils/basic';
|
import { errorToConsole } from '../utils/basic';
|
||||||
import { Entity } from '../utils/ha/entity-registry/types';
|
import { Entity } from '../utils/ha/registry/entity/types';
|
||||||
import { supportsFeature } from '../utils/ha/update';
|
import { supportsFeature } from '../utils/ha/update';
|
||||||
import { ViewMedia } from '../view/media';
|
import { ViewMedia } from '../view/media';
|
||||||
import { ViewMediaClassifier } from '../view/media-classifier';
|
import { ViewMediaClassifier } from '../view/media-classifier';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CameraManager } from '../camera-manager/manager';
|
import type { CameraManager } from '../camera-manager/manager';
|
||||||
import type { Automation } from '../config/types';
|
import type { Automation } from '../config/types';
|
||||||
import type { EntityRegistryManager } from '../utils/ha/entity-registry';
|
import type { EntityRegistryManager } from '../utils/ha/registry/entity';
|
||||||
import type { ResolvedMediaCache } from '../utils/ha/resolved-media';
|
import type { ResolvedMediaCache } from '../utils/ha/resolved-media';
|
||||||
import type { ActionsManager } from './actions/actions-manager';
|
import type { ActionsManager } from './actions/actions-manager';
|
||||||
import type { AutomationsManager } from './automations-manager';
|
import type { AutomationsManager } from './automations-manager';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { MenuController } from '../components-lib/menu-controller.js';
|
|||||||
import type { MenuConfig, MenuItem } from '../config/types.js';
|
import type { MenuConfig, MenuItem } from '../config/types.js';
|
||||||
import menuStyle from '../scss/menu.scss';
|
import menuStyle from '../scss/menu.scss';
|
||||||
import { frigateCardHasAction } from '../utils/action.js';
|
import { frigateCardHasAction } from '../utils/action.js';
|
||||||
import { EntityRegistryManager } from '../utils/ha/entity-registry/index.js';
|
import { EntityRegistryManager } from '../utils/ha/registry/entity/index.js';
|
||||||
import './submenu.js';
|
import './submenu.js';
|
||||||
|
|
||||||
@customElement('frigate-card-menu')
|
@customElement('frigate-card-menu')
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
stopEventFromActivatingCardWideActions,
|
stopEventFromActivatingCardWideActions,
|
||||||
} from '../utils/action.js';
|
} from '../utils/action.js';
|
||||||
import { isHassDifferent, refreshDynamicStateParameters } from '../utils/ha';
|
import { isHassDifferent, refreshDynamicStateParameters } from '../utils/ha';
|
||||||
import { EntityRegistryManager } from '../utils/ha/entity-registry/index.js';
|
|
||||||
import { getEntityStateTranslation } from '../utils/ha/entity-state-translation.js';
|
import { getEntityStateTranslation } from '../utils/ha/entity-state-translation.js';
|
||||||
|
import { EntityRegistryManager } from '../utils/ha/registry/entity/index.js';
|
||||||
import { domainIcon } from '../utils/icons/domain-icon.js';
|
import { domainIcon } from '../utils/icons/domain-icon.js';
|
||||||
|
|
||||||
@customElement('frigate-card-submenu')
|
@customElement('frigate-card-submenu')
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
|||||||
import pkg from '../../package.json';
|
import pkg from '../../package.json';
|
||||||
import { RawFrigateCardConfig } from '../config/types';
|
import { RawFrigateCardConfig } from '../config/types';
|
||||||
import { getLanguage } from '../localize/localize';
|
import { getLanguage } from '../localize/localize';
|
||||||
import { DeviceList, getAllDevices } from './ha/device-registry';
|
import { getAllDevices } from './ha/registry/device';
|
||||||
|
import { DeviceList } from './ha/registry/device/types';
|
||||||
|
|
||||||
type FrigateVersions = Record<string, string>;
|
type FrigateVersions = Record<string, string>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { computeDomain, HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
import { computeDomain, HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { HassEntity } from 'home-assistant-js-websocket';
|
import { HassEntity } from 'home-assistant-js-websocket';
|
||||||
import { Entity } from './entity-registry/types';
|
import { Entity } from './registry/entity/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the translation of an entity state. Inspired by:
|
* Get the translation of an entity state. Inspired by:
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Entity } from './types.js';
|
export class RegistryCache<T> {
|
||||||
|
protected _cache: Map<string, T> = new Map();
|
||||||
|
protected _keyCallback: (_data: T) => string;
|
||||||
|
|
||||||
export class EntityCache {
|
constructor(keyCallback: (_data: T) => string) {
|
||||||
protected _cache: Map<string, Entity> = new Map();
|
this._keyCallback = keyCallback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the cache has a given entity_id.
|
* Determine if the cache has a given entity_id.
|
||||||
@@ -17,11 +20,7 @@ export class EntityCache {
|
|||||||
* @param func A callback function that returns a boolean.
|
* @param func A callback function that returns a boolean.
|
||||||
* @returns The first matching value.
|
* @returns The first matching value.
|
||||||
*/
|
*/
|
||||||
// public getFirstMatch(func: (arg: T) => boolean): T | null {
|
public getMatches(func: (arg: T) => boolean): T[] {
|
||||||
// return [...this._cache.values()].find(func) ?? null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
public getMatches(func: (arg: Entity) => boolean): Entity[] {
|
|
||||||
return [...this._cache.values()].filter(func);
|
return [...this._cache.values()].filter(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,16 +29,16 @@ export class EntityCache {
|
|||||||
* @param id The entity id.
|
* @param id The entity id.
|
||||||
* @returns The entity for this id.
|
* @returns The entity for this id.
|
||||||
*/
|
*/
|
||||||
public get(id: string): Entity | undefined {
|
public get(id: string): T | null {
|
||||||
return this._cache.get(id);
|
return this._cache.get(id) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a given entity to the cache.
|
* Add a given entity to the cache.
|
||||||
* @param input The entity.
|
* @param input The entity.
|
||||||
*/
|
*/
|
||||||
public set(input: Entity | Entity[]): void {
|
public add(input: T | T[]): void {
|
||||||
const _set = (entity: Entity) => this._cache.set(entity.entity_id, entity);
|
const _set = (arg: T) => this._cache.set(this._keyCallback(arg), arg);
|
||||||
|
|
||||||
if (Array.isArray(input)) {
|
if (Array.isArray(input)) {
|
||||||
input.forEach(_set);
|
input.forEach(_set);
|
||||||
@@ -1,14 +1,6 @@
|
|||||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { z } from 'zod';
|
import { homeAssistantWSRequest } from '../..';
|
||||||
import { homeAssistantWSRequest } from '.';
|
import { DeviceList, deviceListSchema } from './types';
|
||||||
|
|
||||||
const deviceSchema = z.object({
|
|
||||||
model: z.string().nullable(),
|
|
||||||
config_entries: z.string().array(),
|
|
||||||
manufacturer: z.string().nullable(),
|
|
||||||
});
|
|
||||||
const deviceListSchema = deviceSchema.array();
|
|
||||||
export type DeviceList = z.infer<typeof deviceListSchema>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of all entities from the entity registry. May throw.
|
* Get a list of all entities from the entity registry. May throw.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const deviceSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
model: z.string().nullable(),
|
||||||
|
config_entries: z.string().array(),
|
||||||
|
manufacturer: z.string().nullable(),
|
||||||
|
});
|
||||||
|
export type Device = z.infer<typeof deviceSchema>;
|
||||||
|
|
||||||
|
export const deviceListSchema = deviceSchema.array();
|
||||||
|
export type DeviceList = z.infer<typeof deviceListSchema>;
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||||
import { homeAssistantWSRequest } from '..';
|
import { homeAssistantWSRequest } from '../..';
|
||||||
import { EntityCache } from './cache';
|
|
||||||
import { Entity, EntityList, entitySchema, entityListSchema } from './types.js';
|
import { Entity, EntityList, entitySchema, entityListSchema } from './types.js';
|
||||||
|
import { RegistryCache } from '../cache';
|
||||||
|
|
||||||
|
export const createEntityRegistryCache = (): RegistryCache<Entity> => {
|
||||||
|
return new RegistryCache<Entity>((entity) => entity.entity_id);
|
||||||
|
};
|
||||||
|
|
||||||
// This class manages interactions with entities, caching results and fetching
|
// This class manages interactions with entities, caching results and fetching
|
||||||
// as necessary. Some calls require every entity to be fetched, which may be
|
// as necessary. Some calls require every entity to be fetched, which may be
|
||||||
// non-trivial in size (after which it is cached forever).
|
// non-trivial in size (after which they are cached forever).
|
||||||
|
|
||||||
export class EntityRegistryManager {
|
export class EntityRegistryManager {
|
||||||
protected _cache: EntityCache;
|
protected _cache: RegistryCache<Entity>;
|
||||||
protected _fetchedEntityList = false;
|
protected _fetchedEntityList = false;
|
||||||
|
|
||||||
constructor(cache: EntityCache) {
|
constructor(cache: RegistryCache<Entity>) {
|
||||||
this._cache = cache;
|
this._cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,11 +25,16 @@ export class EntityRegistryManager {
|
|||||||
return cachedEntity;
|
return cachedEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entity = await homeAssistantWSRequest<Entity>(hass, entitySchema, {
|
let entity: Entity | null = null;
|
||||||
type: 'config/entity_registry/get',
|
try {
|
||||||
entity_id: entityID,
|
entity = await homeAssistantWSRequest<Entity>(hass, entitySchema, {
|
||||||
});
|
type: 'config/entity_registry/get',
|
||||||
this._cache.set(entity);
|
entity_id: entityID,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
this._cache.add(entity);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,15 +52,11 @@ export class EntityRegistryManager {
|
|||||||
): Promise<Map<string, Entity>> {
|
): Promise<Map<string, Entity>> {
|
||||||
const output: Map<string, Entity> = new Map();
|
const output: Map<string, Entity> = new Map();
|
||||||
const _storeEntity = async (entityID: string): Promise<void> => {
|
const _storeEntity = async (entityID: string): Promise<void> => {
|
||||||
let entity: Entity | null = null;
|
const entity = await this.getEntity(hass, entityID);
|
||||||
try {
|
|
||||||
entity = await this.getEntity(hass, entityID);
|
if (entity) {
|
||||||
} catch {
|
|
||||||
// When asked to fetch multiple entities, ignore missing entities (they
|
// When asked to fetch multiple entities, ignore missing entities (they
|
||||||
// will just not feature in the output).
|
// will just not feature in the output).
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (entity) {
|
|
||||||
output.set(entityID, entity);
|
output.set(entityID, entity);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -66,7 +71,7 @@ export class EntityRegistryManager {
|
|||||||
const entityList = await homeAssistantWSRequest<EntityList>(hass, entityListSchema, {
|
const entityList = await homeAssistantWSRequest<EntityList>(hass, entityListSchema, {
|
||||||
type: 'config/entity_registry/list',
|
type: 'config/entity_registry/list',
|
||||||
});
|
});
|
||||||
this._cache.set(entityList);
|
this._cache.add(entityList);
|
||||||
this._fetchedEntityList = true;
|
this._fetchedEntityList = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@ import { describe, expect, it } from 'vitest';
|
|||||||
import { mock } from 'vitest-mock-extended';
|
import { mock } from 'vitest-mock-extended';
|
||||||
import { BrowseMediaCamera } from '../../../src/camera-manager/browse-media/camera';
|
import { BrowseMediaCamera } from '../../../src/camera-manager/browse-media/camera';
|
||||||
import { CameraManagerEngine } from '../../../src/camera-manager/engine';
|
import { CameraManagerEngine } from '../../../src/camera-manager/engine';
|
||||||
import { EntityRegistryManager } from '../../../src/utils/ha/entity-registry';
|
|
||||||
import { Entity } from '../../../src/utils/ha/entity-registry/types';
|
|
||||||
import { createCameraConfig, createHASS } from '../../test-utils';
|
|
||||||
import { StateWatcherSubscriptionInterface } from '../../../src/card-controller/hass/state-watcher';
|
import { StateWatcherSubscriptionInterface } from '../../../src/card-controller/hass/state-watcher';
|
||||||
|
import { EntityRegistryManager } from '../../../src/utils/ha/registry/entity';
|
||||||
|
import { Entity } from '../../../src/utils/ha/registry/entity/types';
|
||||||
|
import { createCameraConfig, createHASS } from '../../test-utils';
|
||||||
|
|
||||||
describe('BrowseMediaCamera', () => {
|
describe('BrowseMediaCamera', () => {
|
||||||
describe('should initialize', () => {
|
describe('should initialize', () => {
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import { MotionEyeCameraManagerEngine } from '../../src/camera-manager/motioneye
|
|||||||
import { Engine } from '../../src/camera-manager/types.js';
|
import { Engine } from '../../src/camera-manager/types.js';
|
||||||
import { StateWatcherSubscriptionInterface } from '../../src/card-controller/hass/state-watcher.js';
|
import { StateWatcherSubscriptionInterface } from '../../src/card-controller/hass/state-watcher.js';
|
||||||
import { CardWideConfig } from '../../src/config/types.js';
|
import { CardWideConfig } from '../../src/config/types.js';
|
||||||
import { EntityRegistryManager } from '../../src/utils/ha/entity-registry';
|
import {
|
||||||
import { EntityCache } from '../../src/utils/ha/entity-registry/cache';
|
createEntityRegistryCache,
|
||||||
|
EntityRegistryManager,
|
||||||
|
} from '../../src/utils/ha/registry/entity/index.js';
|
||||||
import { ResolvedMediaCache } from '../../src/utils/ha/resolved-media';
|
import { ResolvedMediaCache } from '../../src/utils/ha/resolved-media';
|
||||||
import {
|
import {
|
||||||
createCameraConfig,
|
createCameraConfig,
|
||||||
@@ -25,7 +27,8 @@ const createFactory = (options?: {
|
|||||||
cardWideConfig?: CardWideConfig;
|
cardWideConfig?: CardWideConfig;
|
||||||
}): CameraManagerEngineFactory => {
|
}): CameraManagerEngineFactory => {
|
||||||
return new CameraManagerEngineFactory(
|
return new CameraManagerEngineFactory(
|
||||||
options?.entityRegistryManager ?? new EntityRegistryManager(new EntityCache()),
|
options?.entityRegistryManager ??
|
||||||
|
new EntityRegistryManager(createEntityRegistryCache()),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,7 +43,9 @@ describe('getEngineForCamera()', () => {
|
|||||||
|
|
||||||
it('from auto detection', async () => {
|
it('from auto detection', async () => {
|
||||||
const config = createCameraConfig({ engine: 'auto', camera_entity: 'camera.foo' });
|
const config = createCameraConfig({ engine: 'auto', camera_entity: 'camera.foo' });
|
||||||
const entityRegistryManager = new EntityRegistryManager(new EntityCache());
|
const entityRegistryManager = new EntityRegistryManager(
|
||||||
|
createEntityRegistryCache(),
|
||||||
|
);
|
||||||
|
|
||||||
entityRegistryManager.getEntity = vi
|
entityRegistryManager.getEntity = vi
|
||||||
.fn()
|
.fn()
|
||||||
@@ -75,7 +80,9 @@ describe('getEngineForCamera()', () => {
|
|||||||
|
|
||||||
it('from auto detection', async () => {
|
it('from auto detection', async () => {
|
||||||
const config = createCameraConfig({ engine: 'auto', camera_entity: 'camera.foo' });
|
const config = createCameraConfig({ engine: 'auto', camera_entity: 'camera.foo' });
|
||||||
const entityRegistryManager = new EntityRegistryManager(new EntityCache());
|
const entityRegistryManager = new EntityRegistryManager(
|
||||||
|
createEntityRegistryCache(),
|
||||||
|
);
|
||||||
|
|
||||||
entityRegistryManager.getEntity = vi
|
entityRegistryManager.getEntity = vi
|
||||||
.fn()
|
.fn()
|
||||||
@@ -101,7 +108,9 @@ describe('getEngineForCamera()', () => {
|
|||||||
|
|
||||||
it('from auto detection', async () => {
|
it('from auto detection', async () => {
|
||||||
const config = createCameraConfig({ engine: 'auto', camera_entity: 'camera.foo' });
|
const config = createCameraConfig({ engine: 'auto', camera_entity: 'camera.foo' });
|
||||||
const entityRegistryManager = new EntityRegistryManager(new EntityCache());
|
const entityRegistryManager = new EntityRegistryManager(
|
||||||
|
createEntityRegistryCache(),
|
||||||
|
);
|
||||||
|
|
||||||
entityRegistryManager.getEntity = vi
|
entityRegistryManager.getEntity = vi
|
||||||
.fn()
|
.fn()
|
||||||
@@ -121,9 +130,11 @@ describe('getEngineForCamera()', () => {
|
|||||||
engine: 'auto',
|
engine: 'auto',
|
||||||
webrtc_card: { entity: 'camera.foo' },
|
webrtc_card: { entity: 'camera.foo' },
|
||||||
});
|
});
|
||||||
const entityRegistryManager = new EntityRegistryManager(new EntityCache());
|
const entityRegistryManager = new EntityRegistryManager(
|
||||||
|
createEntityRegistryCache(),
|
||||||
|
);
|
||||||
|
|
||||||
entityRegistryManager.getEntity = vi.fn().mockRejectedValue(new Error());
|
entityRegistryManager.getEntity = vi.fn().mockResolvedValue(null);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await createFactory({
|
await createFactory({
|
||||||
@@ -137,6 +148,24 @@ describe('getEngineForCamera()', () => {
|
|||||||
).toBe(Engine.Generic);
|
).toBe(Engine.Generic);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('from entity not in registry and not in state', async () => {
|
||||||
|
const config = createCameraConfig({
|
||||||
|
engine: 'auto',
|
||||||
|
webrtc_card: { entity: 'camera.foo' },
|
||||||
|
});
|
||||||
|
const entityRegistryManager = new EntityRegistryManager(
|
||||||
|
createEntityRegistryCache(),
|
||||||
|
);
|
||||||
|
entityRegistryManager.getEntity = vi.fn().mockResolvedValue(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
async () =>
|
||||||
|
await createFactory({
|
||||||
|
entityRegistryManager: entityRegistryManager,
|
||||||
|
}).getEngineForCamera(createHASS(), config),
|
||||||
|
).rejects.toThrow(/Could not find camera entity/);
|
||||||
|
});
|
||||||
|
|
||||||
it('from webrtc-card url', async () => {
|
it('from webrtc-card url', async () => {
|
||||||
const config = createCameraConfig({
|
const config = createCameraConfig({
|
||||||
engine: 'auto',
|
engine: 'auto',
|
||||||
@@ -167,7 +196,7 @@ describe('getEngineForCamera()', () => {
|
|||||||
|
|
||||||
it('should throw error on invalid entity', async () => {
|
it('should throw error on invalid entity', async () => {
|
||||||
const config = createCameraConfig({ engine: 'auto', camera_entity: 'camera.foo' });
|
const config = createCameraConfig({ engine: 'auto', camera_entity: 'camera.foo' });
|
||||||
const entityRegistryManager = new EntityRegistryManager(new EntityCache());
|
const entityRegistryManager = new EntityRegistryManager(createEntityRegistryCache());
|
||||||
|
|
||||||
entityRegistryManager.getEntity = vi.fn().mockRejectedValue(new Error());
|
entityRegistryManager.getEntity = vi.fn().mockRejectedValue(new Error());
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { getPTZInfo } from '../../../src/camera-manager/frigate/requests';
|
|||||||
import { FrigateEventChange } from '../../../src/camera-manager/frigate/types';
|
import { FrigateEventChange } from '../../../src/camera-manager/frigate/types';
|
||||||
import { StateWatcher } from '../../../src/card-controller/hass/state-watcher';
|
import { StateWatcher } from '../../../src/card-controller/hass/state-watcher';
|
||||||
import { CameraTriggerEventType } from '../../../src/config/types';
|
import { CameraTriggerEventType } from '../../../src/config/types';
|
||||||
import { EntityRegistryManager } from '../../../src/utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../../../src/utils/ha/registry/entity';
|
||||||
import { Entity } from '../../../src/utils/ha/entity-registry/types';
|
import { Entity } from '../../../src/utils/ha/registry/entity/types';
|
||||||
import { createCameraConfig, createHASS, createRegistryEntity } from '../../test-utils';
|
import { createCameraConfig, createHASS, createRegistryEntity } from '../../test-utils';
|
||||||
|
|
||||||
vi.mock('../../../src/camera-manager/frigate/requests');
|
vi.mock('../../../src/camera-manager/frigate/requests');
|
||||||
@@ -54,7 +54,7 @@ describe('FrigateCamera', () => {
|
|||||||
mock<CameraManagerEngine>(),
|
mock<CameraManagerEngine>(),
|
||||||
);
|
);
|
||||||
const entityRegistryManager = mock<EntityRegistryManager>();
|
const entityRegistryManager = mock<EntityRegistryManager>();
|
||||||
entityRegistryManager.getEntity.mockRejectedValue(null);
|
entityRegistryManager.getEntity.mockResolvedValue(null);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
async () =>
|
async () =>
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import { Capabilities } from '../../src/camera-manager/capabilities.js';
|
|||||||
import { CameraManagerEngineFactory } from '../../src/camera-manager/engine-factory.js';
|
import { CameraManagerEngineFactory } from '../../src/camera-manager/engine-factory.js';
|
||||||
import { CameraManagerStore } from '../../src/camera-manager/store.js';
|
import { CameraManagerStore } from '../../src/camera-manager/store.js';
|
||||||
import { Engine } from '../../src/camera-manager/types.js';
|
import { Engine } from '../../src/camera-manager/types.js';
|
||||||
import { EntityRegistryManager } from '../../src/utils/ha/entity-registry/index.js';
|
import { StateWatcherSubscriptionInterface } from '../../src/card-controller/hass/state-watcher.js';
|
||||||
|
import { EntityRegistryManager } from '../../src/utils/ha/registry/entity/index.js';
|
||||||
import { ResolvedMediaCache } from '../../src/utils/ha/resolved-media.js';
|
import { ResolvedMediaCache } from '../../src/utils/ha/resolved-media.js';
|
||||||
import { TestViewMedia, createCameraConfig } from '../test-utils.js';
|
import { TestViewMedia, createCameraConfig } from '../test-utils.js';
|
||||||
import { StateWatcherSubscriptionInterface } from '../../src/card-controller/hass/state-watcher.js';
|
|
||||||
|
|
||||||
describe('CameraManagerStore', async () => {
|
describe('CameraManagerStore', async () => {
|
||||||
const configVisible = createCameraConfig({
|
const configVisible = createCameraConfig({
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { StyleManager } from '../../src/card-controller/style-manager';
|
|||||||
import { TriggersManager } from '../../src/card-controller/triggers-manager';
|
import { TriggersManager } from '../../src/card-controller/triggers-manager';
|
||||||
import { ViewManager } from '../../src/card-controller/view/view-manager';
|
import { ViewManager } from '../../src/card-controller/view/view-manager';
|
||||||
import { FrigateCardEditor } from '../../src/editor';
|
import { FrigateCardEditor } from '../../src/editor';
|
||||||
import { EntityRegistryManager } from '../../src/utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../../src/utils/ha/registry/entity';
|
||||||
import { ResolvedMediaCache } from '../../src/utils/ha/resolved-media';
|
import { ResolvedMediaCache } from '../../src/utils/ha/resolved-media';
|
||||||
|
|
||||||
vi.mock('../../src/camera-manager/manager');
|
vi.mock('../../src/camera-manager/manager');
|
||||||
@@ -55,7 +55,7 @@ vi.mock('../../src/card-controller/status-bar-item-manager');
|
|||||||
vi.mock('../../src/card-controller/style-manager');
|
vi.mock('../../src/card-controller/style-manager');
|
||||||
vi.mock('../../src/card-controller/triggers-manager');
|
vi.mock('../../src/card-controller/triggers-manager');
|
||||||
vi.mock('../../src/card-controller/view/view-manager');
|
vi.mock('../../src/card-controller/view/view-manager');
|
||||||
vi.mock('../../src/utils/ha/entity-registry');
|
vi.mock('../../src/utils/ha/registry/entity');
|
||||||
vi.mock('../../src/utils/ha/resolved-media');
|
vi.mock('../../src/utils/ha/resolved-media');
|
||||||
|
|
||||||
const createCardElement = (): CardHTMLElement => {
|
const createCardElement = (): CardHTMLElement => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { mock } from 'vitest-mock-extended';
|
|||||||
import { MediaPlayerManager } from '../../src/card-controller/media-player-manager';
|
import { MediaPlayerManager } from '../../src/card-controller/media-player-manager';
|
||||||
import { MEDIA_PLAYER_SUPPORT_BROWSE_MEDIA } from '../../src/const';
|
import { MEDIA_PLAYER_SUPPORT_BROWSE_MEDIA } from '../../src/const';
|
||||||
import { ExtendedHomeAssistant } from '../../src/types';
|
import { ExtendedHomeAssistant } from '../../src/types';
|
||||||
import { EntityRegistryManager } from '../../src/utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../../src/utils/ha/registry/entity';
|
||||||
import {
|
import {
|
||||||
createCameraConfig,
|
createCameraConfig,
|
||||||
createCameraManager,
|
createCameraManager,
|
||||||
|
|||||||
+2
-2
@@ -55,8 +55,8 @@ import {
|
|||||||
} from '../src/config/types';
|
} from '../src/config/types';
|
||||||
import { CapabilitiesRaw, ExtendedHomeAssistant, MediaLoadedInfo } from '../src/types';
|
import { CapabilitiesRaw, ExtendedHomeAssistant, MediaLoadedInfo } from '../src/types';
|
||||||
import { HassStateDifference } from '../src/utils/ha';
|
import { HassStateDifference } from '../src/utils/ha';
|
||||||
import { EntityRegistryManager } from '../src/utils/ha/entity-registry';
|
import { EntityRegistryManager } from '../src/utils/ha/registry/entity';
|
||||||
import { Entity } from '../src/utils/ha/entity-registry/types';
|
import { Entity } from '../src/utils/ha/registry/entity/types';
|
||||||
import { ViewMedia, ViewMediaType } from '../src/view/media';
|
import { ViewMedia, ViewMediaType } from '../src/view/media';
|
||||||
import { MediaQueriesResults } from '../src/view/media-queries-results';
|
import { MediaQueriesResults } from '../src/view/media-queries-results';
|
||||||
import { View, ViewParameters } from '../src/view/view';
|
import { View, ViewParameters } from '../src/view/view';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { HassConfig } from 'home-assistant-js-websocket';
|
|||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { getLanguage } from '../../src/localize/localize';
|
import { getLanguage } from '../../src/localize/localize';
|
||||||
import { getDiagnostics, getReleaseVersion } from '../../src/utils/diagnostics.js';
|
import { getDiagnostics, getReleaseVersion } from '../../src/utils/diagnostics.js';
|
||||||
import { getAllDevices } from '../../src/utils/ha/device-registry.js';
|
import { getAllDevices } from '../../src/utils/ha/registry/device/index.js';
|
||||||
import { createHASS } from '../test-utils';
|
import { createHASS } from '../test-utils';
|
||||||
|
|
||||||
vi.mock('../../package.json', () => ({
|
vi.mock('../../package.json', () => ({
|
||||||
@@ -14,7 +14,7 @@ vi.mock('../../package.json', () => ({
|
|||||||
}));
|
}));
|
||||||
vi.mock('../../src/utils/ha');
|
vi.mock('../../src/utils/ha');
|
||||||
vi.mock('../../src/localize/localize.js');
|
vi.mock('../../src/localize/localize.js');
|
||||||
vi.mock('../../src/utils/ha/device-registry');
|
vi.mock('../../src/utils/ha/registry/device/index.js');
|
||||||
|
|
||||||
describe('getReleaseVersion', () => {
|
describe('getReleaseVersion', () => {
|
||||||
it('should get release version', () => {
|
it('should get release version', () => {
|
||||||
@@ -37,6 +37,7 @@ describe('getDiagnostics', () => {
|
|||||||
|
|
||||||
vi.mocked(getAllDevices).mockResolvedValue([
|
vi.mocked(getAllDevices).mockResolvedValue([
|
||||||
{
|
{
|
||||||
|
id: 'id',
|
||||||
model: '4.0.0/0.13.0-aded314',
|
model: '4.0.0/0.13.0-aded314',
|
||||||
config_entries: [
|
config_entries: [
|
||||||
'ac4e79d258449a83bc0cf6d47a021c46',
|
'ac4e79d258449a83bc0cf6d47a021c46',
|
||||||
@@ -97,6 +98,7 @@ describe('getDiagnostics', () => {
|
|||||||
it('should fetch diagnostics without device model', async () => {
|
it('should fetch diagnostics without device model', async () => {
|
||||||
vi.mocked(getAllDevices).mockResolvedValue([
|
vi.mocked(getAllDevices).mockResolvedValue([
|
||||||
{
|
{
|
||||||
|
id: 'id',
|
||||||
model: null,
|
model: null,
|
||||||
config_entries: [
|
config_entries: [
|
||||||
'ac4e79d258449a83bc0cf6d47a021c46',
|
'ac4e79d258449a83bc0cf6d47a021c46',
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { RegistryCache } from '../../../../src/utils/ha/registry/cache';
|
||||||
|
|
||||||
|
interface TestCacheValue {
|
||||||
|
id: string;
|
||||||
|
val?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('RegistryCache', () => {
|
||||||
|
describe('has', () => {
|
||||||
|
it('positive', () => {
|
||||||
|
const cache = new RegistryCache<TestCacheValue>((arg) => arg.id);
|
||||||
|
cache.add({ id: 'test' });
|
||||||
|
expect(cache.has('test')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('negative', () => {
|
||||||
|
const cache = new RegistryCache<TestCacheValue>((arg) => arg.id);
|
||||||
|
cache.add({ id: 'test' });
|
||||||
|
expect(cache.has('absent')).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMatches', () => {
|
||||||
|
const cache = new RegistryCache<TestCacheValue>((arg) => arg.id);
|
||||||
|
cache.add([
|
||||||
|
{ id: 'test-1', val: 1 },
|
||||||
|
{ id: 'test-5', val: 5 },
|
||||||
|
{ id: 'test-8', val: 8 },
|
||||||
|
]);
|
||||||
|
expect(cache.getMatches((obj) => !!obj.val && obj.val >= 5)).toEqual([
|
||||||
|
{ id: 'test-5', val: 5 },
|
||||||
|
{ id: 'test-8', val: 8 },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('get', () => {
|
||||||
|
it('positive', () => {
|
||||||
|
const cache = new RegistryCache<TestCacheValue>((arg) => arg.id);
|
||||||
|
cache.add({ id: 'test', val: 42 });
|
||||||
|
expect(cache.get('test')).toEqual({ id: 'test', val: 42 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('negative', () => {
|
||||||
|
const cache = new RegistryCache<TestCacheValue>((arg) => arg.id);
|
||||||
|
expect(cache.get('test')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
import { homeAssistantWSRequest } from '../../../../../src/utils/ha';
|
||||||
|
import {
|
||||||
|
createEntityRegistryCache,
|
||||||
|
EntityRegistryManager,
|
||||||
|
} from '../../../../../src/utils/ha/registry/entity';
|
||||||
|
import { createHASS, createRegistryEntity } from '../../../../test-utils.js';
|
||||||
|
|
||||||
|
vi.mock('../../../../../src/utils/ha');
|
||||||
|
|
||||||
|
describe('EntityRegistryManager', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getEntity', () => {
|
||||||
|
it('should not fetch when cached', async () => {
|
||||||
|
const cache = createEntityRegistryCache();
|
||||||
|
const testEntity = createRegistryEntity({ entity_id: 'test' });
|
||||||
|
|
||||||
|
cache.add(testEntity);
|
||||||
|
|
||||||
|
const manager = new EntityRegistryManager(cache);
|
||||||
|
expect(await manager.getEntity(createHASS(), 'test')).toEqual(testEntity);
|
||||||
|
|
||||||
|
expect(homeAssistantWSRequest).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch and cache when not cached', async () => {
|
||||||
|
const testEntity = createRegistryEntity({ entity_id: 'test' });
|
||||||
|
|
||||||
|
const manager = new EntityRegistryManager(createEntityRegistryCache());
|
||||||
|
vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce(testEntity);
|
||||||
|
|
||||||
|
expect(await manager.getEntity(createHASS(), 'test')).toEqual(testEntity);
|
||||||
|
expect(homeAssistantWSRequest).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
expect(await manager.getEntity(createHASS(), 'test')).toEqual(testEntity);
|
||||||
|
expect(homeAssistantWSRequest).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null when entity does not exist', async () => {
|
||||||
|
vi.mocked(homeAssistantWSRequest).mockRejectedValueOnce(new Error('Not found'));
|
||||||
|
|
||||||
|
const manager = new EntityRegistryManager(createEntityRegistryCache());
|
||||||
|
expect(await manager.getEntity(createHASS(), 'missing')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getEntities', () => {
|
||||||
|
const cachedEntity = createRegistryEntity({ entity_id: 'cached' });
|
||||||
|
const notCachedEntity = createRegistryEntity({ entity_id: 'not-cached' });
|
||||||
|
|
||||||
|
const cache = createEntityRegistryCache();
|
||||||
|
cache.add(cachedEntity);
|
||||||
|
|
||||||
|
const manager = new EntityRegistryManager(cache);
|
||||||
|
vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce(notCachedEntity);
|
||||||
|
vi.mocked(homeAssistantWSRequest).mockRejectedValueOnce(new Error('Not found'));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
manager.getEntities(createHASS(), ['cached', 'not-cached', 'missing']),
|
||||||
|
).resolves.toEqual(
|
||||||
|
new Map([
|
||||||
|
['cached', cachedEntity],
|
||||||
|
['not-cached', notCachedEntity],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetchEntityList', async () => {
|
||||||
|
const hass = createHASS();
|
||||||
|
const entity = createRegistryEntity({ entity_id: 'cached' });
|
||||||
|
vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce([entity]);
|
||||||
|
|
||||||
|
const manager = new EntityRegistryManager(createEntityRegistryCache());
|
||||||
|
|
||||||
|
await manager.fetchEntityList(hass);
|
||||||
|
|
||||||
|
expect(homeAssistantWSRequest).toBeCalledTimes(1);
|
||||||
|
expect(homeAssistantWSRequest).toBeCalledWith(expect.anything(), expect.anything(), {
|
||||||
|
type: 'config/entity_registry/list',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await manager.getEntity(hass, 'cached')).toEqual(entity);
|
||||||
|
expect(homeAssistantWSRequest).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
await manager.fetchEntityList(hass);
|
||||||
|
expect(homeAssistantWSRequest).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMatchingEntities', async () => {
|
||||||
|
const matchingEntity = createRegistryEntity({ entity_id: 'matching' });
|
||||||
|
const notMatchingEntity = createRegistryEntity({ entity_id: 'not-matching' });
|
||||||
|
const hass = createHASS();
|
||||||
|
|
||||||
|
vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce([
|
||||||
|
matchingEntity,
|
||||||
|
notMatchingEntity,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const manager = new EntityRegistryManager(createEntityRegistryCache());
|
||||||
|
expect(
|
||||||
|
await manager.getMatchingEntities(
|
||||||
|
hass,
|
||||||
|
(entity) => entity.entity_id == 'matching',
|
||||||
|
),
|
||||||
|
).toEqual([matchingEntity]);
|
||||||
|
});
|
||||||
|
});
|
||||||
+1
-1
@@ -25,7 +25,7 @@ const FULL_COVERAGE_FILES_RELATIVE = [
|
|||||||
'utils/download.ts',
|
'utils/download.ts',
|
||||||
'utils/embla/**/*.ts',
|
'utils/embla/**/*.ts',
|
||||||
'utils/endpoint.ts',
|
'utils/endpoint.ts',
|
||||||
'utils/ha/entity-registry/types.ts',
|
'utils/ha/registry/entity/**/*.ts',
|
||||||
'utils/ha/types.ts',
|
'utils/ha/types.ts',
|
||||||
'utils/initializer.ts',
|
'utils/initializer.ts',
|
||||||
'utils/interaction-mode.ts',
|
'utils/interaction-mode.ts',
|
||||||
|
|||||||
Reference in New Issue
Block a user