154 lines
6.4 KiB
TypeScript
154 lines
6.4 KiB
TypeScript
import * as plugins from '../../plugins.js';
|
|
import { BaseIntegration } from '../../core/classes.baseintegration.js';
|
|
import type { IIntegrationEntity, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
|
|
import { AndroidtvClient } from './androidtv.classes.client.js';
|
|
import { AndroidtvConfigFlow } from './androidtv.classes.configflow.js';
|
|
import { createAndroidtvDiscoveryDescriptor } from './androidtv.discovery.js';
|
|
import { AndroidtvMapper } from './androidtv.mapper.js';
|
|
import type { IAndroidtvConfig } from './androidtv.types.js';
|
|
|
|
export class AndroidtvIntegration extends BaseIntegration<IAndroidtvConfig> {
|
|
public readonly domain = 'androidtv';
|
|
public readonly displayName = 'Android Debug Bridge';
|
|
public readonly status = 'control-runtime' as const;
|
|
public readonly discoveryDescriptor = createAndroidtvDiscoveryDescriptor();
|
|
public readonly configFlow = new AndroidtvConfigFlow();
|
|
public readonly metadata = {
|
|
source: 'home-assistant/core',
|
|
upstreamPath: 'homeassistant/components/androidtv',
|
|
upstreamDomain: 'androidtv',
|
|
integrationType: 'device',
|
|
iotClass: 'local_polling',
|
|
requirements: ['adb-shell[async]==0.4.4', 'androidtv[async]==0.0.75'],
|
|
dependencies: [],
|
|
afterDependencies: [],
|
|
codeowners: ['@JeffLIrion', '@ollo69'],
|
|
};
|
|
|
|
public async setup(configArg: IAndroidtvConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
|
|
void contextArg;
|
|
return new AndroidtvRuntime(new AndroidtvClient(configArg));
|
|
}
|
|
|
|
public async destroy(): Promise<void> {}
|
|
}
|
|
|
|
export class HomeAssistantAndroidtvIntegration extends AndroidtvIntegration {}
|
|
|
|
class AndroidtvRuntime implements IIntegrationRuntime {
|
|
public domain = 'androidtv';
|
|
|
|
constructor(private readonly client: AndroidtvClient) {}
|
|
|
|
public async devices(): Promise<plugins.shxInterfaces.data.IDeviceDefinition[]> {
|
|
return AndroidtvMapper.toDevices(await this.client.getSnapshot());
|
|
}
|
|
|
|
public async entities(): Promise<IIntegrationEntity[]> {
|
|
return AndroidtvMapper.toEntities(await this.client.getSnapshot());
|
|
}
|
|
|
|
public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
|
|
try {
|
|
if (requestArg.domain === 'remote') {
|
|
return await this.callRemoteService(requestArg);
|
|
}
|
|
if (requestArg.domain === 'androidtv') {
|
|
return await this.callAndroidtvService(requestArg);
|
|
}
|
|
if (requestArg.domain !== 'media_player') {
|
|
return { success: false, error: `Unsupported Android TV service domain: ${requestArg.domain}` };
|
|
}
|
|
return await this.callMediaPlayerService(requestArg);
|
|
} catch (errorArg) {
|
|
return { success: false, error: errorArg instanceof Error ? errorArg.message : String(errorArg) };
|
|
}
|
|
}
|
|
|
|
public async destroy(): Promise<void> {
|
|
await this.client.destroy();
|
|
}
|
|
|
|
private async callRemoteService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
|
|
if (requestArg.service !== 'send_command') {
|
|
return { success: false, error: `Unsupported Android TV remote service: ${requestArg.service}` };
|
|
}
|
|
const command = requestArg.data?.command;
|
|
const commands = typeof command === 'string' ? [command] : Array.isArray(command) ? command.filter((itemArg): itemArg is string => typeof itemArg === 'string') : [];
|
|
if (!commands.length) {
|
|
return { success: false, error: 'Android TV remote.send_command requires data.command.' };
|
|
}
|
|
const repeats = typeof requestArg.data?.num_repeats === 'number' ? requestArg.data.num_repeats : 1;
|
|
await this.client.sendCommand(commands, repeats);
|
|
return { success: true };
|
|
}
|
|
|
|
private async callAndroidtvService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
|
|
if (requestArg.service === 'adb_command') {
|
|
const command = requestArg.data?.command;
|
|
if (typeof command !== 'string' || !command) {
|
|
return { success: false, error: 'Android TV adb_command requires data.command.' };
|
|
}
|
|
await this.client.adbCommand(command);
|
|
return { success: true };
|
|
}
|
|
return { success: false, error: `Unsupported Android TV service: ${requestArg.service}` };
|
|
}
|
|
|
|
private async callMediaPlayerService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
|
|
if (requestArg.service === 'turn_on') {
|
|
await this.client.turnOn();
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'turn_off') {
|
|
await this.client.turnOff();
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'play' || requestArg.service === 'media_play') {
|
|
await this.client.mediaPlay();
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'pause' || requestArg.service === 'media_pause') {
|
|
await this.client.mediaPause();
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'play_pause' || requestArg.service === 'media_play_pause') {
|
|
await this.client.mediaPlayPause();
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'stop' || requestArg.service === 'media_stop') {
|
|
await this.client.mediaStop();
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'volume_set') {
|
|
const level = requestArg.data?.volume_level;
|
|
if (typeof level !== 'number') {
|
|
return { success: false, error: 'Android TV volume_set requires data.volume_level.' };
|
|
}
|
|
await this.client.setVolumeLevel(level);
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'volume_up' || requestArg.service === 'volume_down') {
|
|
await this.client.stepVolume(requestArg.service === 'volume_up' ? 1 : -1);
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'volume_mute') {
|
|
const muted = requestArg.data?.is_volume_muted ?? requestArg.data?.muted;
|
|
if (typeof muted !== 'boolean') {
|
|
return { success: false, error: 'Android TV volume_mute requires data.is_volume_muted.' };
|
|
}
|
|
await this.client.muteVolume(muted);
|
|
return { success: true };
|
|
}
|
|
if (requestArg.service === 'select_source') {
|
|
const source = requestArg.data?.source;
|
|
if (typeof source !== 'string' || !source) {
|
|
return { success: false, error: 'Android TV select_source requires data.source.' };
|
|
}
|
|
await this.client.selectSource(source);
|
|
return { success: true };
|
|
}
|
|
return { success: false, error: `Unsupported Android TV media_player service: ${requestArg.service}` };
|
|
}
|
|
}
|