217 lines
4.6 KiB
TypeScript
217 lines
4.6 KiB
TypeScript
import * as plugins from '../plugins.js';
|
|
import { Device } from '../abstract/device.abstract.js';
|
|
import type { IDeviceInfo, IRetryOptions } from '../interfaces/index.js';
|
|
|
|
/**
|
|
* Speaker protocol types
|
|
*/
|
|
export type TSpeakerProtocol = 'sonos' | 'airplay' | 'chromecast' | 'dlna';
|
|
|
|
/**
|
|
* Playback state
|
|
*/
|
|
export type TPlaybackState = 'playing' | 'paused' | 'stopped' | 'transitioning' | 'unknown';
|
|
|
|
/**
|
|
* Track information
|
|
*/
|
|
export interface ITrackInfo {
|
|
title: string;
|
|
artist?: string;
|
|
album?: string;
|
|
duration: number; // seconds
|
|
position: number; // seconds
|
|
albumArtUri?: string;
|
|
uri?: string;
|
|
}
|
|
|
|
/**
|
|
* Speaker playback status
|
|
*/
|
|
export interface IPlaybackStatus {
|
|
state: TPlaybackState;
|
|
volume: number; // 0-100
|
|
muted: boolean;
|
|
track?: ITrackInfo;
|
|
}
|
|
|
|
/**
|
|
* Speaker device info
|
|
*/
|
|
export interface ISpeakerInfo extends IDeviceInfo {
|
|
type: 'speaker';
|
|
protocol: TSpeakerProtocol;
|
|
roomName?: string;
|
|
modelName?: string;
|
|
supportsGrouping?: boolean;
|
|
groupId?: string;
|
|
isGroupCoordinator?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Abstract Speaker base class
|
|
* Common interface for all speaker types (Sonos, AirPlay, Chromecast)
|
|
*/
|
|
export abstract class Speaker extends Device {
|
|
protected _protocol: TSpeakerProtocol;
|
|
protected _roomName?: string;
|
|
protected _modelName?: string;
|
|
protected _volume: number = 0;
|
|
protected _muted: boolean = false;
|
|
protected _playbackState: TPlaybackState = 'unknown';
|
|
|
|
constructor(
|
|
info: IDeviceInfo,
|
|
protocol: TSpeakerProtocol,
|
|
options?: {
|
|
roomName?: string;
|
|
modelName?: string;
|
|
},
|
|
retryOptions?: IRetryOptions
|
|
) {
|
|
super(info, retryOptions);
|
|
this._protocol = protocol;
|
|
this._roomName = options?.roomName;
|
|
this._modelName = options?.modelName;
|
|
}
|
|
|
|
// Getters
|
|
public get protocol(): TSpeakerProtocol {
|
|
return this._protocol;
|
|
}
|
|
|
|
public get roomName(): string | undefined {
|
|
return this._roomName;
|
|
}
|
|
|
|
public get speakerModelName(): string | undefined {
|
|
return this._modelName;
|
|
}
|
|
|
|
public get volume(): number {
|
|
return this._volume;
|
|
}
|
|
|
|
public get muted(): boolean {
|
|
return this._muted;
|
|
}
|
|
|
|
public get playbackState(): TPlaybackState {
|
|
return this._playbackState;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Abstract Methods - Must be implemented by subclasses
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Play media from URI
|
|
*/
|
|
public abstract play(uri?: string): Promise<void>;
|
|
|
|
/**
|
|
* Pause playback
|
|
*/
|
|
public abstract pause(): Promise<void>;
|
|
|
|
/**
|
|
* Stop playback
|
|
*/
|
|
public abstract stop(): Promise<void>;
|
|
|
|
/**
|
|
* Next track
|
|
*/
|
|
public abstract next(): Promise<void>;
|
|
|
|
/**
|
|
* Previous track
|
|
*/
|
|
public abstract previous(): Promise<void>;
|
|
|
|
/**
|
|
* Seek to position
|
|
*/
|
|
public abstract seek(seconds: number): Promise<void>;
|
|
|
|
/**
|
|
* Get volume level (0-100)
|
|
*/
|
|
public abstract getVolume(): Promise<number>;
|
|
|
|
/**
|
|
* Set volume level (0-100)
|
|
*/
|
|
public abstract setVolume(level: number): Promise<void>;
|
|
|
|
/**
|
|
* Get mute state
|
|
*/
|
|
public abstract getMute(): Promise<boolean>;
|
|
|
|
/**
|
|
* Set mute state
|
|
*/
|
|
public abstract setMute(muted: boolean): Promise<void>;
|
|
|
|
/**
|
|
* Get current track info
|
|
*/
|
|
public abstract getCurrentTrack(): Promise<ITrackInfo | null>;
|
|
|
|
/**
|
|
* Get playback status
|
|
*/
|
|
public abstract getPlaybackStatus(): Promise<IPlaybackStatus>;
|
|
|
|
// ============================================================================
|
|
// Common Methods
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Toggle mute
|
|
*/
|
|
public async toggleMute(): Promise<boolean> {
|
|
const currentMute = await this.getMute();
|
|
await this.setMute(!currentMute);
|
|
return !currentMute;
|
|
}
|
|
|
|
/**
|
|
* Volume up
|
|
*/
|
|
public async volumeUp(step: number = 5): Promise<number> {
|
|
const current = await this.getVolume();
|
|
const newVolume = Math.min(100, current + step);
|
|
await this.setVolume(newVolume);
|
|
return newVolume;
|
|
}
|
|
|
|
/**
|
|
* Volume down
|
|
*/
|
|
public async volumeDown(step: number = 5): Promise<number> {
|
|
const current = await this.getVolume();
|
|
const newVolume = Math.max(0, current - step);
|
|
await this.setVolume(newVolume);
|
|
return newVolume;
|
|
}
|
|
|
|
/**
|
|
* Get speaker info
|
|
*/
|
|
public getSpeakerInfo(): ISpeakerInfo {
|
|
return {
|
|
id: this.id,
|
|
name: this.name,
|
|
type: 'speaker',
|
|
address: this.address,
|
|
port: this.port,
|
|
status: this.status,
|
|
protocol: this._protocol,
|
|
roomName: this._roomName,
|
|
modelName: this._modelName,
|
|
};
|
|
}
|
|
}
|