Files
advanced-camera-card/src/types.ts
T
Dermot Duffy 3279481b0e feat: Add festive visual effects (#2252)
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> Introduces an effects system (fireworks, ghost, hearts, shamrocks,
snow), a new `effect` action, and optional date-based loading effects,
integrated into the card with docs, schema, editor, and tests.
> 
> - **Effects System**:
> - Add `EffectsController` and `advanced-camera-card-effects` host with
lazy-loaded effect modules (`fireworks`, `ghost`, `hearts`, `shamrocks`,
`snow`).
> - New base effect component with fade-in/out; SCSS and z-index
updates.
> - **Actions**:
> - New custom action `advanced_camera_card_action: effect` with
`effect_action` (`start`|`stop`|`toggle`).
>   - Wire into `ActionFactory` and add `EffectAction` executor.
> - **Card Integration**:
> - Mount effects host in `advanced-camera-card` and expose
`getEffectsControllerAPI()` via `CardController`.
> - Loading component can trigger date-based effects (e.g., New Year
fireworks) when enabled.
> - **Configuration & Editor**:
> - Add `performance.features.card_loading_effects` (default `true`)
alongside `card_loading_indicator`.
> - Update schemas, defaults, low-performance profile (disables
effects), and editor toggles.
> - **Docs & Examples**:
>   - Document `effect` action parameters and add menu example.
>   - Update performance docs with new option.
> - **Localization & Tests**:
>   - Update i18n strings for new performance option.
> - Add comprehensive tests for effects controller, action, factory,
config defaults/profiles, and utilities.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cffd4304fe3bd22340316f9cec53e341379b1756. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-12-06 18:21:44 -08:00

189 lines
4.4 KiB
TypeScript

import { z } from 'zod';
import type { EffectOptions } from './components-lib/effects/types';
import type { LovelaceCard, LovelaceCardConfig, LovelaceCardEditor } from './ha/types';
export type ClipsOrSnapshots = 'clips' | 'snapshots';
export type ClipsOrSnapshotsOrAll = 'clips' | 'snapshots' | 'all';
export class AdvancedCameraCardError extends Error {
context?: unknown;
constructor(message: string, context?: unknown) {
super(message);
this.context = context;
}
}
export interface MediaLoadedCapabilities {
supports2WayAudio?: boolean;
supportsPause?: boolean;
hasAudio?: boolean;
}
export type MediaTechnology =
| 'hls'
| 'jpg'
| 'jsmpeg'
| 'mjpeg'
| 'mp4'
| 'mse'
| 'webrtc';
export interface MediaLoadedInfo {
width: number;
height: number;
technology?: MediaTechnology[];
mediaPlayerController?: MediaPlayerController;
capabilities?: MediaLoadedCapabilities;
// Whether or not this media is a placeholder (temporary image) whilst another
// media item is being loaded.
placeholder?: boolean;
}
export type MessageType = 'info' | 'error' | 'connection' | 'diagnostics';
export interface MessageURL {
link: string;
title: string;
}
export interface Message {
message: string;
type?: MessageType;
icon?: string;
context?: unknown;
dotdotdot?: boolean;
url?: MessageURL;
}
export type WebkitHTMLVideoElement = HTMLVideoElement & {
webkitDisplayingFullscreen: boolean;
webkitSupportsFullscreen: boolean;
webkitEnterFullscreen: () => void;
webkitExitFullscreen: () => void;
};
export type FullscreenElement = HTMLElement;
export interface MediaPlayerController {
play(): Promise<void>;
pause(): Promise<void>;
mute(): Promise<void>;
unmute(): Promise<void>;
isMuted(): boolean;
seek(seconds: number): Promise<void>;
getScreenshotURL(): Promise<string | null>;
// If no value for controls if specified, the player should use the default.
setControls(controls?: boolean): Promise<void>;
isPaused(): boolean;
getFullscreenElement(): FullscreenElement | null;
}
export interface MediaPlayer {
getMediaPlayerController(): Promise<MediaPlayerController | null>;
}
export type MediaPlayerElement<T extends HTMLElement = HTMLElement> = T & MediaPlayer;
export type LovelaceCardWithEditor = LovelaceCard & {
constructor: {
getConfigElement(): Promise<LovelaceCardEditor>;
};
};
export interface CardHelpers {
createCardElement(config: LovelaceCardConfig): Promise<LovelaceCardWithEditor>;
}
export enum PTZMovementType {
Relative = 'relative',
Continuous = 'continuous',
}
export interface PTZCapabilities {
left?: PTZMovementType[];
right?: PTZMovementType[];
up?: PTZMovementType[];
down?: PTZMovementType[];
zoomIn?: PTZMovementType[];
zoomOut?: PTZMovementType[];
presets?: string[];
}
export interface CapabilitiesRaw {
live?: boolean;
substream?: boolean;
clips?: boolean;
recordings?: boolean;
snapshots?: boolean;
'favorite-events'?: boolean;
'favorite-recordings'?: boolean;
'remote-control-entity'?: boolean;
seek?: boolean;
ptz?: PTZCapabilities;
menu?: boolean;
trigger?: boolean;
}
export type CapabilityKey = keyof CapabilitiesRaw;
export const capabilityKeys: readonly [CapabilityKey, ...CapabilityKey[]] = [
'clips',
'remote-control-entity',
'favorite-events',
'favorite-recordings',
'live',
'menu',
'ptz',
'recordings',
'seek',
'snapshots',
'substream',
'trigger',
] as const;
export interface Icon {
// If set, this icon will be used.
icon?: string;
// If icon is not set, this entity's icon will be used (and HA will be asked
// to render it).
entity?: string;
// Whether or not to change the icon color depending on entity state.
stateColor?: boolean;
// If an icon is not otherwise resolved / available, this will be used instead.
fallback?: string;
}
export interface Interaction {
action: string;
}
export interface Endpoint {
endpoint: string;
sign?: boolean;
}
export const signedPathSchema = z.object({
path: z.string(),
});
export type SignedPath = z.infer<typeof signedPathSchema>;
export type EffectName = 'fireworks' | 'ghost' | 'hearts' | 'shamrocks' | 'snow';
export interface EffectsControllerAPI {
startEffect(name: EffectName, options?: EffectOptions): Promise<void>;
stopEffect(effect: EffectName): void;
toggleEffect(effect: EffectName, options?: EffectOptions): Promise<void>;
}