import type { Characteristic, CharacteristicValue, HapStatusError, PlatformAccessory, Service } from 'homebridge'; import type { PhilipsTVClient } from '../api/PhilipsTVClient.js'; import type { CustomAppConfig, InputConfig, RemoteKey, SourceConfig } from '../api/types.js'; /** Input source type for HomeKit categorization */ type InputType = 'app' | 'source' | 'channel'; /** Runtime input source with associated HomeKit service */ interface InputSource { readonly id: string; /** Display/base name. Mutable so a package-id placeholder (used when a source * is registered before the TV reports its label) can be upgraded to the real * app name once the TV becomes reachable. */ name: string; readonly type: InputType; readonly identifier: number; readonly service: Service; readonly channelListId?: string; /** Explicit launch activity for custom apps (apps the TV does not report). */ readonly className?: string; /** Explicit launch intent action for custom apps. */ readonly action?: string; } export interface InputSourceManagerDeps { readonly Service: typeof Service; readonly Characteristic: typeof Characteristic; readonly tvClient: PhilipsTVClient; readonly accessory: PlatformAccessory; readonly storagePath: string; readonly deviceId: string; readonly userInputs?: InputConfig[]; readonly customApps?: CustomAppConfig[]; readonly sourceConfigs?: SourceConfig[]; readonly infoButtonKey?: RemoteKey; readonly backButtonKey?: RemoteKey; readonly playPauseButtonKey?: RemoteKey; readonly communicationError: () => HapStatusError; readonly log: (level: 'debug' | 'info' | 'warn' | 'error', message: string) => void; /** Called after new inputs are discovered so dependent services (e.g. source * switches) can reconcile with the updated list. */ readonly onInputsChanged?: () => void; /** Called after a wheel selection successfully switched the TV, so the source * switches light up immediately instead of waiting for the next poll. */ readonly onInputSwitched?: (sourceId: string) => void; } export declare class InputSourceManager { private readonly deps; private inputSources; private currentInputId; private tvService; /** Identifier of a manual switch awaiting confirmation from the TV, and when * it was requested. See PENDING_CONFIRM_TIMEOUT_MS. */ private pendingInputId; private pendingSince; /** Serializes wheel switches and lets a newer selection supersede queued * ones, so a burst of wheel moves only launches the final choice. */ private switchQueue; private switchGeneration; /** Tracks consecutive sightings of an ambiguous system report (NA / playtv) * so a lone transitional report is never applied. See AMBIGUOUS_CONFIRM_*. */ private ambiguousReport; /** Source configs indexed by id for fast lookup */ private sourceConfigMap; /** HomeKit RemoteKey mapping (info button key is configurable) */ private readonly remoteKeyMap; /** File path for persisted input configs (survives restarts for external accessories) */ private readonly inputCachePath; constructor(deps: InputSourceManagerDeps); get currentId(): number; getSources(): readonly InputSource[]; /** Update ActiveIdentifier on the Television service from a source ID (e.g. from a source switch). */ setActiveInputById(sourceId: string): void; /** Record a manual switch so contradicting polls are ignored for a while. */ private markPending; getVisibleSources(): readonly InputSource[]; /** * Synchronously configure input sources with static sources (HDMI) and * initial app sources (user-configured, cached from previous session, or empty). * Applies visibility and order from the sources config (Homebridge UI). */ configureInputSources(tvService: Service): void; /** * Fetch applications from the TV and dynamically add new InputSource services. * Skipped if user has explicitly configured inputs[] in their config. */ fetchAppsFromTV(): Promise; /** * Replace package-id placeholder names with the real app labels the TV now * reports. A source registered before the TV was reachable is named after its * package id; once discovery returns its label we upgrade the input's name and * ConfiguredName in place. Never overrides a user-set custom name (from the * sources config) or a name the user changed in HomeKit. Returns true if any * input was renamed. */ private upgradePlaceholderNames; handleGetInput(): CharacteristicValue; handleSetInput(value: CharacteristicValue): Promise; private performSwitch; handleRemoteKey(value: CharacteristicValue): Promise; /** * Reconcile the wheel with the app the TV reports. Returns the accepted * input-source id (which the source switches should reflect too), or null * when the report was unusable or suppressed — in that case dependent * services must keep their current state so they don't bounce off a * selection the TV is still executing. */ updateFromPoll(currentApp: string | null, tvService: Service): string | null; /** * Map system packages the TV reports to the input they represent. A TV on * its home screen reports the Android launcher (never registered as an * input), and the tuner/HDMI sources all report org.droidtv.playtv — without * this mapping a wake from standby left the wheel and switches stale because * the reported package matched no input. */ private resolveReportedApp; /** * Count a sighting of an ambiguous system report. Returns true once the * report has been seen AMBIGUOUS_CONFIRM_SIGHTINGS times in a row (within * the confirmation window), i.e. it reflects a settled TV state rather * than a transition. */ private recordAmbiguousSighting; /** * Set the DisplayOrder TLV8 characteristic on the Television service. * Uses the order from the sources config (Homebridge UI) if available, * otherwise falls back to the array insertion order. */ private updateDisplayOrder; /** * Encode input source identifiers as a TLV8 buffer, sorted by the * sources config order. Inputs without a sources config entry are * appended at the end. */ private buildDisplayOrderTLV; /** * Sort inputs so user-configured visible sources come first, pushing * explicitly-hidden and unconfigured sources toward the end. This ensures * visible sources are never accidentally dropped when the list is truncated * at MAX_INPUT_SOURCES. * * Priority order: explicitly visible (0) → no config entry (1) → explicitly hidden (2). * Sort is stable — relative order within each priority group is preserved. */ private sortBySourcePriority; /** Static sources that are always present: Watch TV + HDMI 1-4 */ private getStaticSources; /** * Returns the initial set of app inputs for startup: * 1. If user configured inputs[] → use those (explicit list, self-managed) * 2. Else combine cached apps (previous TV fetch) + custom apps + every source * the user marked visible in the sources config. * * Seeding from the visible sources config is what guarantees a selected source * always becomes an input even when the TV was asleep/slow at boot and hasn't * been discovered or cached yet — the app label is filled in later when the TV * is reachable. */ private getInitialAppInputs; /** * Append an app input for every source the user marked visible in the sources * config that isn't already present. Static sources (Watch TV / Home / HDMI) * are skipped — they're always added by getStaticSources(). This decouples the * user's visible selection from the TV's boot-time responsiveness. */ private mergeConfiguredVisibleSources; /** Map the user's custom-app config entries to app inputs. */ private getCustomAppInputs; /** * Merge custom apps into a base app list. Custom apps win on id collision * (so their explicit launch intent overrides any cached/discovered entry) * and are placed first so they are never dropped by the MAX_INPUT_SOURCES cap. */ private mergeCustomApps; /** * Resolve the visibility for an input source. * Priority: sources config (Homebridge UI) → cached config → default (SHOWN). */ private resolveVisibility; /** * Resolve the display name for an input source. * Priority: sources config customName → cached configuredName → default name. */ private resolveDisplayName; /** * Resolve a stable identifier for an input. * Static sources always use position-based IDs (1-5). * Apps use persisted identifiers from cached configs, or get the next available. */ private resolveIdentifier; /** Load input configs from disk into accessory context (for external accessories) */ private loadInputConfigsFromFile; private getCachedInputConfigs; private saveInputConfigs; /** Remove InputSource services that are no longer in the current input list */ private removeStaleInputSources; private restoreOrCreateInputSource; private createInputSourceService; private setupInputSourceHandlers; private switchInput; } export {};