Add initial keyboard shortcut support.
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import { isConfigUpgradeable } from '../../config/management.js';
|
||||
import {
|
||||
CardWideConfig,
|
||||
FrigateCardConfig,
|
||||
frigateCardConfigSchema,
|
||||
RawFrigateCardConfig,
|
||||
} from '../../config/types.js';
|
||||
import { localize } from '../../localize/localize.js';
|
||||
import { setProfiles } from '../../config/profiles/index.js';
|
||||
import { getParseErrorPaths } from '../../utils/zod.js';
|
||||
import { getOverriddenConfig } from '../conditions-manager.js';
|
||||
import { InitializationAspect } from '../initialization-manager.js';
|
||||
import { CardConfigAPI } from '../types.js';
|
||||
import { setAutomationsFromConfig } from './load-automations.js';
|
||||
import { setKeyboardShortcutsFromConfig } from './load-keyboard-shortcuts.js';
|
||||
|
||||
export class ConfigManager {
|
||||
protected _api: CardConfigAPI;
|
||||
|
||||
// The main base configuration object. For most usecases use getConfig() to
|
||||
// get the correct configuration (which will return overrides as appropriate).
|
||||
// This variable must be called `_config` or `config` to be compatible with
|
||||
// card-mod.
|
||||
protected _config: FrigateCardConfig | null = null;
|
||||
protected _overriddenConfig: FrigateCardConfig | null = null;
|
||||
protected _rawConfig: RawFrigateCardConfig | null = null;
|
||||
protected _cardWideConfig: CardWideConfig | null = null;
|
||||
|
||||
constructor(api: CardConfigAPI) {
|
||||
this._api = api;
|
||||
}
|
||||
|
||||
public hasConfig(): boolean {
|
||||
return !!this.getConfig();
|
||||
}
|
||||
|
||||
public getConfig(): FrigateCardConfig | null {
|
||||
return this._overriddenConfig ?? this._config;
|
||||
}
|
||||
|
||||
public getCardWideConfig(): CardWideConfig | null {
|
||||
return this._cardWideConfig;
|
||||
}
|
||||
|
||||
public getNonOverriddenConfig(): FrigateCardConfig | null {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
public getRawConfig(): RawFrigateCardConfig | null {
|
||||
return this._rawConfig;
|
||||
}
|
||||
|
||||
public setConfig(inputConfig?: RawFrigateCardConfig): void {
|
||||
if (!inputConfig) {
|
||||
throw new Error(localize('error.invalid_configuration'));
|
||||
}
|
||||
|
||||
const parseResult = frigateCardConfigSchema.safeParse(inputConfig);
|
||||
if (!parseResult.success) {
|
||||
const configUpgradeable = isConfigUpgradeable(inputConfig);
|
||||
const hint = getParseErrorPaths(parseResult.error);
|
||||
let upgradeMessage = '';
|
||||
if (configUpgradeable) {
|
||||
upgradeMessage = `${localize('error.upgrade_available')}. `;
|
||||
}
|
||||
throw new Error(
|
||||
upgradeMessage +
|
||||
`${localize('error.invalid_configuration')}: ` +
|
||||
(hint && hint.size
|
||||
? JSON.stringify([...hint], null, ' ')
|
||||
: localize('error.invalid_configuration_no_hint')),
|
||||
);
|
||||
}
|
||||
const config = setProfiles(inputConfig, parseResult.data, parseResult.data.profiles);
|
||||
|
||||
this._rawConfig = inputConfig;
|
||||
if (isEqual(this._config, config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
this._cardWideConfig = {
|
||||
performance: config.performance,
|
||||
debug: config.debug,
|
||||
};
|
||||
|
||||
this._api.getConditionsManager().setConditionsFromConfig();
|
||||
this._api.getConditionsManager().setState({
|
||||
view: undefined,
|
||||
displayMode: undefined,
|
||||
camera: undefined,
|
||||
});
|
||||
this._api.getMediaLoadedInfoManager().clear();
|
||||
this._api.getViewManager().reset();
|
||||
this._api.getMessageManager().reset();
|
||||
this._api.getStyleManager().setPerformance();
|
||||
this._api.getCardElementManager().update();
|
||||
setKeyboardShortcutsFromConfig(this._api, this);
|
||||
setAutomationsFromConfig(this._api);
|
||||
|
||||
this.computeOverrideConfig();
|
||||
}
|
||||
|
||||
public computeOverrideConfig(): void {
|
||||
const conditionsManager = this._api.getConditionsManager();
|
||||
if (!this._config) {
|
||||
return;
|
||||
}
|
||||
const overriddenConfig = getOverriddenConfig(conditionsManager, this._config, {
|
||||
configOverrides: this._config.overrides,
|
||||
schema: frigateCardConfigSchema,
|
||||
logOnParseError: !!this.getCardWideConfig()?.debug?.logging,
|
||||
}) as FrigateCardConfig;
|
||||
|
||||
// Save on Lit re-rendering costs by only updating the configuration if it
|
||||
// actually changes.
|
||||
if (isEqual(overriddenConfig, this._overriddenConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousConfig = this._overriddenConfig;
|
||||
this._overriddenConfig = overriddenConfig;
|
||||
|
||||
this._api.getStyleManager().setMinMaxHeight();
|
||||
|
||||
if (
|
||||
previousConfig &&
|
||||
(!isEqual(previousConfig?.cameras, this._overriddenConfig?.cameras) ||
|
||||
!isEqual(previousConfig?.cameras_global, this._overriddenConfig?.cameras_global))
|
||||
) {
|
||||
this._api.getInitializationManager().uninitialize(InitializationAspect.CAMERAS);
|
||||
}
|
||||
|
||||
if (
|
||||
previousConfig &&
|
||||
previousConfig?.live.microphone.always_connected !==
|
||||
this._overriddenConfig?.live.microphone.always_connected
|
||||
) {
|
||||
this._api
|
||||
.getInitializationManager()
|
||||
.uninitialize(InitializationAspect.MICROPHONE_CONNECT);
|
||||
}
|
||||
|
||||
this._api.getCardElementManager().update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { CardConfigLoaderAPI } from '../types';
|
||||
|
||||
export const setAutomationsFromConfig = (api: CardConfigLoaderAPI) => {
|
||||
api.getAutomationsManager().deleteAutomations();
|
||||
api
|
||||
.getAutomationsManager()
|
||||
.addAutomations(api.getConfigManager().getNonOverriddenConfig()?.automations ?? []);
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
import {
|
||||
KeyboardShortcuts,
|
||||
PTZKeyboardShortcutName,
|
||||
} from '../../config/keyboard-shortcuts';
|
||||
import { PTZAction } from '../../config/ptz';
|
||||
import { CardConfigLoaderAPI, TaggedAutomations } from '../types';
|
||||
import { createPTZMultiAction } from '../../utils/action';
|
||||
|
||||
export const setKeyboardShortcutsFromConfig = (
|
||||
api: CardConfigLoaderAPI,
|
||||
tag: unknown,
|
||||
) => {
|
||||
api.getAutomationsManager().deleteAutomations(tag);
|
||||
|
||||
const shortcuts = api.getConfigManager().getConfig()?.view.keyboard_shortcuts;
|
||||
if (!shortcuts) {
|
||||
return;
|
||||
}
|
||||
|
||||
const automations = convertKeyboardShortcutsToAutomations(tag, shortcuts);
|
||||
if (automations.length) {
|
||||
api.getAutomationsManager().addAutomations(automations);
|
||||
}
|
||||
};
|
||||
|
||||
const ptzKeyboardShortcutToPTZAction = (
|
||||
ptzKbs: PTZKeyboardShortcutName,
|
||||
): PTZAction | null => {
|
||||
switch (ptzKbs) {
|
||||
case 'ptz_left':
|
||||
return 'left';
|
||||
case 'ptz_right':
|
||||
return 'right';
|
||||
case 'ptz_up':
|
||||
return 'up';
|
||||
case 'ptz_down':
|
||||
return 'down';
|
||||
case 'ptz_zoom_in':
|
||||
return 'zoom_in';
|
||||
case 'ptz_zoom_out':
|
||||
return 'zoom_out';
|
||||
}
|
||||
/* istanbul ignore next: No (current) way to reach this code -- @preserve */
|
||||
return null;
|
||||
};
|
||||
|
||||
const convertKeyboardShortcutsToAutomations = (
|
||||
tag: unknown,
|
||||
shortcuts: KeyboardShortcuts,
|
||||
): TaggedAutomations => {
|
||||
if (!shortcuts.enabled) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const automations: TaggedAutomations = [];
|
||||
|
||||
for (const name of [
|
||||
'ptz_down',
|
||||
'ptz_left',
|
||||
'ptz_right',
|
||||
'ptz_up',
|
||||
'ptz_zoom_in',
|
||||
'ptz_zoom_out',
|
||||
] as const) {
|
||||
const shortcut = shortcuts[name];
|
||||
const ptzAction = ptzKeyboardShortcutToPTZAction(name);
|
||||
if (!shortcut || !ptzAction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
automations.push({
|
||||
conditions: [
|
||||
{
|
||||
condition: 'key' as const,
|
||||
key: shortcut.key,
|
||||
state: 'down',
|
||||
shift: shortcut.shift,
|
||||
ctrl: shortcut.ctrl,
|
||||
alt: shortcut.alt,
|
||||
meta: shortcut.meta,
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
createPTZMultiAction({
|
||||
ptzAction: ptzAction,
|
||||
ptzPhase: 'start',
|
||||
}),
|
||||
],
|
||||
tag: tag,
|
||||
});
|
||||
|
||||
automations.push({
|
||||
conditions: [
|
||||
{
|
||||
condition: 'key' as const,
|
||||
key: shortcut.key,
|
||||
state: 'up',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
createPTZMultiAction({
|
||||
ptzAction: ptzAction,
|
||||
ptzPhase: 'stop',
|
||||
}),
|
||||
],
|
||||
tag: tag,
|
||||
});
|
||||
}
|
||||
|
||||
const homeShortcut = shortcuts.ptz_home;
|
||||
if (homeShortcut) {
|
||||
automations.push({
|
||||
conditions: [
|
||||
{
|
||||
condition: 'key' as const,
|
||||
key: homeShortcut.key,
|
||||
state: 'down',
|
||||
shift: homeShortcut.shift,
|
||||
ctrl: homeShortcut.ctrl,
|
||||
alt: homeShortcut.alt,
|
||||
meta: homeShortcut.meta,
|
||||
},
|
||||
],
|
||||
actions: [createPTZMultiAction()],
|
||||
tag: tag,
|
||||
});
|
||||
}
|
||||
|
||||
return automations;
|
||||
};
|
||||
Reference in New Issue
Block a user