Rename several view variables for clarity.
This commit is contained in:
@@ -3,6 +3,7 @@ import { CameraConfig } from '../config/types';
|
||||
import { localize } from '../localize/localize';
|
||||
import { allPromises } from '../utils/basic';
|
||||
import {
|
||||
DestroyCallback,
|
||||
isTriggeredState,
|
||||
parseStateChangeTrigger,
|
||||
subscribeToTrigger,
|
||||
@@ -13,8 +14,6 @@ import { CameraManagerEngine } from './engine';
|
||||
import { CameraNoIDError } from './error';
|
||||
import { CameraEventCallback } from './types';
|
||||
|
||||
type DestroyCallback = () => Promise<void>;
|
||||
|
||||
export class Camera {
|
||||
protected _config: CameraConfig;
|
||||
protected _engine: CameraManagerEngine;
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Timer } from '../utils/timer';
|
||||
import { CardAutoRefreshAPI } from './types';
|
||||
|
||||
export class AutoUpdateManager {
|
||||
protected _timer = new Timer();
|
||||
protected _api: CardAutoRefreshAPI;
|
||||
|
||||
constructor(api: CardAutoRefreshAPI) {
|
||||
this._api = api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the update timer to trigger an update refresh every
|
||||
* `view.update_seconds`.
|
||||
*/
|
||||
public startDefaultViewTimer(): void {
|
||||
this._timer.stop();
|
||||
const updateSeconds = this._api.getConfigManager().getConfig()?.view.update_seconds;
|
||||
if (updateSeconds) {
|
||||
this._timer.start(updateSeconds, () => {
|
||||
if (this._isAutomatedUpdateAllowed()) {
|
||||
this._api.getViewManager().setViewDefault();
|
||||
} else {
|
||||
// Not allowed to update this time around, but try again at the next
|
||||
// interval.
|
||||
this.startDefaultViewTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected _isAutomatedUpdateAllowed(): boolean {
|
||||
const triggers = this._api.getTriggersManager();
|
||||
const config = this._api.getConfigManager().getConfig();
|
||||
const interactionManager = this._api.getInteractionManager();
|
||||
|
||||
return (
|
||||
!triggers.isTriggered() &&
|
||||
(config?.view.update_force || !interactionManager.hasInteraction())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ export class CardElementManager {
|
||||
this._api.getMediaLoadedInfoManager().initialize();
|
||||
this._api.getMicrophoneManager().initialize();
|
||||
this._api.getKeyboardStateManager().initialize();
|
||||
this._api.getDefaultManager().initialize();
|
||||
|
||||
// Whether or not the card is in panel mode on the dashboard.
|
||||
setOrRemoveAttribute(this._element, isCardInPanel(this._element), 'panel');
|
||||
@@ -123,6 +124,7 @@ export class CardElementManager {
|
||||
this._api.getFullscreenManager().disconnect();
|
||||
this._api.getKeyboardStateManager().uninitialize();
|
||||
this._api.getActionsManager().uninitialize();
|
||||
this._api.getDefaultManager().uninitialize();
|
||||
|
||||
// Uninitialize cameras to cause them to reinitialize on
|
||||
// reconnection, to ensure the state subscription/unsubscription works
|
||||
|
||||
@@ -142,6 +142,18 @@ export class ConfigManager {
|
||||
.uninitialize(InitializationAspect.MICROPHONE_CONNECT);
|
||||
}
|
||||
|
||||
if (
|
||||
previousConfig &&
|
||||
!isEqual(
|
||||
previousConfig?.view.default_reset,
|
||||
this._overriddenConfig?.view.default_reset,
|
||||
)
|
||||
) {
|
||||
this._api
|
||||
.getInitializationManager()
|
||||
.uninitialize(InitializationAspect.DEFAULT_RESET);
|
||||
}
|
||||
|
||||
this._api.getCardElementManager().update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { EntityRegistryManager } from '../utils/ha/entity-registry';
|
||||
import { EntityCache } from '../utils/ha/entity-registry/cache';
|
||||
import { ResolvedMediaCache } from '../utils/ha/resolved-media';
|
||||
import { ActionsManager } from './actions/actions-manager';
|
||||
import { AutoUpdateManager } from './auto-update-manager';
|
||||
import { DefaultManager } from './default-manager';
|
||||
import { AutomationsManager } from './automations-manager';
|
||||
import { CameraURLManager } from './camera-url-manager';
|
||||
import {
|
||||
@@ -32,12 +32,12 @@ import { StyleManager } from './style-manager';
|
||||
import { TriggersManager } from './triggers-manager';
|
||||
import {
|
||||
CardActionsManagerAPI,
|
||||
CardAutoRefreshAPI,
|
||||
CardAutomationsAPI,
|
||||
CardCameraAPI,
|
||||
CardCameraURLAPI,
|
||||
CardConditionAPI,
|
||||
CardConfigAPI,
|
||||
CardDefaultManagerAPI,
|
||||
CardDownloadAPI,
|
||||
CardElementAPI,
|
||||
CardExpandAPI,
|
||||
@@ -62,11 +62,11 @@ export class CardController
|
||||
implements
|
||||
CardActionsManagerAPI,
|
||||
CardAutomationsAPI,
|
||||
CardAutoRefreshAPI,
|
||||
CardCameraAPI,
|
||||
CardCameraURLAPI,
|
||||
CardConditionAPI,
|
||||
CardConfigAPI,
|
||||
CardDefaultManagerAPI,
|
||||
CardDownloadAPI,
|
||||
CardElementAPI,
|
||||
CardExpandAPI,
|
||||
@@ -92,12 +92,12 @@ export class CardController
|
||||
|
||||
protected _actionsManager = new ActionsManager(this);
|
||||
protected _automationsManager = new AutomationsManager(this);
|
||||
protected _autoUpdateManager = new AutoUpdateManager(this);
|
||||
protected _cameraManager = new CameraManager(this);
|
||||
protected _cameraURLManager = new CameraURLManager(this);
|
||||
protected _cardElementManager: CardElementManager;
|
||||
protected _conditionsManager: ConditionsManager;
|
||||
protected _configManager = new ConfigManager(this);
|
||||
protected _defaultManager = new DefaultManager(this);
|
||||
protected _downloadManager = new DownloadManager(this);
|
||||
protected _expandManager = new ExpandManager(this);
|
||||
protected _fullscreenManager = new FullscreenManager(this);
|
||||
@@ -143,10 +143,6 @@ export class CardController
|
||||
return this._automationsManager;
|
||||
}
|
||||
|
||||
public getAutoUpdateManager(): AutoUpdateManager {
|
||||
return this._autoUpdateManager;
|
||||
}
|
||||
|
||||
public getCameraManager(): CameraManager {
|
||||
return this._cameraManager;
|
||||
}
|
||||
@@ -171,6 +167,11 @@ export class CardController
|
||||
public getConfigManager(): ConfigManager {
|
||||
return this._configManager;
|
||||
}
|
||||
|
||||
public getDefaultManager(): DefaultManager {
|
||||
return this._defaultManager;
|
||||
}
|
||||
|
||||
public getDownloadManager(): DownloadManager {
|
||||
return this._downloadManager;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import PQueue from 'p-queue';
|
||||
import { DestroyCallback, subscribeToTrigger } from '../utils/ha';
|
||||
import { isActionAllowedBasedOnInteractionState } from '../utils/interaction-mode';
|
||||
import { Timer } from '../utils/timer';
|
||||
import { CardDefaultManagerAPI } from './types';
|
||||
|
||||
/**
|
||||
* Manages automated resetting to the default view.
|
||||
*/
|
||||
export class DefaultManager {
|
||||
protected _timer = new Timer();
|
||||
protected _api: CardDefaultManagerAPI;
|
||||
protected _unsubscribeCallback: DestroyCallback | null = null;
|
||||
protected _initializationLimit = new PQueue({ concurrency: 1 });
|
||||
|
||||
constructor(api: CardDefaultManagerAPI) {
|
||||
this._api = api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the default manager. Requires both hass and configuration to be
|
||||
* effective (so cannot be called from just the configuration manager, as hass
|
||||
* will not be available yet)
|
||||
*/
|
||||
public async initialize(): Promise<boolean> {
|
||||
const result = await this._initializationLimit.add(() => this._reconfigure());
|
||||
this._startTimer();
|
||||
return !!result;
|
||||
}
|
||||
|
||||
public uninitialize(): void {
|
||||
this._timer.stop();
|
||||
this._unsubscribeCallback?.();
|
||||
this._unsubscribeCallback = null;
|
||||
}
|
||||
|
||||
protected async _reconfigure(): Promise<boolean> {
|
||||
const hass = this._api.getHASSManager().getHASS();
|
||||
const config = this._api.getConfigManager().getConfig()?.view.default_reset;
|
||||
if (!hass || !config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._unsubscribeCallback) {
|
||||
await this._unsubscribeCallback();
|
||||
}
|
||||
|
||||
this._unsubscribeCallback = await subscribeToTrigger(
|
||||
hass,
|
||||
() => this._setToDefaultIfAllowed(),
|
||||
{
|
||||
entityID: config.entities,
|
||||
platform: 'state',
|
||||
stateOnly: true,
|
||||
},
|
||||
);
|
||||
|
||||
// If the timer is running, restart it with the newly configured timer.
|
||||
if (this._timer.isRunning()) {
|
||||
this._timer.stop();
|
||||
this._startTimer();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected _setToDefaultIfAllowed(): void {
|
||||
if (this._isAutomatedUpdateAllowed()) {
|
||||
this._api.getViewManager().setViewDefault();
|
||||
}
|
||||
}
|
||||
|
||||
protected _isAutomatedUpdateAllowed(): boolean {
|
||||
const interactionMode = this._api.getConfigManager().getConfig()?.view
|
||||
.default_reset.interaction_mode;
|
||||
return (
|
||||
!!interactionMode &&
|
||||
isActionAllowedBasedOnInteractionState(
|
||||
interactionMode,
|
||||
this._api.getInteractionManager().hasInteraction(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected _startTimer(): void {
|
||||
const timerSeconds = this._api.getConfigManager().getConfig()?.view
|
||||
.default_reset.every_seconds;
|
||||
if (timerSeconds) {
|
||||
this._timer.startRepeated(timerSeconds, () => this._setToDefaultIfAllowed());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,23 +37,6 @@ export class HASSManager {
|
||||
this._hass = hass;
|
||||
|
||||
if (
|
||||
// Home Assistant pumps a lot of updates through. Re-rendering the card is
|
||||
// necessary at times (e.g. to update the 'clip' view as new clips
|
||||
// arrive), but also is a jarring experience for the user (e.g. if they
|
||||
// are browsing the mini-gallery). Do not allow re-rendering from a Home
|
||||
// Assistant update if there's been recent interaction (e.g. clicks on the
|
||||
// card) or if there is media active playing.
|
||||
this._isAutomatedViewUpdateAllowed() &&
|
||||
isHassDifferent(
|
||||
this._hass,
|
||||
oldHass,
|
||||
this._api.getConfigManager().getConfig()?.view.update_entities ?? [],
|
||||
)
|
||||
) {
|
||||
// If entities being monitored have changed then reset the view to the
|
||||
// default.
|
||||
this._api.getViewManager().setViewDefault();
|
||||
} else if (
|
||||
isHassDifferent(this._hass, oldHass, [
|
||||
...(this._api.getConfigManager().getConfig()?.view.render_entities ?? []),
|
||||
|
||||
@@ -75,11 +58,4 @@ export class HASSManager {
|
||||
// Dark mode may depend on HASS.
|
||||
this._api.getStyleManager().setLightOrDarkMode();
|
||||
}
|
||||
|
||||
protected _isAutomatedViewUpdateAllowed(): boolean {
|
||||
return (
|
||||
this._api.getConfigManager().getConfig()?.view.update_force ||
|
||||
!this._api.getInteractionManager().hasInteraction()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export enum InitializationAspect {
|
||||
MEDIA_PLAYERS = 'media-players',
|
||||
CAMERAS = 'cameras',
|
||||
MICROPHONE_CONNECT = 'microphone-connect',
|
||||
DEFAULT_RESET = 'default-reset',
|
||||
}
|
||||
|
||||
export class InitializationManager {
|
||||
@@ -125,6 +126,7 @@ export class InitializationManager {
|
||||
|
||||
if (
|
||||
this._initializer.isInitializedMultiple([
|
||||
InitializationAspect.DEFAULT_RESET,
|
||||
...(config.menu.buttons.media_player.enabled
|
||||
? [InitializationAspect.MEDIA_PLAYERS]
|
||||
: []),
|
||||
@@ -135,6 +137,8 @@ export class InitializationManager {
|
||||
|
||||
if (
|
||||
!(await this._initializer.initializeMultipleIfNecessary({
|
||||
[InitializationAspect.DEFAULT_RESET]: async () =>
|
||||
await this._api.getDefaultManager().initialize(),
|
||||
...(config.menu.buttons.media_player.enabled && {
|
||||
[InitializationAspect.MEDIA_PLAYERS]: async () =>
|
||||
await this._api.getMediaPlayerManager().initialize(),
|
||||
|
||||
@@ -41,7 +41,10 @@ export class InteractionManager {
|
||||
this._api.getConditionsManager().setState({ interaction: false });
|
||||
|
||||
if (!this._api.getTriggersManager().isTriggered()) {
|
||||
if (this._api.getConfigManager().getConfig()?.view.reset_after_interaction) {
|
||||
if (
|
||||
this._api.getConfigManager().getConfig()?.view.default_reset
|
||||
.after_interaction
|
||||
) {
|
||||
this._api.getViewManager().setViewDefault();
|
||||
}
|
||||
this._api.getStyleManager().setLightOrDarkMode();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CardKeyboardStateAPI, KeysState } from './types';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
|
||||
export class KeyboardStateManager {
|
||||
protected _api: CardKeyboardStateAPI;
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { ConditionsManager } from './conditions-manager';
|
||||
import type { EntityRegistryManager } from '../utils/ha/entity-registry';
|
||||
import type { ResolvedMediaCache } from '../utils/ha/resolved-media';
|
||||
import type { ActionsManager } from './actions/actions-manager';
|
||||
import type { AutoUpdateManager } from './auto-update-manager';
|
||||
import type { DefaultManager } from './default-manager';
|
||||
import type { AutomationsManager } from './automations-manager';
|
||||
import type { CameraURLManager } from './camera-url-manager';
|
||||
import type { CardElementManager } from './card-element-manager';
|
||||
@@ -60,13 +60,6 @@ export interface CardAutomationsAPI {
|
||||
getMessageManager(): MessageManager;
|
||||
}
|
||||
|
||||
export interface CardAutoRefreshAPI {
|
||||
getConfigManager(): ConfigManager;
|
||||
getInteractionManager(): InteractionManager;
|
||||
getTriggersManager(): TriggersManager;
|
||||
getViewManager(): ViewManager;
|
||||
}
|
||||
|
||||
export interface CardCameraAPI {
|
||||
getActionsManager(): ActionsManager;
|
||||
getConfigManager(): ConfigManager;
|
||||
@@ -92,6 +85,7 @@ export interface CardConfigAPI {
|
||||
getCardElementManager(): CardElementManager;
|
||||
getConditionsManager(): ConditionsManager;
|
||||
getConfigManager(): ConfigManager;
|
||||
getDefaultManager(): DefaultManager;
|
||||
getInitializationManager(): InitializationManager;
|
||||
getMediaLoadedInfoManager(): MediaLoadedInfoManager;
|
||||
getMessageManager(): MessageManager;
|
||||
@@ -104,6 +98,14 @@ export interface CardConfigLoaderAPI {
|
||||
getAutomationsManager(): AutomationsManager;
|
||||
}
|
||||
|
||||
export interface CardDefaultManagerAPI {
|
||||
getConfigManager(): ConfigManager;
|
||||
getHASSManager(): HASSManager;
|
||||
getInteractionManager(): InteractionManager;
|
||||
getTriggersManager(): TriggersManager;
|
||||
getViewManager(): ViewManager;
|
||||
}
|
||||
|
||||
export interface CardDownloadAPI {
|
||||
getCameraManager(): CameraManager;
|
||||
getHASSManager(): HASSManager;
|
||||
@@ -115,6 +117,7 @@ export interface CardDownloadAPI {
|
||||
export interface CardElementAPI {
|
||||
getActionsManager(): ActionsManager;
|
||||
getCameraManager(): CameraManager;
|
||||
getDefaultManager(): DefaultManager;
|
||||
getExpandManager(): ExpandManager;
|
||||
getFullscreenManager(): FullscreenManager;
|
||||
getInitializationManager(): InitializationManager;
|
||||
@@ -143,6 +146,7 @@ export interface CardHASSAPI {
|
||||
getCardElementManager(): CardElementManager;
|
||||
getConditionsManager(): ConditionsManager;
|
||||
getConfigManager(): ConfigManager;
|
||||
getDefaultManager(): DefaultManager;
|
||||
getInteractionManager(): InteractionManager;
|
||||
getMediaPlayerManager(): MediaPlayerManager;
|
||||
getMessageManager(): MessageManager;
|
||||
@@ -155,6 +159,7 @@ export interface CardInitializerAPI {
|
||||
getCameraManager(): CameraManager;
|
||||
getCardElementManager(): CardElementManager;
|
||||
getConfigManager(): ConfigManager;
|
||||
getDefaultManager(): DefaultManager;
|
||||
getEntityRegistryManager(): EntityRegistryManager;
|
||||
getHASSManager(): HASSManager;
|
||||
getMediaPlayerManager(): MediaPlayerManager;
|
||||
@@ -233,7 +238,6 @@ export interface CardTriggersAPI {
|
||||
}
|
||||
|
||||
export interface CardViewAPI {
|
||||
getAutoUpdateManager(): AutoUpdateManager;
|
||||
getCameraManager(): CameraManager;
|
||||
getCardElementManager(): CardElementManager;
|
||||
getConditionsManager(): ConditionsManager;
|
||||
|
||||
@@ -55,7 +55,7 @@ export class ViewManager {
|
||||
let forceCameraID: string | null = params?.cameraID ?? null;
|
||||
const viewName = config.view.default;
|
||||
|
||||
if (!forceCameraID && this._view?.camera && config.view.update_cycle_camera) {
|
||||
if (!forceCameraID && this._view?.camera && config.view.default_cycle_camera) {
|
||||
const cameraIDs = [
|
||||
...getCameraIDsForViewName(this._api.getCameraManager(), viewName),
|
||||
];
|
||||
@@ -69,10 +69,6 @@ export class ViewManager {
|
||||
viewName: viewName,
|
||||
...(forceCameraID && { cameraID: forceCameraID }),
|
||||
});
|
||||
|
||||
// Restart the refresh timer, so the default view is refreshed at a fixed
|
||||
// interval from now (if so configured).
|
||||
this._api.getAutoUpdateManager().startDefaultViewTimer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,11 @@ import {
|
||||
CONF_OVERRIDES,
|
||||
CONF_PROFILES,
|
||||
CONF_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||
CONF_VIEW_DEFAULT_CYCLE_CAMERA,
|
||||
CONF_VIEW_INTERACTION_SECONDS,
|
||||
CONF_VIEW_DEFAULT_RESET_EVERY_SECONDS,
|
||||
CONF_VIEW_DEFAULT_RESET_ENTITIES,
|
||||
CONF_VIEW_DEFAULT_RESET_INTERACTION_MODE,
|
||||
CONF_VIEW_TRIGGERS,
|
||||
CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER,
|
||||
CONF_VIEW_TRIGGERS_ACTIONS_UNTRIGGER,
|
||||
@@ -805,4 +809,17 @@ const UPGRADES = [
|
||||
keepOriginal: true,
|
||||
}),
|
||||
upgradeWithOverrides('live.controls.ptz', ptzControlSettingsTransform),
|
||||
upgradeMoveToWithOverrides('view.update_cycle_camera', CONF_VIEW_DEFAULT_CYCLE_CAMERA),
|
||||
upgradeMoveToWithOverrides(
|
||||
'view.update_force',
|
||||
CONF_VIEW_DEFAULT_RESET_INTERACTION_MODE,
|
||||
{
|
||||
transform: (val) => (val === true ? 'all' : null),
|
||||
},
|
||||
),
|
||||
upgradeMoveToWithOverrides(
|
||||
'view.update_seconds',
|
||||
CONF_VIEW_DEFAULT_RESET_EVERY_SECONDS,
|
||||
),
|
||||
upgradeMoveToWithOverrides('view.update_entities', CONF_VIEW_DEFAULT_RESET_ENTITIES),
|
||||
];
|
||||
|
||||
@@ -137,5 +137,5 @@ export const LOW_PERFORMANCE_PROFILE = {
|
||||
[CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS]: 10,
|
||||
|
||||
// No trigger actions.
|
||||
[CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER]: 'none'
|
||||
[CONF_VIEW_TRIGGERS_ACTIONS_TRIGGER]: 'none',
|
||||
};
|
||||
|
||||
+26
-15
@@ -1396,16 +1396,18 @@ const viewConfigDefault = {
|
||||
default: FRIGATE_CARD_VIEW_DEFAULT,
|
||||
camera_select: 'current' as const,
|
||||
interaction_seconds: 300,
|
||||
reset_after_interaction: true,
|
||||
update_seconds: 0,
|
||||
update_force: false,
|
||||
update_cycle_camera: false,
|
||||
default_reset: {
|
||||
every_seconds: 0,
|
||||
after_interaction: false,
|
||||
entities: [],
|
||||
interaction_mode: 'inactive' as const,
|
||||
},
|
||||
default_cycle_camera: false,
|
||||
dark_mode: 'off' as const,
|
||||
triggers: {
|
||||
show_trigger_status: false,
|
||||
filter_selected_camera: true,
|
||||
actions: {
|
||||
interaction_mode: 'inactive' as const,
|
||||
trigger: 'update' as const,
|
||||
untrigger: 'none' as const,
|
||||
},
|
||||
@@ -1414,12 +1416,13 @@ const viewConfigDefault = {
|
||||
keyboard_shortcuts: keyboardShortcutsDefault,
|
||||
};
|
||||
|
||||
const interactionModeSchema = z.enum(['all', 'inactive', 'active']).default('inactive');
|
||||
export type InteractionMode = z.infer<typeof interactionModeSchema>;
|
||||
|
||||
export const triggersSchema = z.object({
|
||||
actions: z
|
||||
.object({
|
||||
interaction_mode: z
|
||||
.enum(['all', 'inactive', 'active'])
|
||||
.default(viewConfigDefault.triggers.actions.interaction_mode),
|
||||
interaction_mode: interactionModeSchema,
|
||||
trigger: z
|
||||
.enum(['default', 'live', 'media', 'none', 'update'])
|
||||
.default(viewConfigDefault.triggers.actions.trigger),
|
||||
@@ -1447,13 +1450,21 @@ const viewConfigSchema = z
|
||||
.enum([...FRIGATE_CARD_VIEWS_USER_SPECIFIED, 'current'])
|
||||
.default(viewConfigDefault.camera_select),
|
||||
interaction_seconds: z.number().default(viewConfigDefault.interaction_seconds),
|
||||
reset_after_interaction: z
|
||||
.boolean()
|
||||
.default(viewConfigDefault.reset_after_interaction),
|
||||
update_seconds: z.number().default(viewConfigDefault.update_seconds),
|
||||
update_force: z.boolean().default(viewConfigDefault.update_force),
|
||||
update_cycle_camera: z.boolean().default(viewConfigDefault.update_cycle_camera),
|
||||
update_entities: z.string().array().optional(),
|
||||
default_cycle_camera: z.boolean().default(viewConfigDefault.default_cycle_camera),
|
||||
|
||||
default_reset: z
|
||||
.object({
|
||||
after_interaction: z
|
||||
.boolean()
|
||||
.default(viewConfigDefault.default_reset.after_interaction),
|
||||
every_seconds: z.number().default(viewConfigDefault.default_reset.every_seconds),
|
||||
entities: z.string().array().default(viewConfigDefault.default_reset.entities),
|
||||
interaction_mode: interactionModeSchema.default(
|
||||
viewConfigDefault.default_reset.interaction_mode,
|
||||
),
|
||||
})
|
||||
.default(viewConfigDefault.default_reset),
|
||||
|
||||
render_entities: z.string().array().optional(),
|
||||
dark_mode: z.enum(['on', 'off', 'auto']).optional(),
|
||||
triggers: triggersSchema.default(viewConfigDefault.triggers),
|
||||
|
||||
+11
-5
@@ -123,11 +123,17 @@ export const CONF_VIEW_KEYBOARD_SHORTCUTS_PTZ_ZOOM_OUT =
|
||||
`${CONF_VIEW_KEYBOARD_SHORTCUTS}.ptz_zoom_out` as const;
|
||||
export const CONF_VIEW_KEYBOARD_SHORTCUTS_PTZ_HOME =
|
||||
`${CONF_VIEW_KEYBOARD_SHORTCUTS}.ptz_home` as const;
|
||||
export const CONF_VIEW_UPDATE_CYCLE_CAMERA = `${CONF_VIEW}.update_cycle_camera` as const;
|
||||
export const CONF_VIEW_UPDATE_FORCE = `${CONF_VIEW}.update_force` as const;
|
||||
export const CONF_VIEW_UPDATE_SECONDS = `${CONF_VIEW}.update_seconds` as const;
|
||||
export const CONF_VIEW_RESET_AFTER_INTERACTION =
|
||||
`${CONF_VIEW}.reset_after_interaction` as const;
|
||||
export const CONF_VIEW_DEFAULT_CYCLE_CAMERA =
|
||||
`${CONF_VIEW}.default_cycle_camera` as const;
|
||||
export const CONF_VIEW_DEFAULT_RESET = `${CONF_VIEW}.default_reset` as const;
|
||||
export const CONF_VIEW_DEFAULT_RESET_INTERACTION_MODE =
|
||||
`${CONF_VIEW_DEFAULT_RESET}.interaction_mode` as const;
|
||||
export const CONF_VIEW_DEFAULT_RESET_EVERY_SECONDS =
|
||||
`${CONF_VIEW_DEFAULT_RESET}.every_seconds` as const;
|
||||
export const CONF_VIEW_DEFAULT_RESET_ENTITIES =
|
||||
`${CONF_VIEW_DEFAULT_RESET}.entities` as const;
|
||||
export const CONF_VIEW_DEFAULT_RESET_AFTER_INTERACTION =
|
||||
`${CONF_VIEW_DEFAULT_RESET}.after_interaction` as const;
|
||||
export const CONF_VIEW_TRIGGERS = `${CONF_VIEW}.triggers` as const;
|
||||
export const CONF_VIEW_TRIGGERS_SHOW_TRIGGER_STATUS =
|
||||
`${CONF_VIEW_TRIGGERS}.show_trigger_status` as const;
|
||||
|
||||
+56
-16
@@ -201,7 +201,7 @@ import {
|
||||
CONF_VIEW_KEYBOARD_SHORTCUTS_PTZ_UP,
|
||||
CONF_VIEW_KEYBOARD_SHORTCUTS_PTZ_ZOOM_IN,
|
||||
CONF_VIEW_KEYBOARD_SHORTCUTS_PTZ_ZOOM_OUT,
|
||||
CONF_VIEW_RESET_AFTER_INTERACTION,
|
||||
CONF_VIEW_DEFAULT_RESET_AFTER_INTERACTION,
|
||||
CONF_VIEW_TRIGGERS,
|
||||
CONF_VIEW_TRIGGERS_ACTIONS,
|
||||
CONF_VIEW_TRIGGERS_ACTIONS_INTERACTION_MODE,
|
||||
@@ -210,10 +210,12 @@ import {
|
||||
CONF_VIEW_TRIGGERS_FILTER_SELECTED_CAMERA,
|
||||
CONF_VIEW_TRIGGERS_SHOW_TRIGGER_STATUS,
|
||||
CONF_VIEW_TRIGGERS_UNTRIGGER_SECONDS,
|
||||
CONF_VIEW_UPDATE_CYCLE_CAMERA,
|
||||
CONF_VIEW_UPDATE_FORCE,
|
||||
CONF_VIEW_UPDATE_SECONDS,
|
||||
CONF_VIEW_DEFAULT_CYCLE_CAMERA,
|
||||
MEDIA_CHUNK_SIZE_MAX,
|
||||
CONF_VIEW_DEFAULT_RESET,
|
||||
CONF_VIEW_DEFAULT_RESET_EVERY_SECONDS,
|
||||
CONF_VIEW_DEFAULT_RESET_INTERACTION_MODE,
|
||||
CONF_VIEW_DEFAULT_RESET_ENTITIES,
|
||||
} from './const.js';
|
||||
import { localize } from './localize/localize.js';
|
||||
import frigate_card_editor_style from './scss/editor.scss';
|
||||
@@ -263,6 +265,7 @@ const MENU_PERFORMANCE_FEATURES = 'performance.features';
|
||||
const MENU_PERFORMANCE_STYLE = 'performance.style';
|
||||
const MENU_TIMELINE_CONTROLS_THUMBNAILS = 'timeline.controls.thumbnails';
|
||||
const MENU_VIEW_KEYBOARD_SHORTCUTS = 'view.keyboard_shortcuts';
|
||||
const MENU_VIEW_DEFAULT_RESET = 'view.default_reset';
|
||||
const MENU_VIEW_TRIGGERS = 'view.triggers';
|
||||
const MENU_VIEW_TRIGGERS_ACTIONS = 'view.triggers.actions';
|
||||
|
||||
@@ -813,6 +816,22 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
},
|
||||
];
|
||||
|
||||
protected _defaultResetInteractionModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{
|
||||
value: 'all',
|
||||
label: localize('config.view.default_reset.interaction_modes.all'),
|
||||
},
|
||||
{
|
||||
value: 'inactive',
|
||||
label: localize('config.view.default_reset.interaction_modes.inactive'),
|
||||
},
|
||||
{
|
||||
value: 'active',
|
||||
label: localize('config.view.default_reset.interaction_modes.active'),
|
||||
},
|
||||
];
|
||||
|
||||
public setConfig(config: RawFrigateCardConfig): void {
|
||||
// Note: This does not use Zod to parse the full configuration, so it may be
|
||||
// partially or completely invalid. It's more useful to have a partially
|
||||
@@ -1068,6 +1087,36 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
);
|
||||
}
|
||||
|
||||
protected _renderViewDefaultResetMenu(): TemplateResult {
|
||||
return this._putInSubmenu(
|
||||
MENU_VIEW_DEFAULT_RESET,
|
||||
true,
|
||||
`config.${CONF_VIEW_DEFAULT_RESET}.editor_label`,
|
||||
{ name: 'mdi:restart' },
|
||||
html`
|
||||
${this._renderSwitch(
|
||||
CONF_VIEW_DEFAULT_RESET_AFTER_INTERACTION,
|
||||
this._defaults.view.default_reset.after_interaction,
|
||||
)}
|
||||
${this._renderNumberInput(CONF_VIEW_DEFAULT_RESET_EVERY_SECONDS)}
|
||||
${this._renderOptionSelector(
|
||||
CONF_VIEW_DEFAULT_RESET_INTERACTION_MODE,
|
||||
this._defaultResetInteractionModes,
|
||||
{
|
||||
label: localize('config.view.default_reset.interaction_mode'),
|
||||
},
|
||||
)},
|
||||
${this._renderOptionSelector(
|
||||
CONF_VIEW_DEFAULT_RESET_ENTITIES,
|
||||
this.hass ? getEntitiesFromHASS(this.hass) : [],
|
||||
{
|
||||
multiple: true,
|
||||
},
|
||||
)}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
protected _renderViewTriggersMenu(): TemplateResult {
|
||||
return this._putInSubmenu(
|
||||
MENU_VIEW_TRIGGERS,
|
||||
@@ -2330,19 +2379,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
${this._renderOptionSelector(CONF_VIEW_DARK_MODE, this._darkModes)}
|
||||
${this._renderNumberInput(CONF_VIEW_INTERACTION_SECONDS)}
|
||||
${this._renderSwitch(
|
||||
CONF_VIEW_RESET_AFTER_INTERACTION,
|
||||
this._defaults.view.reset_after_interaction,
|
||||
CONF_VIEW_DEFAULT_CYCLE_CAMERA,
|
||||
this._defaults.view.default_cycle_camera,
|
||||
)}
|
||||
${this._renderNumberInput(CONF_VIEW_UPDATE_SECONDS)}
|
||||
${this._renderSwitch(
|
||||
CONF_VIEW_UPDATE_FORCE,
|
||||
this._defaults.view.update_force,
|
||||
)}
|
||||
${this._renderSwitch(
|
||||
CONF_VIEW_UPDATE_CYCLE_CAMERA,
|
||||
this._defaults.view.update_cycle_camera,
|
||||
)}
|
||||
${this._renderViewTriggersMenu()}
|
||||
${this._renderViewDefaultResetMenu()} ${this._renderViewTriggersMenu()}
|
||||
${this._renderViewKeyboardShortcutMenu()}
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -436,12 +436,31 @@
|
||||
"on": "Activat"
|
||||
},
|
||||
"default": "Vista per defecte",
|
||||
"default_cycle_camera": "Passeu per les càmeres quan s'actualitzi la vista predeterminada",
|
||||
"default_reset": {
|
||||
"after_interaction": "Restableix la vista predeterminada després de la interacció de l'usuari",
|
||||
"editor_label": "",
|
||||
"entities": "",
|
||||
"every_seconds": "Actualitza la vista predeterminada cada X segons (0=mai)",
|
||||
"interaction_mode": "",
|
||||
"interaction_modes": {
|
||||
"active": "",
|
||||
"all": "",
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"interaction_seconds": "Segons després de l'acció de l'usuari per continuar interactuant (0=mai)",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
"enabled": ""
|
||||
"enabled": "",
|
||||
"ptz_down": "",
|
||||
"ptz_home": "",
|
||||
"ptz_left": "",
|
||||
"ptz_right": "",
|
||||
"ptz_up": "",
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"reset_after_interaction": "Restableix la vista predeterminada després de la interacció de l'usuari",
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "Activar accions",
|
||||
@@ -469,9 +488,6 @@
|
||||
"show_trigger_status": "Mostra la vora intermitent quan s'activa",
|
||||
"untrigger_seconds": "Segons després del canvi d'estat inactiu a desactivat"
|
||||
},
|
||||
"update_cycle_camera": "Passeu per les càmeres quan s'actualitzi la vista predeterminada",
|
||||
"update_force": "Força les actualitzacions de la targeta (ignora la interacció de l'usuari)",
|
||||
"update_seconds": "Actualitza la vista predeterminada cada X segons (0=mai)",
|
||||
"views": {
|
||||
"clip": "Clip més recent",
|
||||
"clips": "Galeria de clips",
|
||||
|
||||
@@ -362,7 +362,6 @@
|
||||
"camera_ui": "Camera user interface",
|
||||
"cameras": "Cameras",
|
||||
"clips": "Clips",
|
||||
"ptz_home": "PTZ Home",
|
||||
"display_mode": "Display mode",
|
||||
"download": "Download",
|
||||
"enabled": "Button enabled",
|
||||
@@ -378,6 +377,7 @@
|
||||
"play": "Play / Pause",
|
||||
"priority": "Priority",
|
||||
"ptz_controls": "Show PTZ controls",
|
||||
"ptz_home": "PTZ Home",
|
||||
"recordings": "Recordings",
|
||||
"screenshot": "Screenshot",
|
||||
"snapshots": "Snapshots",
|
||||
@@ -436,6 +436,19 @@
|
||||
"on": "On"
|
||||
},
|
||||
"default": "Default view",
|
||||
"default_cycle_camera": "Cycle through cameras when default view updates",
|
||||
"default_reset": {
|
||||
"after_interaction": "Reset to the default view after user interaction ends",
|
||||
"editor_label": "Default view reset behavior",
|
||||
"entities": "Reset to the default view on entity state change",
|
||||
"every_seconds": "Reset to default view every X seconds (0=never)",
|
||||
"interaction_mode": "How default reset behaves when the card has human interaction",
|
||||
"interaction_modes": {
|
||||
"active": "Only allow reset when card has active human interaction",
|
||||
"all": "Reset regardless of human interaction",
|
||||
"inactive": "Only reset when card has no human interaction"
|
||||
}
|
||||
},
|
||||
"interaction_seconds": "Seconds after user action to remain interacted with (0=never)",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "Keyboard shortcuts",
|
||||
@@ -448,7 +461,6 @@
|
||||
"ptz_zoom_in": "PTZ Zoom In",
|
||||
"ptz_zoom_out": "PTZ Zoom Out"
|
||||
},
|
||||
"reset_after_interaction": "Reset to the default view after user interaction",
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "Trigger actions",
|
||||
@@ -476,9 +488,6 @@
|
||||
"show_trigger_status": "Show pulsing border when triggered",
|
||||
"untrigger_seconds": "Seconds after inactive state change to untrigger"
|
||||
},
|
||||
"update_cycle_camera": "Cycle through cameras when default view updates",
|
||||
"update_force": "Force card updates (ignore user interaction)",
|
||||
"update_seconds": "Refresh default view every X seconds (0=never)",
|
||||
"views": {
|
||||
"clip": "Most recent clip",
|
||||
"clips": "Clips gallery",
|
||||
|
||||
@@ -436,12 +436,31 @@
|
||||
"on": "Activé"
|
||||
},
|
||||
"default": "Vue par défaut",
|
||||
"default_cycle_camera": "Parcourez les caméras lorsque la vue par défaut est mise à jour",
|
||||
"default_reset": {
|
||||
"after_interaction": "",
|
||||
"editor_label": "",
|
||||
"entities": "",
|
||||
"every_seconds": "Actualiser la vue par défaut toutes les X secondes (0=jamais)",
|
||||
"interaction_mode": "",
|
||||
"interaction_modes": {
|
||||
"active": "",
|
||||
"all": "",
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"interaction_seconds": "",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
"enabled": ""
|
||||
"enabled": "",
|
||||
"ptz_down": "",
|
||||
"ptz_home": "",
|
||||
"ptz_left": "",
|
||||
"ptz_right": "",
|
||||
"ptz_up": "",
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"reset_after_interaction": "",
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "",
|
||||
@@ -469,9 +488,6 @@
|
||||
"show_trigger_status": "Afficher la bordure clignotante lors du déclenchement",
|
||||
"untrigger_seconds": "Quelques secondes après le changement d'état inactif pour débloquer"
|
||||
},
|
||||
"update_cycle_camera": "Parcourez les caméras lorsque la vue par défaut est mise à jour",
|
||||
"update_force": "Forcer les mises à jour de la carte (ignorer l'interaction de l'utilisateur)",
|
||||
"update_seconds": "Actualiser la vue par défaut toutes les X secondes (0=jamais)",
|
||||
"views": {
|
||||
"clip": "Clip le plus récent",
|
||||
"clips": "Galerie de clips",
|
||||
|
||||
@@ -436,12 +436,31 @@
|
||||
"on": "On"
|
||||
},
|
||||
"default": "Visualizzazione predefinita",
|
||||
"default_cycle_camera": "Scorri le telecamere quando si aggiorna la visualizzazione predefinita",
|
||||
"default_reset": {
|
||||
"after_interaction": "",
|
||||
"editor_label": "",
|
||||
"entities": "",
|
||||
"every_seconds": "Aggiorna la visualizzazione predefinita ogni x secondi (0 = mai)",
|
||||
"interaction_mode": "",
|
||||
"interaction_modes": {
|
||||
"active": "",
|
||||
"all": "",
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"interaction_seconds": "",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
"enabled": ""
|
||||
"enabled": "",
|
||||
"ptz_down": "",
|
||||
"ptz_home": "",
|
||||
"ptz_left": "",
|
||||
"ptz_right": "",
|
||||
"ptz_up": "",
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"reset_after_interaction": "",
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "",
|
||||
@@ -469,9 +488,6 @@
|
||||
"show_trigger_status": "Mostra bordo pulsante quando attivato",
|
||||
"untrigger_seconds": "Reimposta la vista ai valori predefiniti dopo aver annullato l'attivazione"
|
||||
},
|
||||
"update_cycle_camera": "Scorri le telecamere quando si aggiorna la visualizzazione predefinita",
|
||||
"update_force": "Aggiornamenti della scheda forza (ignora l'interazione dell'utente)",
|
||||
"update_seconds": "Aggiorna la visualizzazione predefinita ogni x secondi (0 = mai)",
|
||||
"views": {
|
||||
"clip": "Clip più recente",
|
||||
"clips": "Galleria delle clip",
|
||||
|
||||
@@ -436,12 +436,31 @@
|
||||
"on": "Ligado"
|
||||
},
|
||||
"default": "Visualização padrão",
|
||||
"default_cycle_camera": "Percorrer as câmeras quando a visualização padrão for atualizada",
|
||||
"default_reset": {
|
||||
"after_interaction": "",
|
||||
"editor_label": "",
|
||||
"entities": "",
|
||||
"every_seconds": "Atualize a visualização padrão a cada X segundos (0 = nunca)",
|
||||
"interaction_mode": "",
|
||||
"interaction_modes": {
|
||||
"active": "",
|
||||
"all": "",
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"interaction_seconds": "",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
"enabled": ""
|
||||
"enabled": "",
|
||||
"ptz_down": "",
|
||||
"ptz_home": "",
|
||||
"ptz_left": "",
|
||||
"ptz_right": "",
|
||||
"ptz_up": "",
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"reset_after_interaction": "",
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "",
|
||||
@@ -469,9 +488,6 @@
|
||||
"show_trigger_status": "Pulsar borda quando acionado",
|
||||
"untrigger_seconds": "Segundos após a mudar para o estado inativo para desacionar"
|
||||
},
|
||||
"update_cycle_camera": "Percorrer as câmeras quando a visualização padrão for atualizada",
|
||||
"update_force": "Forçar atualizações do cartão (ignore a interação do usuário)",
|
||||
"update_seconds": "Atualize a visualização padrão a cada X segundos (0 = nunca)",
|
||||
"views": {
|
||||
"clip": "Clipe mais recente",
|
||||
"clips": "Galeria de clipes",
|
||||
|
||||
@@ -436,12 +436,31 @@
|
||||
"on": "Ligado"
|
||||
},
|
||||
"default": "Visualização padrão",
|
||||
"default_cycle_camera": "Percorrer as câmeras quando a visualização padrão for atualizada",
|
||||
"default_reset": {
|
||||
"after_interaction": "",
|
||||
"editor_label": "",
|
||||
"entities": "",
|
||||
"every_seconds": "Atualize a visualização padrão a cada X segundos (0 = nunca)",
|
||||
"interaction_mode": "",
|
||||
"interaction_modes": {
|
||||
"active": "",
|
||||
"all": "",
|
||||
"inactive": ""
|
||||
}
|
||||
},
|
||||
"interaction_seconds": "",
|
||||
"keyboard_shortcuts": {
|
||||
"editor_label": "",
|
||||
"enabled": ""
|
||||
"enabled": "",
|
||||
"ptz_down": "",
|
||||
"ptz_home": "",
|
||||
"ptz_left": "",
|
||||
"ptz_right": "",
|
||||
"ptz_up": "",
|
||||
"ptz_zoom_in": "",
|
||||
"ptz_zoom_out": ""
|
||||
},
|
||||
"reset_after_interaction": "",
|
||||
"triggers": {
|
||||
"actions": {
|
||||
"editor_label": "",
|
||||
@@ -469,9 +488,6 @@
|
||||
"show_trigger_status": "Exibir estado do gatilho",
|
||||
"untrigger_seconds": "Segundos após a mudar para o estado inativo para desacionar"
|
||||
},
|
||||
"update_cycle_camera": "Percorrer as câmeras quando a visualização padrão for atualizada",
|
||||
"update_force": "Forçar atualizações do cartão (ignore a interação do Utilizador)",
|
||||
"update_seconds": "Atualize a visualização padrão a cada X segundos (0 = nunca)",
|
||||
"views": {
|
||||
"clip": "Clipe mais recente",
|
||||
"clips": "Galeria de clipes",
|
||||
|
||||
@@ -24,6 +24,8 @@ import {
|
||||
SubscriptionUnsubscribe,
|
||||
} from './types.js';
|
||||
|
||||
export type DestroyCallback = () => Promise<void>;
|
||||
|
||||
/**
|
||||
* Make a HomeAssistant websocket request. May throw.
|
||||
* @param hass The HomeAssistant object to send the request with.
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { InteractionMode } from '../config/types';
|
||||
|
||||
export const isActionAllowedBasedOnInteractionState = (
|
||||
interactionMode: InteractionMode,
|
||||
interactionState: boolean,
|
||||
): boolean => {
|
||||
switch (interactionMode) {
|
||||
case 'all':
|
||||
return true;
|
||||
case 'active':
|
||||
return interactionState;
|
||||
case 'inactive':
|
||||
return !interactionState;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user