Files
advanced-camera-card/src/utils/action.ts
T

224 lines
6.5 KiB
TypeScript

import { ActionConfig, hasAction } from '@dermotduffy/custom-card-helpers';
import { ZoomSettingsBase } from '../components-lib/zoom/types.js';
import { PTZAction } from '../config/ptz.js';
import {
ActionPhase,
ActionType,
Actions,
FrigateCardCustomAction,
FrigateCardGeneralAction,
FrigateCardUserSpecifiedView,
LogActionConfig,
LogActionLevel,
PTZActionConfig,
PTZDigitialActionConfig,
PTZMultiActionConfig,
frigateCardCustomActionSchema,
} from '../config/types.js';
import { arrayify } from './basic.js';
/**
* Convert a generic Action to a FrigateCardCustomAction if it parses correctly.
* @param action The generic action configuration.
* @returns A FrigateCardCustomAction or null if it cannot be converted.
*/
export function convertActionToCardCustomAction(
action: unknown,
): FrigateCardCustomAction | null {
if (!action) {
return null;
}
// Parse a custom event as other things could generate ll-custom events that
// are not related to Frigate Card.
const parseResult = frigateCardCustomActionSchema.safeParse(action);
return parseResult.success ? parseResult.data : null;
}
export function createGeneralAction(
action: FrigateCardGeneralAction | FrigateCardUserSpecifiedView,
options?: {
cardID?: string;
},
): FrigateCardCustomAction | null {
return {
action: 'fire-dom-event',
frigate_card_action: action,
...(options?.cardID && { card_id: options.cardID }),
};
}
export function createCameraAction(
action: 'camera_select' | 'live_substream_select',
camera: string,
options?: {
cardID?: string;
},
): FrigateCardCustomAction {
return {
action: 'fire-dom-event',
frigate_card_action: action,
camera: camera,
...(options?.cardID && { card_id: options.cardID }),
};
}
export function createMediaPlayerAction(
mediaPlayer: string,
mediaPlayerAction: 'play' | 'stop',
options?: {
cardID?: string;
},
): FrigateCardCustomAction {
return {
action: 'fire-dom-event',
frigate_card_action: 'media_player',
media_player: mediaPlayer,
media_player_action: mediaPlayerAction,
...(options?.cardID && { card_id: options.cardID }),
};
}
export function createDisplayModeAction(
displayMode: 'single' | 'grid',
options?: {
cardID?: string;
},
): FrigateCardCustomAction {
return {
action: 'fire-dom-event',
frigate_card_action: 'display_mode_select',
display_mode: displayMode,
...(options?.cardID && { card_id: options.cardID }),
};
}
export function createPTZControlsAction(
enabled: boolean,
options?: {
cardID?: string;
},
): FrigateCardCustomAction {
return {
action: 'fire-dom-event',
frigate_card_action: 'ptz_controls',
enabled: enabled,
...(options?.cardID && { card_id: options.cardID }),
};
}
export function createPTZAction(options?: {
cardID?: string;
ptzAction?: PTZAction;
ptzPhase?: ActionPhase;
ptzPreset?: string;
cameraID?: string;
}): PTZActionConfig {
return {
action: 'fire-dom-event',
frigate_card_action: 'ptz',
...(options?.cardID && { card_id: options.cardID }),
...(options?.ptzAction && { ptz_action: options.ptzAction }),
...(options?.ptzPhase && { ptz_phase: options.ptzPhase }),
...(options?.ptzPreset && { ptz_preset: options.ptzPreset }),
...(options?.cameraID && { camera: options.cameraID }),
};
}
export function createPTZDigitalAction(options?: {
cardID?: string;
ptzPhase?: ActionPhase;
ptzAction?: PTZAction;
absolute?: ZoomSettingsBase;
targetID?: string;
}): PTZDigitialActionConfig {
return {
action: 'fire-dom-event',
frigate_card_action: 'ptz_digital',
...(options?.cardID && { card_id: options.cardID }),
...(options?.ptzAction && { ptz_action: options.ptzAction }),
...(options?.ptzPhase && { ptz_phase: options.ptzPhase }),
...(options?.absolute && { absolute: options.absolute }),
...(options?.targetID && { target_id: options.targetID }),
};
}
export function createPTZMultiAction(options?: {
cardID?: string;
ptzAction?: PTZAction;
ptzPhase?: ActionPhase;
ptzPreset?: string;
targetID?: string;
}): PTZMultiActionConfig {
return {
action: 'fire-dom-event',
frigate_card_action: 'ptz_multi',
...(options?.cardID && { card_id: options.cardID }),
...(options?.ptzAction && { ptz_action: options.ptzAction }),
...(options?.ptzPhase && { ptz_phase: options.ptzPhase }),
...(options?.ptzPreset && { ptz_preset: options.ptzPreset }),
...(options?.targetID && { target_id: options.targetID }),
};
}
export function createLogAction(
message: string,
options?: {
cardID?: string;
level?: LogActionLevel;
},
): LogActionConfig {
return {
action: 'fire-dom-event',
frigate_card_action: 'log',
message: message,
level: options?.level ?? 'info',
...(options?.cardID && { card_id: options.cardID }),
};
}
/**
* Get an action configuration given a config and an interaction (e.g. 'tap').
* @param interaction The interaction: `tap`, `hold` or `double_tap`
* @param config The configuration containing multiple actions.
* @returns The relevant action configuration or null if none found.
*/
export function getActionConfigGivenAction(
interaction?: string,
config?: Actions | null,
): ActionType | ActionType[] | null {
if (!interaction || !config) {
return null;
}
if (interaction === 'tap' && config.tap_action) {
return config.tap_action;
} else if (interaction === 'hold' && config.hold_action) {
return config.hold_action;
} else if (interaction === 'double_tap' && config.double_tap_action) {
return config.double_tap_action;
} else if (interaction === 'end_tap' && config.end_tap_action) {
return config.end_tap_action;
} else if (interaction === 'start_tap' && config.start_tap_action) {
return config.start_tap_action;
}
return null;
}
/**
* Determine if an action config has a real action. A modified version of
* custom-card-helpers hasAction to also work with arrays of action configs.
* @param config The action config in question.
* @returns `true` if there's a real action defined, `false` otherwise.
*/
export const frigateCardHasAction = (config?: ActionType | ActionType[]): boolean => {
// See note above on 'ActionConfig vs ActionType' for why this cast is
// necessary and harmless.
return arrayify(config).some((item) => hasAction(item as ActionConfig | undefined));
};
/**
* Stop an event from activating card wide actions.
*/
export const stopEventFromActivatingCardWideActions = (ev: Event): void => {
ev.stopPropagation();
};