From 49c35a2ea0ec73dd89aa7ddc2aff9e03641bb54f Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Thu, 28 May 2026 14:57:25 +0300 Subject: [PATCH] refactor(frontend): split types.ts into 18 per-entity files (audit H6) Convert the 1140-LOC types.ts into a pure re-export barrel backed by focused per-entity files under types/, joining the existing bindable.ts. Every import { ... } from '../types.ts' resolves unchanged; reviewer-confirmed all 102 type exports preserved. --- server/src/ledgrab/static/js/types.ts | 1214 ++--------------- server/src/ledgrab/static/js/types/asset.ts | 25 + .../ledgrab/static/js/types/audio-source.ts | 40 + .../src/ledgrab/static/js/types/automation.ts | 62 + .../ledgrab/static/js/types/color-strip.ts | 181 +++ server/src/ledgrab/static/js/types/device.ts | 66 + server/src/ledgrab/static/js/types/display.ts | 14 + .../static/js/types/game-integration.ts | 79 ++ .../ledgrab/static/js/types/home-assistant.ts | 39 + .../ledgrab/static/js/types/http-endpoint.ts | 63 + server/src/ledgrab/static/js/types/mqtt.ts | 40 + .../ledgrab/static/js/types/output-target.ts | 94 ++ .../static/js/types/pattern-template.ts | 29 + .../ledgrab/static/js/types/picture-source.ts | 60 + .../ledgrab/static/js/types/scene-preset.ts | 34 + .../src/ledgrab/static/js/types/sync-clock.ts | 23 + .../src/ledgrab/static/js/types/template.ts | 91 ++ .../ledgrab/static/js/types/value-source.ts | 198 +++ .../ledgrab/static/js/types/weather-source.ts | 25 + 19 files changed, 1293 insertions(+), 1084 deletions(-) create mode 100644 server/src/ledgrab/static/js/types/asset.ts create mode 100644 server/src/ledgrab/static/js/types/audio-source.ts create mode 100644 server/src/ledgrab/static/js/types/automation.ts create mode 100644 server/src/ledgrab/static/js/types/color-strip.ts create mode 100644 server/src/ledgrab/static/js/types/device.ts create mode 100644 server/src/ledgrab/static/js/types/display.ts create mode 100644 server/src/ledgrab/static/js/types/game-integration.ts create mode 100644 server/src/ledgrab/static/js/types/home-assistant.ts create mode 100644 server/src/ledgrab/static/js/types/http-endpoint.ts create mode 100644 server/src/ledgrab/static/js/types/mqtt.ts create mode 100644 server/src/ledgrab/static/js/types/output-target.ts create mode 100644 server/src/ledgrab/static/js/types/pattern-template.ts create mode 100644 server/src/ledgrab/static/js/types/picture-source.ts create mode 100644 server/src/ledgrab/static/js/types/scene-preset.ts create mode 100644 server/src/ledgrab/static/js/types/sync-clock.ts create mode 100644 server/src/ledgrab/static/js/types/template.ts create mode 100644 server/src/ledgrab/static/js/types/value-source.ts create mode 100644 server/src/ledgrab/static/js/types/weather-source.ts diff --git a/server/src/ledgrab/static/js/types.ts b/server/src/ledgrab/static/js/types.ts index be1938f..f917c16 100644 --- a/server/src/ledgrab/static/js/types.ts +++ b/server/src/ledgrab/static/js/types.ts @@ -4,11 +4,11 @@ * These mirror the JSON shapes returned by the REST API. Field names use * snake_case to match the JSON payloads — no camelCase transformation is done. * - * Bindable primitives have been extracted into ``types/bindable.ts`` and - * are re-exported here so existing ``import { ... } from '../types.ts'`` - * call sites keep working. The intention is for further entity-shape - * groups (devices, sources, integrations, …) to follow the same pattern - * in subsequent passes — see audit finding H6. + * This file is a **barrel**: every entity shape now lives in its own + * ``types/.ts`` module and is re-exported here so existing + * ``import { ... } from '../types.ts'`` call sites keep working unchanged + * (audit finding H6). Add new entity shapes to the relevant per-entity + * file (or a new one) and surface them through this barrel. */ // ── Bindable Primitives ───────────────────────────────────── @@ -20,1120 +20,166 @@ export { bindableColorSourceId, } from './types/bindable.ts'; -// Local aliases used by the entity interfaces below so TypeScript can -// resolve them without an extra import at every reference site. -import type { BindableFloat, BindableColor } from './types/bindable.ts'; - // ── Device ──────────────────────────────────────────────────── - -export type DeviceType = - | 'wled' | 'adalight' | 'ambiled' | 'mock' | 'mqtt' | 'ws' - | 'openrgb' | 'dmx' | 'ddp' | 'opc' | 'espnow' | 'hue' | 'yeelight' | 'wiz' | 'lifx' | 'govee' - | 'nanoleaf' - | 'ble' | 'usbhid' | 'spi' - | 'chroma' | 'gamesense' | 'group'; - -export interface Device { - id: string; - name: string; - url: string; - device_type: DeviceType; - led_count: number; - enabled: boolean; - baud_rate?: number; - auto_shutdown: boolean; - send_latency_ms: number; - rgbw: boolean; - zone_mode: string; - capabilities: string[]; - tags: string[]; - dmx_protocol: string; - dmx_start_universe: number; - dmx_start_channel: number; - ddp_port: number; - ddp_destination_id: number; - ddp_color_order: number; - opc_channel: number; - espnow_peer_mac: string; - espnow_channel: number; - hue_paired: boolean; - hue_entertainment_group_id: string; - yeelight_min_interval_ms: number; - wiz_min_interval_ms: number; - lifx_min_interval_ms: number; - govee_min_interval_ms: number; - nanoleaf_paired: boolean; - nanoleaf_min_interval_ms: number; - spi_speed_hz: number; - spi_led_type: string; - chroma_device_type: string; - gamesense_device_type: string; - default_css_processing_template_id: string; - group_device_ids: string[]; - group_mode: string; - /** Optional id from the curated icon library (e.g. 'mouse', 'motherboard'). - * Empty/missing → no plate is rendered, head reverts to badge-only layout. */ - icon?: string; - /** Optional CSS color override for the icon. Empty/missing inherits --ch. */ - icon_color?: string; - created_at: string; - updated_at: string; -} +export type { DeviceType, Device, DeviceListResponse } from './types/device.ts'; // ── Output Target ───────────────────────────────────────────── - -export type TargetType = 'led' | 'ha_light' | 'z2m_light'; - -export interface HALightMapping { - entity_id: string; - led_start: number; - led_end: number; - brightness_scale: BindableFloat; -} - -export interface Z2MLightMapping { - friendly_name: string; - led_start: number; - led_end: number; - brightness_scale: BindableFloat; -} - -interface OutputTargetBase { - id: string; - name: string; - target_type: TargetType; - description?: string; - tags: string[]; - /** Optional id from the curated icon library. Empty/missing → - * for LED targets, the card inherits the device's icon; for - * HA-light targets, no plate is rendered. */ - icon?: string; - /** Optional CSS color override for the icon. Empty/missing → - * inherits the device color (LED targets) or --ch (others). */ - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface LedOutputTarget extends OutputTargetBase { - target_type: 'led'; - device_id: string; - color_strip_source_id: string; - brightness?: BindableFloat; - fps?: BindableFloat; - keepalive_interval: number; - state_check_interval: number; - min_brightness_threshold?: BindableFloat; - adaptive_fps: boolean; - protocol: string; -} - -export type HALightSourceKind = 'css' | 'color_vs'; - -export interface HALightOutputTarget extends OutputTargetBase { - target_type: 'ha_light'; - ha_source_id: string; - /** Which colour source feeds the lights: a CSS (`'css'`) or a colour-returning value source (`'color_vs'`). */ - source_kind: HALightSourceKind; - color_strip_source_id: string; - /** Used when `source_kind === 'color_vs'`. References a value source whose `return_type === 'color'`. */ - color_value_source_id?: string; - brightness?: BindableFloat; - ha_light_mappings?: HALightMapping[]; - update_rate?: BindableFloat; - transition?: BindableFloat; - color_tolerance?: BindableFloat; - min_brightness_threshold?: BindableFloat; -} - -export interface Z2MLightOutputTarget extends OutputTargetBase { - target_type: 'z2m_light'; - mqtt_source_id: string; - source_kind: HALightSourceKind; - color_strip_source_id: string; - color_value_source_id?: string; - brightness?: BindableFloat; - z2m_light_mappings?: Z2MLightMapping[]; - base_topic: string; - update_rate?: BindableFloat; - transition?: BindableFloat; - color_tolerance?: BindableFloat; - min_brightness_threshold?: BindableFloat; - stop_action?: 'none' | 'turn_off'; -} - -export type OutputTarget = LedOutputTarget | HALightOutputTarget | Z2MLightOutputTarget; +export type { + TargetType, + HALightMapping, + Z2MLightMapping, + LedOutputTarget, + HALightSourceKind, + HALightOutputTarget, + Z2MLightOutputTarget, + OutputTarget, + OutputTargetListResponse, +} from './types/output-target.ts'; // ── Color Strip Source ──────────────────────────────────────── - -export type CSSSourceType = - | 'picture' | 'picture_advanced' | 'single_color' | 'gradient' - | 'effect' | 'composite' | 'mapped' - | 'audio' | 'api_input' | 'notification' | 'daylight' - | 'candlelight' | 'processed' | 'weather' | 'key_colors' - | 'game_event' | 'math_wave'; - -export interface ColorStop { - position: number; - color: number[]; - color_right?: number[]; -} - -export interface CompositeLayer { - source_id: string; - blend_mode: string; - opacity: number; - enabled: boolean; - brightness_source_id?: string; - processing_template_id?: string; -} - -export interface MappedZone { - source_id: string; - start: number; - end: number; - reverse: boolean; -} - -export interface AnimationConfig { - enabled: boolean; - type: string; - speed: number; -} - -export interface CalibrationLine { - picture_source_id: string; - edge: 'top' | 'right' | 'bottom' | 'left'; - led_count: number; - span_start: number; - span_end: number; - reverse: boolean; - border_width: number; -} - -export interface Calibration { - mode: 'simple' | 'advanced'; - lines?: CalibrationLine[]; - layout?: 'clockwise' | 'counterclockwise'; - start_position?: 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right'; - offset?: number; - leds_top?: number; - leds_right?: number; - leds_bottom?: number; - leds_left?: number; - span_top_start?: number; - span_top_end?: number; - span_right_start?: number; - span_right_end?: number; - span_bottom_start?: number; - span_bottom_end?: number; - span_left_start?: number; - span_left_end?: number; - skip_leds_start?: number; - skip_leds_end?: number; - border_width?: number; -} - -export interface ColorStripSource { - id: string; - name: string; - source_type: CSSSourceType; - led_count: number; - description?: string; - tags: string[]; - overlay_active: boolean; - clock_id?: string; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; - - // Picture - picture_source_id?: string; - smoothing?: BindableFloat; - interpolation_mode?: string; - calibration?: Calibration; - - // Static / Effect / Candlelight - color?: BindableColor; - - // Gradient - stops?: ColorStop[]; - - // Effect - effect_type?: string; - palette?: string; - intensity?: BindableFloat; - scale?: BindableFloat; - mirror?: boolean; - - // Composite - layers?: CompositeLayer[]; - - // Mapped - zones?: MappedZone[]; - - // Audio - visualization_mode?: string; - audio_source_id?: string; - sensitivity?: BindableFloat; - color_peak?: BindableColor; - - // Animation - animation?: AnimationConfig; - speed?: BindableFloat; - - // API Input - fallback_color?: BindableColor; - timeout?: BindableFloat; - interpolation?: string; - - // Notification - notification_effect?: string; - duration_ms?: number; - default_color?: BindableColor | string; - app_colors?: Record; - app_filter_mode?: string; - app_filter_list?: string[]; - os_listener?: boolean; - sound_asset_id?: string | null; - sound_volume?: BindableFloat; - app_sounds?: Record; - - // Daylight - use_real_time?: boolean; - latitude?: number; - longitude?: number; - - // Candlelight - num_candles?: number; - wind_strength?: BindableFloat; - - // Processed - input_source_id?: string; - processing_template_id?: string; - - // Weather - weather_source_id?: string; - temperature_influence?: BindableFloat; - - // Key Colors - rectangles?: KeyColorRectangle[]; - brightness?: BindableFloat; - - // Game Event - game_integration_id?: string; - idle_color?: BindableColor; - event_mappings?: GameEventMapping[]; - - // Math Wave - waves?: Array<{ waveform: string; frequency: number; amplitude: number; phase: number; offset: number }>; - gradient_id?: string; -} +export type { + CSSSourceType, + ColorStop, + CompositeLayer, + MappedZone, + AnimationConfig, + CalibrationLine, + Calibration, + ColorStripSource, + ColorStripSourceListResponse, +} from './types/color-strip.ts'; // ── Pattern Template ────────────────────────────────────────── - -export interface KeyColorRectangle { - name: string; - x: number; - y: number; - width: number; - height: number; -} - -export interface PatternTemplate { - id: string; - name: string; - rectangles: KeyColorRectangle[]; - tags: string[]; - description?: string; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} +export type { + KeyColorRectangle, + PatternTemplate, + PatternTemplateListResponse, +} from './types/pattern-template.ts'; // ── Value Source ─────────────────────────────────────────────── - -export type ValueSourceType = - | 'static' | 'animated' | 'audio' - | 'adaptive_time' | 'adaptive_scene' | 'daylight' - | 'static_color' | 'animated_color' | 'adaptive_time_color' - | 'ha_entity' | 'gradient_map' | 'css_extract' - | 'system_metrics' | 'game_event' | 'http'; - -export interface SchedulePoint { - time: string; - value: number; -} - -export interface ColorSchedulePoint { - time: string; - color: number[]; -} - -interface ValueSourceBase { - id: string; - name: string; - source_type: ValueSourceType; - return_type: 'float' | 'color'; - description?: string; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface StaticValueSource extends ValueSourceBase { - source_type: 'static'; - return_type: 'float'; - value: number; -} - -export interface AnimatedValueSource extends ValueSourceBase { - source_type: 'animated'; - return_type: 'float'; - waveform: string; - speed: number; - min_value: number; - max_value: number; -} - -export interface AudioValueSource extends ValueSourceBase { - source_type: 'audio'; - return_type: 'float'; - audio_source_id: string; - mode: string; - sensitivity: number; - smoothing: number; - min_value: number; - max_value: number; - auto_gain: boolean; -} - -export interface AdaptiveTimeValueSource extends ValueSourceBase { - source_type: 'adaptive_time'; - return_type: 'float'; - schedule: SchedulePoint[]; - min_value: number; - max_value: number; -} - -export interface AdaptiveSceneValueSource extends ValueSourceBase { - source_type: 'adaptive_scene'; - return_type: 'float'; - picture_source_id: string; - scene_behavior: string; - sensitivity: number; - smoothing: number; - min_value: number; - max_value: number; -} - -export interface DaylightValueSource extends ValueSourceBase { - source_type: 'daylight'; - return_type: 'float'; - speed: number; - use_real_time: boolean; - latitude: number; - longitude: number; - min_value: number; - max_value: number; -} - -export interface StaticColorValueSource extends ValueSourceBase { - source_type: 'static_color'; - return_type: 'color'; - color: number[]; -} - -export interface AnimatedColorValueSource extends ValueSourceBase { - source_type: 'animated_color'; - return_type: 'color'; - colors: number[][]; - speed: number; - easing: string; - clock_id?: string; -} - -export interface AdaptiveTimeColorValueSource extends ValueSourceBase { - source_type: 'adaptive_time_color'; - return_type: 'color'; - schedule: ColorSchedulePoint[]; -} - -export interface HAEntityValueSource extends ValueSourceBase { - source_type: 'ha_entity'; - return_type: 'float'; - ha_source_id: string; - entity_id: string; - attribute: string; - min_ha_value: number; - max_ha_value: number; - smoothing: number; -} - -export interface GradientMapValueSource extends ValueSourceBase { - source_type: 'gradient_map'; - return_type: 'color'; - value_source_id: string; - gradient_id: string; - easing: string; -} - -export interface CSSExtractValueSource extends ValueSourceBase { - source_type: 'css_extract'; - return_type: 'color'; - color_strip_source_id: string; - led_start: number; - led_end: number; -} - -export interface SystemMetricsValueSource extends ValueSourceBase { - source_type: 'system_metrics'; - return_type: 'float'; - metric: string; - min_value: number; - max_value: number; - max_rate: number; - disk_path: string; - sensor_label: string; - poll_interval: number; - smoothing: number; -} - -export interface GameEventValueSource extends ValueSourceBase { - source_type: 'game_event'; - return_type: 'float'; - game_integration_id: string; - event_type: string; - min_game_value: number; - max_game_value: number; - smoothing: number; - default_value: number; - timeout: number; -} - -export interface HTTPValueSource extends ValueSourceBase { - source_type: 'http'; - return_type: 'float'; - http_endpoint_id: string; - json_path: string; - interval_s: number; - min_value: number; - max_value: number; - smoothing: number; -} - -export type ValueSource = - | StaticValueSource - | AnimatedValueSource - | AudioValueSource - | AdaptiveTimeValueSource - | AdaptiveSceneValueSource - | DaylightValueSource - | StaticColorValueSource - | AnimatedColorValueSource - | AdaptiveTimeColorValueSource - | HAEntityValueSource - | GradientMapValueSource - | CSSExtractValueSource - | SystemMetricsValueSource - | GameEventValueSource - | HTTPValueSource; +export type { + ValueSourceType, + SchedulePoint, + ColorSchedulePoint, + StaticValueSource, + AnimatedValueSource, + AudioValueSource, + AdaptiveTimeValueSource, + AdaptiveSceneValueSource, + DaylightValueSource, + StaticColorValueSource, + AnimatedColorValueSource, + AdaptiveTimeColorValueSource, + HAEntityValueSource, + GradientMapValueSource, + CSSExtractValueSource, + SystemMetricsValueSource, + GameEventValueSource, + HTTPValueSource, + ValueSource, + ValueSourceListResponse, +} from './types/value-source.ts'; // ── Audio Source ─────────────────────────────────────────────── - -export type AudioSourceType = 'capture' | 'processed'; - -interface AudioSourceBase { - id: string; - name: string; - source_type: AudioSourceType; - description?: string; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface CaptureAudioSource extends AudioSourceBase { - source_type: 'capture'; - device_index: number; - is_loopback: boolean; - audio_template_id?: string; -} - -export interface ProcessedAudioSource extends AudioSourceBase { - source_type: 'processed'; - audio_source_id: string; - audio_processing_template_id: string; -} - -export type AudioSource = - | CaptureAudioSource - | ProcessedAudioSource; +export type { + AudioSourceType, + CaptureAudioSource, + ProcessedAudioSource, + AudioSource, + AudioSourceListResponse, +} from './types/audio-source.ts'; // ── Picture Source ───────────────────────────────────────────── - -export type PictureSourceType = 'raw' | 'processed' | 'static_image' | 'video'; - -interface PictureSourceBase { - id: string; - name: string; - stream_type: PictureSourceType; - description?: string; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface RawPictureSource extends PictureSourceBase { - stream_type: 'raw'; - display_index: number; - capture_template_id: string; - target_fps: number; -} - -export interface ProcessedPictureSource extends PictureSourceBase { - stream_type: 'processed'; - source_stream_id: string; - postprocessing_template_id: string; -} - -export interface StaticImagePictureSource extends PictureSourceBase { - stream_type: 'static_image'; - image_asset_id?: string; -} - -export interface VideoPictureSource extends PictureSourceBase { - stream_type: 'video'; - video_asset_id?: string; - loop: boolean; - playback_speed: number; - start_time?: number; - end_time?: number; - resolution_limit?: number; - clock_id?: string; - target_fps: number; -} - -export type PictureSource = - | RawPictureSource - | ProcessedPictureSource - | StaticImagePictureSource - | VideoPictureSource; +export type { + PictureSourceType, + RawPictureSource, + ProcessedPictureSource, + StaticImagePictureSource, + VideoPictureSource, + PictureSource, + PictureSourceListResponse, +} from './types/picture-source.ts'; // ── Scene Preset ────────────────────────────────────────────── - -export interface TargetSnapshot { - id?: string; - target_id: string; - running: boolean; - color_strip_source_id: string; - brightness?: BindableFloat; - fps: number; -} - -export interface ScenePreset { - id: string; - name: string; - description: string; - color?: string; - targets: TargetSnapshot[]; - order: number; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} +export type { + TargetSnapshot, + ScenePreset, + ScenePresetListResponse, +} from './types/scene-preset.ts'; // ── Sync Clock ──────────────────────────────────────────────── +export type { SyncClock, SyncClockListResponse } from './types/sync-clock.ts'; -export interface SyncClock { - id: string; - name: string; - speed: number; - description?: string; - tags: string[]; - is_running: boolean; - elapsed_time: number; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface WeatherSource { - id: string; - name: string; - provider: string; - provider_config: Record; - latitude: number; - longitude: number; - update_interval: number; - description?: string; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface WeatherSourceListResponse { - sources: WeatherSource[]; - count: number; -} +// ── Weather Source ──────────────────────────────────────────── +export type { WeatherSource, WeatherSourceListResponse } from './types/weather-source.ts'; // ── Home Assistant Source ──────────────────────────────────── - -export interface HomeAssistantSource { - id: string; - name: string; - host: string; - use_ssl: boolean; - entity_filters: string[]; - connected: boolean; - entity_count: number; - description?: string; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface HomeAssistantSourceListResponse { - sources: HomeAssistantSource[]; - count: number; -} - -export interface HomeAssistantConnectionStatus { - source_id: string; - name: string; - connected: boolean; - entity_count: number; - host?: string; -} - -export interface HomeAssistantStatusResponse { - connections: HomeAssistantConnectionStatus[]; - total_sources: number; - connected_count: number; -} +export type { + HomeAssistantSource, + HomeAssistantSourceListResponse, + HomeAssistantConnectionStatus, + HomeAssistantStatusResponse, +} from './types/home-assistant.ts'; // ── MQTT Source ──────────────────────────────────────────────── - -export interface MQTTSource { - id: string; - name: string; - broker_host: string; - broker_port: number; - username: string; - password_set: boolean; - client_id: string; - base_topic: string; - connected: boolean; - description?: string; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface MQTTSourceListResponse { - sources: MQTTSource[]; - count: number; -} - -export interface MQTTConnectionStatus { - source_id: string; - name: string; - connected: boolean; - broker: string; -} - -export interface MQTTStatusResponse { - connections: MQTTConnectionStatus[]; - total_sources: number; - connected_count: number; -} +export type { + MQTTSource, + MQTTSourceListResponse, + MQTTConnectionStatus, + MQTTStatusResponse, +} from './types/mqtt.ts'; // ── HTTP Endpoint ──────────────────────────────────────────── -// -// A connection definition only (URL + auth + headers + timeout). -// No polling cadence is configured on the endpoint itself — -// HTTPValueSource owns interval_s and references the endpoint. - -export type HTTPMethod = 'GET' | 'HEAD'; - -export interface HTTPEndpoint { - id: string; - name: string; - url: string; - method: HTTPMethod; - /** Server NEVER returns the token; this flag indicates one is stored. */ - auth_token_set: boolean; - headers: Record; - timeout_s: number; - description?: string; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface HTTPEndpointListResponse { - endpoints: HTTPEndpoint[]; - count: number; -} - -/** Wire payload for `POST /http/endpoints` / `PUT /http/endpoints/{id}`. - * All fields optional — the route validates required-on-create separately. */ -export interface HTTPEndpointWritePayload { - name?: string; - url?: string; - method?: HTTPMethod; - /** Plaintext token. PUT distinguishes None=keep / ""=clear; omit the field to keep. */ - auth_token?: string; - headers?: Record; - timeout_s?: number; - description?: string; - tags?: string[]; - icon?: string; - icon_color?: string; -} - -export interface HTTPTestRequest { - url: string; - method: HTTPMethod; - auth_token: string; - headers: Record; - timeout_s: number; -} - -export interface HTTPTestResponse { - success: boolean; - status_code?: number; - body_preview?: string; - body_json?: unknown; - error?: string; -} +export type { + HTTPMethod, + HTTPEndpoint, + HTTPEndpointListResponse, + HTTPEndpointWritePayload, + HTTPTestRequest, + HTTPTestResponse, +} from './types/http-endpoint.ts'; // ── Asset ──────────────────────────────────────────────────── - -export interface Asset { - id: string; - name: string; - filename: string; - mime_type: string; - asset_type: string; - size_bytes: number; - description?: string; - tags: string[]; - prebuilt: boolean; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface AssetListResponse { - assets: Asset[]; - count: number; -} +export type { Asset, AssetListResponse } from './types/asset.ts'; // ── Automation ──────────────────────────────────────────────── +export type { + RuleType, + HTTPPollOperator, + AutomationRule, + Automation, + AutomationListResponse, +} from './types/automation.ts'; -export type RuleType = - | 'application' | 'time_of_day' | 'system_idle' - | 'display_state' | 'mqtt' | 'webhook' | 'startup' - | 'home_assistant' | 'http_poll'; - -export type HTTPPollOperator = - | 'equals' | 'not_equals' | 'contains' | 'regex' - | 'gt' | 'lt' | 'exists'; - -export interface AutomationRule { - rule_type: RuleType; - apps?: string[]; - match_type?: string; - start_time?: string; - end_time?: string; - idle_minutes?: number; - when_idle?: boolean; - state?: string; - topic?: string; - payload?: string; - match_mode?: string; - token?: string; - /** home_assistant rule */ - ha_source_id?: string; - entity_id?: string; - /** http_poll rule — references an HTTPValueSource. */ - value_source_id?: string; - operator?: HTTPPollOperator; - value?: string; -} - -export interface Automation { - id: string; - name: string; - enabled: boolean; - rule_logic: 'or' | 'and'; - rules: AutomationRule[]; - scene_preset_id?: string; - deactivation_mode: 'none' | 'revert' | 'fallback_scene'; - deactivation_scene_preset_id?: string; - tags: string[]; - webhook_url?: string; - is_active: boolean; - last_activated_at?: string; - last_deactivated_at?: string; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -// ── Templates ───────────────────────────────────────────────── - -export interface FilterInstance { - filter_id: string; - options: Record; -} - -export interface CaptureTemplate { - id: string; - name: string; - engine_type: string; - engine_config: Record; - tags: string[]; - description?: string; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface PostprocessingTemplate { - id: string; - name: string; - filters: FilterInstance[]; - tags: string[]; - description?: string; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface ColorStripProcessingTemplate { - id: string; - name: string; - filters: FilterInstance[]; - tags: string[]; - description?: string; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface AudioTemplate { - id: string; - name: string; - engine_type: string; - engine_config: Record; - tags: string[]; - description?: string; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -// ── Filter Definition (from /filters endpoint) ──────────────── - -export interface FilterOptionDef { - type: string; - default?: any; - min?: number; - max?: number; - step?: number; - choices?: string[]; - label?: string; -} - -export interface FilterDef { - id: string; - name: string; - description?: string; - category?: string; - options: Record; -} - -// ── Engine Info (from /capture-engines, /audio-engines) ─────── - -export interface EngineInfo { - type: string; - name: string; - available: boolean; - has_own_displays?: boolean; - default_config?: Record; - config_choices?: Record; -} +// ── Templates / Filters / Engines ───────────────────────────── +export type { + FilterInstance, + CaptureTemplate, + PostprocessingTemplate, + ColorStripProcessingTemplate, + AudioTemplate, + FilterOptionDef, + FilterDef, + EngineInfo, +} from './types/template.ts'; // ── Display ─────────────────────────────────────────────────── - -export interface Display { - index: number; - name: string; - width: number; - height: number; - x: number; - y: number; - is_primary: boolean; -} - -// ── API List Response Wrappers ──────────────────────────────── - -export interface DeviceListResponse { - devices: Device[]; - count: number; -} - -export interface OutputTargetListResponse { - targets: OutputTarget[]; - count: number; -} - -export interface ColorStripSourceListResponse { - sources: ColorStripSource[]; - count: number; -} - -export interface PatternTemplateListResponse { - templates: PatternTemplate[]; - count: number; -} - -export interface ValueSourceListResponse { - sources: ValueSource[]; - count: number; -} - -export interface AudioSourceListResponse { - sources: AudioSource[]; - count: number; -} - -export interface PictureSourceListResponse { - streams: PictureSource[]; - count: number; -} - -export interface ScenePresetListResponse { - presets: ScenePreset[]; - count: number; -} - -export interface SyncClockListResponse { - clocks: SyncClock[]; - count: number; -} - -export interface AutomationListResponse { - automations: Automation[]; - count: number; -} +export type { Display } from './types/display.ts'; // ── Game Integration ───────────────────────────────────────── - -export interface GameEventMapping { - event_type: string; - effect_type: string; - color: number[]; - duration_ms: number; - intensity: number; - priority: number; -} - -export interface GameIntegration { - id: string; - name: string; - adapter_type: string; - adapter_config: Record; - event_mappings: GameEventMapping[]; - enabled: boolean; - description?: string; - tags: string[]; - icon?: string; - icon_color?: string; - created_at: string; - updated_at: string; -} - -export interface GameIntegrationListResponse { - integrations: GameIntegration[]; - count: number; -} - -export interface GameAdapterConfigField { - name: string; - type: string; - label?: string; - default?: any; - required?: boolean; - hint?: string; -} - -export interface GameAdapterInfo { - adapter_type: string; - display_name: string; - game_name: string; - supported_events: string[]; - config_schema: GameAdapterConfigField[]; - setup_instructions?: string; - supports_auto_setup?: boolean; -} - -export interface GameAdapterListResponse { - adapters: GameAdapterInfo[]; -} - -export interface GameEventRecord { - timestamp: string; - event_type: string; - value?: number; - data?: Record; -} - -export interface GameIntegrationStatus { - integration_id: string; - connected: boolean; - last_event_at?: string; - event_count: number; - error?: string; -} - -export interface EffectPreset { - key: string; - name: string; - description: string; - target_game_types: string[]; - event_mappings: GameEventMapping[]; -} +export type { + GameEventMapping, + GameIntegration, + GameIntegrationListResponse, + GameAdapterConfigField, + GameAdapterInfo, + GameAdapterListResponse, + GameEventRecord, + GameIntegrationStatus, + EffectPreset, +} from './types/game-integration.ts'; // ── Component Option Types (re-exported from authoritative sources) ─── - export type { IconSelectItem, IconSelectOpts } from './core/icon-select.ts'; export type { EntitySelectOpts } from './core/entity-palette.ts'; export type { BulkAction, CardItem, CardSectionOpts } from './core/card-sections.ts'; diff --git a/server/src/ledgrab/static/js/types/asset.ts b/server/src/ledgrab/static/js/types/asset.ts new file mode 100644 index 0000000..6ac57bf --- /dev/null +++ b/server/src/ledgrab/static/js/types/asset.ts @@ -0,0 +1,25 @@ +/** + * Asset shapes — uploaded/prebuilt media (images, video, sound) keyed by + * id and referenced from static-image / video / notification sources. + */ + +export interface Asset { + id: string; + name: string; + filename: string; + mime_type: string; + asset_type: string; + size_bytes: number; + description?: string; + tags: string[]; + prebuilt: boolean; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface AssetListResponse { + assets: Asset[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/audio-source.ts b/server/src/ledgrab/static/js/types/audio-source.ts new file mode 100644 index 0000000..9c4dedd --- /dev/null +++ b/server/src/ledgrab/static/js/types/audio-source.ts @@ -0,0 +1,40 @@ +/** + * Audio source shapes — capture (device) and processed (template-driven) + * variants, discriminated on `source_type`. + */ + +export type AudioSourceType = 'capture' | 'processed'; + +interface AudioSourceBase { + id: string; + name: string; + source_type: AudioSourceType; + description?: string; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface CaptureAudioSource extends AudioSourceBase { + source_type: 'capture'; + device_index: number; + is_loopback: boolean; + audio_template_id?: string; +} + +export interface ProcessedAudioSource extends AudioSourceBase { + source_type: 'processed'; + audio_source_id: string; + audio_processing_template_id: string; +} + +export type AudioSource = + | CaptureAudioSource + | ProcessedAudioSource; + +export interface AudioSourceListResponse { + sources: AudioSource[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/automation.ts b/server/src/ledgrab/static/js/types/automation.ts new file mode 100644 index 0000000..41f1ac8 --- /dev/null +++ b/server/src/ledgrab/static/js/types/automation.ts @@ -0,0 +1,62 @@ +/** + * Automation shapes — rule sets (`AutomationRule[]`) combined with + * AND/OR logic that activate a scene preset. `AutomationRule` is a wide + * optional-field shape keyed by `rule_type`; see audit finding H8 for the + * frontend rule-type registry that dispatches on it. + */ + +export type RuleType = + | 'application' | 'time_of_day' | 'system_idle' + | 'display_state' | 'mqtt' | 'webhook' | 'startup' + | 'home_assistant' | 'http_poll'; + +export type HTTPPollOperator = + | 'equals' | 'not_equals' | 'contains' | 'regex' + | 'gt' | 'lt' | 'exists'; + +export interface AutomationRule { + rule_type: RuleType; + apps?: string[]; + match_type?: string; + start_time?: string; + end_time?: string; + idle_minutes?: number; + when_idle?: boolean; + state?: string; + topic?: string; + payload?: string; + match_mode?: string; + token?: string; + /** home_assistant rule */ + ha_source_id?: string; + entity_id?: string; + /** http_poll rule — references an HTTPValueSource. */ + value_source_id?: string; + operator?: HTTPPollOperator; + value?: string; +} + +export interface Automation { + id: string; + name: string; + enabled: boolean; + rule_logic: 'or' | 'and'; + rules: AutomationRule[]; + scene_preset_id?: string; + deactivation_mode: 'none' | 'revert' | 'fallback_scene'; + deactivation_scene_preset_id?: string; + tags: string[]; + webhook_url?: string; + is_active: boolean; + last_activated_at?: string; + last_deactivated_at?: string; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface AutomationListResponse { + automations: Automation[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/color-strip.ts b/server/src/ledgrab/static/js/types/color-strip.ts new file mode 100644 index 0000000..503c63b --- /dev/null +++ b/server/src/ledgrab/static/js/types/color-strip.ts @@ -0,0 +1,181 @@ +/** + * Color strip source (CSS) shapes — the per-source-type field bag plus + * the supporting structures (gradient stops, composite layers, mapped + * zones, calibration). `ColorStripSource` is a wide optional-field shape + * because the backend stores all source types in one collection keyed by + * `source_type`. + */ + +import type { BindableColor, BindableFloat } from './bindable.ts'; +import type { KeyColorRectangle } from './pattern-template.ts'; +import type { GameEventMapping } from './game-integration.ts'; + +export type CSSSourceType = + | 'picture' | 'picture_advanced' | 'single_color' | 'gradient' + | 'effect' | 'composite' | 'mapped' + | 'audio' | 'api_input' | 'notification' | 'daylight' + | 'candlelight' | 'processed' | 'weather' | 'key_colors' + | 'game_event' | 'math_wave'; + +export interface ColorStop { + position: number; + color: number[]; + color_right?: number[]; +} + +export interface CompositeLayer { + source_id: string; + blend_mode: string; + opacity: number; + enabled: boolean; + brightness_source_id?: string; + processing_template_id?: string; +} + +export interface MappedZone { + source_id: string; + start: number; + end: number; + reverse: boolean; +} + +export interface AnimationConfig { + enabled: boolean; + type: string; + speed: number; +} + +export interface CalibrationLine { + picture_source_id: string; + edge: 'top' | 'right' | 'bottom' | 'left'; + led_count: number; + span_start: number; + span_end: number; + reverse: boolean; + border_width: number; +} + +export interface Calibration { + mode: 'simple' | 'advanced'; + lines?: CalibrationLine[]; + layout?: 'clockwise' | 'counterclockwise'; + start_position?: 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right'; + offset?: number; + leds_top?: number; + leds_right?: number; + leds_bottom?: number; + leds_left?: number; + span_top_start?: number; + span_top_end?: number; + span_right_start?: number; + span_right_end?: number; + span_bottom_start?: number; + span_bottom_end?: number; + span_left_start?: number; + span_left_end?: number; + skip_leds_start?: number; + skip_leds_end?: number; + border_width?: number; +} + +export interface ColorStripSource { + id: string; + name: string; + source_type: CSSSourceType; + led_count: number; + description?: string; + tags: string[]; + overlay_active: boolean; + clock_id?: string; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; + + // Picture + picture_source_id?: string; + smoothing?: BindableFloat; + interpolation_mode?: string; + calibration?: Calibration; + + // Static / Effect / Candlelight + color?: BindableColor; + + // Gradient + stops?: ColorStop[]; + + // Effect + effect_type?: string; + palette?: string; + intensity?: BindableFloat; + scale?: BindableFloat; + mirror?: boolean; + + // Composite + layers?: CompositeLayer[]; + + // Mapped + zones?: MappedZone[]; + + // Audio + visualization_mode?: string; + audio_source_id?: string; + sensitivity?: BindableFloat; + color_peak?: BindableColor; + + // Animation + animation?: AnimationConfig; + speed?: BindableFloat; + + // API Input + fallback_color?: BindableColor; + timeout?: BindableFloat; + interpolation?: string; + + // Notification + notification_effect?: string; + duration_ms?: number; + default_color?: BindableColor | string; + app_colors?: Record; + app_filter_mode?: string; + app_filter_list?: string[]; + os_listener?: boolean; + sound_asset_id?: string | null; + sound_volume?: BindableFloat; + app_sounds?: Record; + + // Daylight + use_real_time?: boolean; + latitude?: number; + longitude?: number; + + // Candlelight + num_candles?: number; + wind_strength?: BindableFloat; + + // Processed + input_source_id?: string; + processing_template_id?: string; + + // Weather + weather_source_id?: string; + temperature_influence?: BindableFloat; + + // Key Colors + rectangles?: KeyColorRectangle[]; + brightness?: BindableFloat; + + // Game Event + game_integration_id?: string; + idle_color?: BindableColor; + event_mappings?: GameEventMapping[]; + + // Math Wave + waves?: Array<{ waveform: string; frequency: number; amplitude: number; phase: number; offset: number }>; + gradient_id?: string; +} + +export interface ColorStripSourceListResponse { + sources: ColorStripSource[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/device.ts b/server/src/ledgrab/static/js/types/device.ts new file mode 100644 index 0000000..2fac06d --- /dev/null +++ b/server/src/ledgrab/static/js/types/device.ts @@ -0,0 +1,66 @@ +/** + * Device entity shapes — physical/logical LED controllers and groups. + * + * Mirrors the backend `storage/device_store.py` dataclass and the + * `api/schemas/devices.py` Pydantic models. Field names use snake_case + * to match the JSON payloads. + */ + +export type DeviceType = + | 'wled' | 'adalight' | 'ambiled' | 'mock' | 'mqtt' | 'ws' + | 'openrgb' | 'dmx' | 'ddp' | 'opc' | 'espnow' | 'hue' | 'yeelight' | 'wiz' | 'lifx' | 'govee' + | 'nanoleaf' + | 'ble' | 'usbhid' | 'spi' + | 'chroma' | 'gamesense' | 'group'; + +export interface Device { + id: string; + name: string; + url: string; + device_type: DeviceType; + led_count: number; + enabled: boolean; + baud_rate?: number; + auto_shutdown: boolean; + send_latency_ms: number; + rgbw: boolean; + zone_mode: string; + capabilities: string[]; + tags: string[]; + dmx_protocol: string; + dmx_start_universe: number; + dmx_start_channel: number; + ddp_port: number; + ddp_destination_id: number; + ddp_color_order: number; + opc_channel: number; + espnow_peer_mac: string; + espnow_channel: number; + hue_paired: boolean; + hue_entertainment_group_id: string; + yeelight_min_interval_ms: number; + wiz_min_interval_ms: number; + lifx_min_interval_ms: number; + govee_min_interval_ms: number; + nanoleaf_paired: boolean; + nanoleaf_min_interval_ms: number; + spi_speed_hz: number; + spi_led_type: string; + chroma_device_type: string; + gamesense_device_type: string; + default_css_processing_template_id: string; + group_device_ids: string[]; + group_mode: string; + /** Optional id from the curated icon library (e.g. 'mouse', 'motherboard'). + * Empty/missing → no plate is rendered, head reverts to badge-only layout. */ + icon?: string; + /** Optional CSS color override for the icon. Empty/missing inherits --ch. */ + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface DeviceListResponse { + devices: Device[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/display.ts b/server/src/ledgrab/static/js/types/display.ts new file mode 100644 index 0000000..f081824 --- /dev/null +++ b/server/src/ledgrab/static/js/types/display.ts @@ -0,0 +1,14 @@ +/** + * Display shape — a detected monitor as returned by + * `GET /api/v1/config/displays`. + */ + +export interface Display { + index: number; + name: string; + width: number; + height: number; + x: number; + y: number; + is_primary: boolean; +} diff --git a/server/src/ledgrab/static/js/types/game-integration.ts b/server/src/ledgrab/static/js/types/game-integration.ts new file mode 100644 index 0000000..a6d6dfe --- /dev/null +++ b/server/src/ledgrab/static/js/types/game-integration.ts @@ -0,0 +1,79 @@ +/** + * Game integration shapes — adapters (Chroma, GameSense, …), their + * event→effect mappings, runtime status, and curated effect presets. + */ + +export interface GameEventMapping { + event_type: string; + effect_type: string; + color: number[]; + duration_ms: number; + intensity: number; + priority: number; +} + +export interface GameIntegration { + id: string; + name: string; + adapter_type: string; + adapter_config: Record; + event_mappings: GameEventMapping[]; + enabled: boolean; + description?: string; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface GameIntegrationListResponse { + integrations: GameIntegration[]; + count: number; +} + +export interface GameAdapterConfigField { + name: string; + type: string; + label?: string; + default?: any; + required?: boolean; + hint?: string; +} + +export interface GameAdapterInfo { + adapter_type: string; + display_name: string; + game_name: string; + supported_events: string[]; + config_schema: GameAdapterConfigField[]; + setup_instructions?: string; + supports_auto_setup?: boolean; +} + +export interface GameAdapterListResponse { + adapters: GameAdapterInfo[]; +} + +export interface GameEventRecord { + timestamp: string; + event_type: string; + value?: number; + data?: Record; +} + +export interface GameIntegrationStatus { + integration_id: string; + connected: boolean; + last_event_at?: string; + event_count: number; + error?: string; +} + +export interface EffectPreset { + key: string; + name: string; + description: string; + target_game_types: string[]; + event_mappings: GameEventMapping[]; +} diff --git a/server/src/ledgrab/static/js/types/home-assistant.ts b/server/src/ledgrab/static/js/types/home-assistant.ts new file mode 100644 index 0000000..d6e480e --- /dev/null +++ b/server/src/ledgrab/static/js/types/home-assistant.ts @@ -0,0 +1,39 @@ +/** + * Home Assistant source shapes — a HA connection plus its live + * connection-status projections used by the dashboard integration card. + */ + +export interface HomeAssistantSource { + id: string; + name: string; + host: string; + use_ssl: boolean; + entity_filters: string[]; + connected: boolean; + entity_count: number; + description?: string; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface HomeAssistantSourceListResponse { + sources: HomeAssistantSource[]; + count: number; +} + +export interface HomeAssistantConnectionStatus { + source_id: string; + name: string; + connected: boolean; + entity_count: number; + host?: string; +} + +export interface HomeAssistantStatusResponse { + connections: HomeAssistantConnectionStatus[]; + total_sources: number; + connected_count: number; +} diff --git a/server/src/ledgrab/static/js/types/http-endpoint.ts b/server/src/ledgrab/static/js/types/http-endpoint.ts new file mode 100644 index 0000000..f92660b --- /dev/null +++ b/server/src/ledgrab/static/js/types/http-endpoint.ts @@ -0,0 +1,63 @@ +/** + * HTTP endpoint shapes. + * + * A connection definition only (URL + auth + headers + timeout). + * No polling cadence is configured on the endpoint itself — + * HTTPValueSource owns interval_s and references the endpoint. + */ + +export type HTTPMethod = 'GET' | 'HEAD'; + +export interface HTTPEndpoint { + id: string; + name: string; + url: string; + method: HTTPMethod; + /** Server NEVER returns the token; this flag indicates one is stored. */ + auth_token_set: boolean; + headers: Record; + timeout_s: number; + description?: string; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface HTTPEndpointListResponse { + endpoints: HTTPEndpoint[]; + count: number; +} + +/** Wire payload for `POST /http/endpoints` / `PUT /http/endpoints/{id}`. + * All fields optional — the route validates required-on-create separately. */ +export interface HTTPEndpointWritePayload { + name?: string; + url?: string; + method?: HTTPMethod; + /** Plaintext token. PUT distinguishes None=keep / ""=clear; omit the field to keep. */ + auth_token?: string; + headers?: Record; + timeout_s?: number; + description?: string; + tags?: string[]; + icon?: string; + icon_color?: string; +} + +export interface HTTPTestRequest { + url: string; + method: HTTPMethod; + auth_token: string; + headers: Record; + timeout_s: number; +} + +export interface HTTPTestResponse { + success: boolean; + status_code?: number; + body_preview?: string; + body_json?: unknown; + error?: string; +} diff --git a/server/src/ledgrab/static/js/types/mqtt.ts b/server/src/ledgrab/static/js/types/mqtt.ts new file mode 100644 index 0000000..6d1b18f --- /dev/null +++ b/server/src/ledgrab/static/js/types/mqtt.ts @@ -0,0 +1,40 @@ +/** + * MQTT source shapes — a broker connection plus its live connection + * status. Backs Zigbee2MQTT light targets and MQTT automation rules. + */ + +export interface MQTTSource { + id: string; + name: string; + broker_host: string; + broker_port: number; + username: string; + password_set: boolean; + client_id: string; + base_topic: string; + connected: boolean; + description?: string; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface MQTTSourceListResponse { + sources: MQTTSource[]; + count: number; +} + +export interface MQTTConnectionStatus { + source_id: string; + name: string; + connected: boolean; + broker: string; +} + +export interface MQTTStatusResponse { + connections: MQTTConnectionStatus[]; + total_sources: number; + connected_count: number; +} diff --git a/server/src/ledgrab/static/js/types/output-target.ts b/server/src/ledgrab/static/js/types/output-target.ts new file mode 100644 index 0000000..b76a5e1 --- /dev/null +++ b/server/src/ledgrab/static/js/types/output-target.ts @@ -0,0 +1,94 @@ +/** + * Output target shapes — the discriminated union over `target_type` + * (`led` | `ha_light` | `z2m_light`). Each target binds a colour source + * to a physical/logical output. + */ + +import type { BindableFloat } from './bindable.ts'; + +export type TargetType = 'led' | 'ha_light' | 'z2m_light'; + +export interface HALightMapping { + entity_id: string; + led_start: number; + led_end: number; + brightness_scale: BindableFloat; +} + +export interface Z2MLightMapping { + friendly_name: string; + led_start: number; + led_end: number; + brightness_scale: BindableFloat; +} + +interface OutputTargetBase { + id: string; + name: string; + target_type: TargetType; + description?: string; + tags: string[]; + /** Optional id from the curated icon library. Empty/missing → + * for LED targets, the card inherits the device's icon; for + * HA-light targets, no plate is rendered. */ + icon?: string; + /** Optional CSS color override for the icon. Empty/missing → + * inherits the device color (LED targets) or --ch (others). */ + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface LedOutputTarget extends OutputTargetBase { + target_type: 'led'; + device_id: string; + color_strip_source_id: string; + brightness?: BindableFloat; + fps?: BindableFloat; + keepalive_interval: number; + state_check_interval: number; + min_brightness_threshold?: BindableFloat; + adaptive_fps: boolean; + protocol: string; +} + +export type HALightSourceKind = 'css' | 'color_vs'; + +export interface HALightOutputTarget extends OutputTargetBase { + target_type: 'ha_light'; + ha_source_id: string; + /** Which colour source feeds the lights: a CSS (`'css'`) or a colour-returning value source (`'color_vs'`). */ + source_kind: HALightSourceKind; + color_strip_source_id: string; + /** Used when `source_kind === 'color_vs'`. References a value source whose `return_type === 'color'`. */ + color_value_source_id?: string; + brightness?: BindableFloat; + ha_light_mappings?: HALightMapping[]; + update_rate?: BindableFloat; + transition?: BindableFloat; + color_tolerance?: BindableFloat; + min_brightness_threshold?: BindableFloat; +} + +export interface Z2MLightOutputTarget extends OutputTargetBase { + target_type: 'z2m_light'; + mqtt_source_id: string; + source_kind: HALightSourceKind; + color_strip_source_id: string; + color_value_source_id?: string; + brightness?: BindableFloat; + z2m_light_mappings?: Z2MLightMapping[]; + base_topic: string; + update_rate?: BindableFloat; + transition?: BindableFloat; + color_tolerance?: BindableFloat; + min_brightness_threshold?: BindableFloat; + stop_action?: 'none' | 'turn_off'; +} + +export type OutputTarget = LedOutputTarget | HALightOutputTarget | Z2MLightOutputTarget; + +export interface OutputTargetListResponse { + targets: OutputTarget[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/pattern-template.ts b/server/src/ledgrab/static/js/types/pattern-template.ts new file mode 100644 index 0000000..39ea654 --- /dev/null +++ b/server/src/ledgrab/static/js/types/pattern-template.ts @@ -0,0 +1,29 @@ +/** + * Pattern template shapes — named collections of key-colour rectangles + * reused across key-colour CSS sources. + */ + +export interface KeyColorRectangle { + name: string; + x: number; + y: number; + width: number; + height: number; +} + +export interface PatternTemplate { + id: string; + name: string; + rectangles: KeyColorRectangle[]; + tags: string[]; + description?: string; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface PatternTemplateListResponse { + templates: PatternTemplate[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/picture-source.ts b/server/src/ledgrab/static/js/types/picture-source.ts new file mode 100644 index 0000000..a8070ce --- /dev/null +++ b/server/src/ledgrab/static/js/types/picture-source.ts @@ -0,0 +1,60 @@ +/** + * Picture source shapes — the discriminated union over `stream_type` + * (`raw` | `processed` | `static_image` | `video`). These feed the + * picture-based CSS sources and calibration. + */ + +export type PictureSourceType = 'raw' | 'processed' | 'static_image' | 'video'; + +interface PictureSourceBase { + id: string; + name: string; + stream_type: PictureSourceType; + description?: string; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface RawPictureSource extends PictureSourceBase { + stream_type: 'raw'; + display_index: number; + capture_template_id: string; + target_fps: number; +} + +export interface ProcessedPictureSource extends PictureSourceBase { + stream_type: 'processed'; + source_stream_id: string; + postprocessing_template_id: string; +} + +export interface StaticImagePictureSource extends PictureSourceBase { + stream_type: 'static_image'; + image_asset_id?: string; +} + +export interface VideoPictureSource extends PictureSourceBase { + stream_type: 'video'; + video_asset_id?: string; + loop: boolean; + playback_speed: number; + start_time?: number; + end_time?: number; + resolution_limit?: number; + clock_id?: string; + target_fps: number; +} + +export type PictureSource = + | RawPictureSource + | ProcessedPictureSource + | StaticImagePictureSource + | VideoPictureSource; + +export interface PictureSourceListResponse { + streams: PictureSource[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/scene-preset.ts b/server/src/ledgrab/static/js/types/scene-preset.ts new file mode 100644 index 0000000..9f7df1a --- /dev/null +++ b/server/src/ledgrab/static/js/types/scene-preset.ts @@ -0,0 +1,34 @@ +/** + * Scene preset shapes — a named snapshot of which targets run with which + * colour source / brightness / fps, applied as a group. + */ + +import type { BindableFloat } from './bindable.ts'; + +export interface TargetSnapshot { + id?: string; + target_id: string; + running: boolean; + color_strip_source_id: string; + brightness?: BindableFloat; + fps: number; +} + +export interface ScenePreset { + id: string; + name: string; + description: string; + color?: string; + targets: TargetSnapshot[]; + order: number; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface ScenePresetListResponse { + presets: ScenePreset[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/sync-clock.ts b/server/src/ledgrab/static/js/types/sync-clock.ts new file mode 100644 index 0000000..2ef32a1 --- /dev/null +++ b/server/src/ledgrab/static/js/types/sync-clock.ts @@ -0,0 +1,23 @@ +/** + * Sync clock shapes — shared time bases that animated sources subscribe + * to so multiple effects stay phase-aligned. + */ + +export interface SyncClock { + id: string; + name: string; + speed: number; + description?: string; + tags: string[]; + is_running: boolean; + elapsed_time: number; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface SyncClockListResponse { + clocks: SyncClock[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/template.ts b/server/src/ledgrab/static/js/types/template.ts new file mode 100644 index 0000000..1624e2d --- /dev/null +++ b/server/src/ledgrab/static/js/types/template.ts @@ -0,0 +1,91 @@ +/** + * Processing template shapes — capture engines, post-processing / + * colour-strip filter chains, and audio engines — plus the filter and + * engine definition shapes returned by the discovery endpoints. + */ + +export interface FilterInstance { + filter_id: string; + options: Record; +} + +export interface CaptureTemplate { + id: string; + name: string; + engine_type: string; + engine_config: Record; + tags: string[]; + description?: string; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface PostprocessingTemplate { + id: string; + name: string; + filters: FilterInstance[]; + tags: string[]; + description?: string; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface ColorStripProcessingTemplate { + id: string; + name: string; + filters: FilterInstance[]; + tags: string[]; + description?: string; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface AudioTemplate { + id: string; + name: string; + engine_type: string; + engine_config: Record; + tags: string[]; + description?: string; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +// ── Filter Definition (from /filters endpoint) ──────────────── + +export interface FilterOptionDef { + type: string; + default?: any; + min?: number; + max?: number; + step?: number; + choices?: string[]; + label?: string; +} + +export interface FilterDef { + id: string; + name: string; + description?: string; + category?: string; + options: Record; +} + +// ── Engine Info (from /capture-engines, /audio-engines) ─────── + +export interface EngineInfo { + type: string; + name: string; + available: boolean; + has_own_displays?: boolean; + default_config?: Record; + config_choices?: Record; +} diff --git a/server/src/ledgrab/static/js/types/value-source.ts b/server/src/ledgrab/static/js/types/value-source.ts new file mode 100644 index 0000000..4e5e139 --- /dev/null +++ b/server/src/ledgrab/static/js/types/value-source.ts @@ -0,0 +1,198 @@ +/** + * Value source shapes — the discriminated union over `source_type`. + * Each variant returns either a `float` or a `color`; the union drives + * the value-source editor and the bindable-binding pickers. + */ + +export type ValueSourceType = + | 'static' | 'animated' | 'audio' + | 'adaptive_time' | 'adaptive_scene' | 'daylight' + | 'static_color' | 'animated_color' | 'adaptive_time_color' + | 'ha_entity' | 'gradient_map' | 'css_extract' + | 'system_metrics' | 'game_event' | 'http'; + +export interface SchedulePoint { + time: string; + value: number; +} + +export interface ColorSchedulePoint { + time: string; + color: number[]; +} + +interface ValueSourceBase { + id: string; + name: string; + source_type: ValueSourceType; + return_type: 'float' | 'color'; + description?: string; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface StaticValueSource extends ValueSourceBase { + source_type: 'static'; + return_type: 'float'; + value: number; +} + +export interface AnimatedValueSource extends ValueSourceBase { + source_type: 'animated'; + return_type: 'float'; + waveform: string; + speed: number; + min_value: number; + max_value: number; +} + +export interface AudioValueSource extends ValueSourceBase { + source_type: 'audio'; + return_type: 'float'; + audio_source_id: string; + mode: string; + sensitivity: number; + smoothing: number; + min_value: number; + max_value: number; + auto_gain: boolean; +} + +export interface AdaptiveTimeValueSource extends ValueSourceBase { + source_type: 'adaptive_time'; + return_type: 'float'; + schedule: SchedulePoint[]; + min_value: number; + max_value: number; +} + +export interface AdaptiveSceneValueSource extends ValueSourceBase { + source_type: 'adaptive_scene'; + return_type: 'float'; + picture_source_id: string; + scene_behavior: string; + sensitivity: number; + smoothing: number; + min_value: number; + max_value: number; +} + +export interface DaylightValueSource extends ValueSourceBase { + source_type: 'daylight'; + return_type: 'float'; + speed: number; + use_real_time: boolean; + latitude: number; + longitude: number; + min_value: number; + max_value: number; +} + +export interface StaticColorValueSource extends ValueSourceBase { + source_type: 'static_color'; + return_type: 'color'; + color: number[]; +} + +export interface AnimatedColorValueSource extends ValueSourceBase { + source_type: 'animated_color'; + return_type: 'color'; + colors: number[][]; + speed: number; + easing: string; + clock_id?: string; +} + +export interface AdaptiveTimeColorValueSource extends ValueSourceBase { + source_type: 'adaptive_time_color'; + return_type: 'color'; + schedule: ColorSchedulePoint[]; +} + +export interface HAEntityValueSource extends ValueSourceBase { + source_type: 'ha_entity'; + return_type: 'float'; + ha_source_id: string; + entity_id: string; + attribute: string; + min_ha_value: number; + max_ha_value: number; + smoothing: number; +} + +export interface GradientMapValueSource extends ValueSourceBase { + source_type: 'gradient_map'; + return_type: 'color'; + value_source_id: string; + gradient_id: string; + easing: string; +} + +export interface CSSExtractValueSource extends ValueSourceBase { + source_type: 'css_extract'; + return_type: 'color'; + color_strip_source_id: string; + led_start: number; + led_end: number; +} + +export interface SystemMetricsValueSource extends ValueSourceBase { + source_type: 'system_metrics'; + return_type: 'float'; + metric: string; + min_value: number; + max_value: number; + max_rate: number; + disk_path: string; + sensor_label: string; + poll_interval: number; + smoothing: number; +} + +export interface GameEventValueSource extends ValueSourceBase { + source_type: 'game_event'; + return_type: 'float'; + game_integration_id: string; + event_type: string; + min_game_value: number; + max_game_value: number; + smoothing: number; + default_value: number; + timeout: number; +} + +export interface HTTPValueSource extends ValueSourceBase { + source_type: 'http'; + return_type: 'float'; + http_endpoint_id: string; + json_path: string; + interval_s: number; + min_value: number; + max_value: number; + smoothing: number; +} + +export type ValueSource = + | StaticValueSource + | AnimatedValueSource + | AudioValueSource + | AdaptiveTimeValueSource + | AdaptiveSceneValueSource + | DaylightValueSource + | StaticColorValueSource + | AnimatedColorValueSource + | AdaptiveTimeColorValueSource + | HAEntityValueSource + | GradientMapValueSource + | CSSExtractValueSource + | SystemMetricsValueSource + | GameEventValueSource + | HTTPValueSource; + +export interface ValueSourceListResponse { + sources: ValueSource[]; + count: number; +} diff --git a/server/src/ledgrab/static/js/types/weather-source.ts b/server/src/ledgrab/static/js/types/weather-source.ts new file mode 100644 index 0000000..52f2956 --- /dev/null +++ b/server/src/ledgrab/static/js/types/weather-source.ts @@ -0,0 +1,25 @@ +/** + * Weather source shapes — a provider connection (+ location) that + * weather-driven CSS sources read temperature / conditions from. + */ + +export interface WeatherSource { + id: string; + name: string; + provider: string; + provider_config: Record; + latitude: number; + longitude: number; + update_interval: number; + description?: string; + tags: string[]; + icon?: string; + icon_color?: string; + created_at: string; + updated_at: string; +} + +export interface WeatherSourceListResponse { + sources: WeatherSource[]; + count: number; +}