Rename several view variables for clarity.

This commit is contained in:
Dermot Duffy
2024-06-11 19:48:39 -07:00
parent f193f9925f
commit afb9af5eb4
40 changed files with 769 additions and 285 deletions
+1 -2
View File
@@ -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();
}
}
+9 -8
View File
@@ -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;
}
+92
View File
@@ -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());
}
}
}
-24
View File
@@ -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(),
+4 -1
View File
@@ -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;
+13 -9
View File
@@ -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;
+1 -5
View File
@@ -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();
}
}
+17
View File
@@ -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),
];
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>
`
+21 -5
View File
@@ -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",
+14 -5
View File
@@ -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",
+21 -5
View File
@@ -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",
+21 -5
View File
@@ -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",
+21 -5
View File
@@ -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",
+21 -5
View File
@@ -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",
+2
View File
@@ -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.
+15
View File
@@ -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;
}
};