mirror of
https://github.com/koush/scrypted.git
synced 2026-02-13 10:22:57 +00:00
141 lines
5.1 KiB
TypeScript
141 lines
5.1 KiB
TypeScript
import { SettingsMixinDeviceBase } from '@scrypted/common/src/settings-mixin';
|
|
import sdk, { DeviceProvider, DeviceState, FFmpegInput, Intercom, MediaObject, MixinProvider, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk';
|
|
import { StorageSettings } from '@scrypted/sdk/storage-settings';
|
|
import { startRtpForwarderProcess } from '../../webrtc/src/rtp-forwarders';
|
|
import { MpegTSWriter, StreamTypePCMATapo } from './mpegts-writer';
|
|
import { TapoAPI } from './tapo-api';
|
|
|
|
class TapoIntercomMixin extends SettingsMixinDeviceBase<VideoCamera & Settings> implements Intercom {
|
|
storageSettings = new StorageSettings(this, {
|
|
cloudPassword: {
|
|
title: 'Cloud Password',
|
|
description: 'The Tapo Cloud account password. This is not the same as the ONVIF/RTSP local password.',
|
|
type: 'password',
|
|
}
|
|
});
|
|
client: Promise<TapoAPI>;
|
|
|
|
async startIntercom(media: MediaObject): Promise<void> {
|
|
const ffmpegInput = await sdk.mediaManager.convertMediaObjectToJSON<FFmpegInput>(media, ScryptedMimeTypes.FFmpegInput);
|
|
|
|
const settings = await this.mixinDevice.getSettings();
|
|
const ip = settings.find(s => s.key === 'ip')?.value?.toString();
|
|
await this.stopIntercom();
|
|
|
|
if (!this.storageSettings.values.cloudPassword) {
|
|
const error = 'Two Way Audio failed. Tapo Cloud password is unconfigured on ' + this.name;
|
|
sdk.log.a(error);
|
|
throw error;
|
|
}
|
|
|
|
this.client = TapoAPI.connect({
|
|
address: `${ip}:8800`,
|
|
cloudPassword: this.storageSettings.values.cloudPassword,
|
|
});
|
|
|
|
const client = await this.client;
|
|
client.processMessages();
|
|
const mpegts = await client.startMpegTsBackchannel();
|
|
|
|
const w = new MpegTSWriter();
|
|
w.AddPES(68, StreamTypePCMATapo)
|
|
w.WritePAT()
|
|
w.WritePMT()
|
|
|
|
const forwarder = await startRtpForwarderProcess(this.console, ffmpegInput, {
|
|
audio: {
|
|
codecCopy: 'pcm_alaw',
|
|
encoderArguments: [
|
|
'-vn', '-sn', '-dn',
|
|
'-acodec', 'pcm_alaw',
|
|
'-ar', '8000',
|
|
'-ac', '1',
|
|
],
|
|
onRtp: rtp => {
|
|
const payload = rtp.subarray(12);
|
|
w.WritePES(68, 192, payload)
|
|
const bytes = w.ResetBytes();
|
|
mpegts.write(bytes);
|
|
}
|
|
}
|
|
});
|
|
|
|
// cp.stdio[3].pipe(mpegts);
|
|
forwarder.killPromise.finally(() => client.stream.destroy());
|
|
client.stream.on('close', () => forwarder.kill());
|
|
}
|
|
|
|
async stopIntercom(): Promise<void> {
|
|
const c = this.client;
|
|
this.client = undefined;
|
|
const client = await c;
|
|
client?.stream.destroy();
|
|
}
|
|
|
|
getMixinSettings(): Promise<Setting[]> {
|
|
return this.storageSettings.getSettings();
|
|
}
|
|
putMixinSetting(key: string, value: SettingValue): Promise<boolean | void> {
|
|
return this.storageSettings.putSetting(key, value);
|
|
}
|
|
}
|
|
|
|
class TapoIntercom extends ScryptedDeviceBase implements MixinProvider {
|
|
async canMixin(type: ScryptedDeviceType, interfaces: string[]): Promise<string[]> {
|
|
if (type !== ScryptedDeviceType.Doorbell && type !== ScryptedDeviceType.Camera)
|
|
return;
|
|
if (!interfaces.includes(ScryptedInterface.VideoCamera) || !interfaces.includes(ScryptedInterface.Settings)
|
|
// this is currently a necessary hack to make sure the intercom gets advertised
|
|
// before the homekit and webrtc plugins mixin it.
|
|
|| !interfaces.includes(ScryptedInterface.Intercom)) {
|
|
return;
|
|
|
|
}
|
|
return [
|
|
ScryptedInterface.Intercom,
|
|
ScryptedInterface.Settings,
|
|
]
|
|
}
|
|
|
|
async getMixin(mixinDevice: any, mixinDeviceInterfaces: ScryptedInterface[], mixinDeviceState: DeviceState): Promise<any> {
|
|
return new TapoIntercomMixin({
|
|
mixinProviderNativeId: this.nativeId,
|
|
group: 'Tapo Two Way Audio',
|
|
groupKey: 'tapo',
|
|
mixinDevice,
|
|
mixinDeviceInterfaces,
|
|
mixinDeviceState,
|
|
});
|
|
}
|
|
|
|
async releaseMixin(id: string, mixinDevice: any): Promise<void> {
|
|
}
|
|
}
|
|
|
|
class TapoPlugin extends ScryptedDeviceBase implements DeviceProvider {
|
|
constructor(nativeId?: string) {
|
|
super(nativeId);
|
|
|
|
process.nextTick(() => {
|
|
sdk.deviceManager.onDeviceDiscovered({
|
|
nativeId: 'intercom',
|
|
type: ScryptedDeviceType.Builtin,
|
|
interfaces: [
|
|
ScryptedInterface.MixinProvider,
|
|
],
|
|
name: 'Tapo Two Way Audio',
|
|
});
|
|
})
|
|
}
|
|
|
|
async getDevice(nativeId: string): Promise<any> {
|
|
if (nativeId === 'intercom')
|
|
return new TapoIntercom('intercom');
|
|
}
|
|
|
|
async releaseDevice(id: string, nativeId: string): Promise<void> {
|
|
}
|
|
}
|
|
|
|
export default TapoPlugin;
|