Skip to content

Camera Chromecast Button

This script will create a button that can stream a camera on a Chromecast compatible device (Google Hubs, Chromecast, etc).

WARNING

Casting to Chromecast requires the Chromecast and Scrypted Cloud plugins.

ts
const DEFAULT_TIMEOUT_SECS = 60;

class ChromecastCameraButton extends ScryptedDeviceBase implements OnOff, Settings {
    timeout: any;

    constructor(nativeId: string) {
        super(nativeId);
        // make this device a switch so it can be synced.
        setTimeout(() => {
            systemManager.getDeviceById(this.id).setType(ScryptedDeviceType.Switch);
        });
    }

    async turnOn() {
        const ids = this.getJSON('chromecasts') as string[];
        const camera = systemManager.getDeviceById<RTCSignalingChannel>(this.getJSON('camera') as string);
        clearTimeout(this.timeout);

        const promises = ids.map(async id => {
            const chromecast = systemManager.getDeviceById<RTCSignalingClient & StartStop>(id);
            try {
                if (!chromecast) {
                    throw new Error(`Device is missing: ${id}`);
                } else {
                    await camera.startRTCSignalingSession(await chromecast.createRTCSignalingSession());
                    return;
                }
            } catch (error) {
                console.warn('Failed to start', id, chromecast?.name, error);
            }
        });

        await Promise.allSettled(promises);

        const duration = parseFloat(this.getJSON('duration') as string) || DEFAULT_TIMEOUT_SECS;
        this.timeout = setTimeout(() => this.turnOff(), duration * 1000);
        this.on = true;
    }

    async turnOff() {
        const ids = this.getJSON('chromecasts') as string[];
        const promises = ids.map(async id => {
            const chromecast = systemManager.getDeviceById<RTCSignalingClient & StartStop>(id);
            try {
                if (!chromecast) {
                    throw new Error(`Device is missing: ${id}`);
                } else {
                    await chromecast.stop();
                    return;
                }
            } catch (error) {
                console.warn('Failed to stop', id, chromecast?.name, error);
            }
        });

        await Promise.allSettled(promises);
        this.on = false;
    }

    async getSettings(): Promise<Setting[]> {
        return [
            {
                title: 'Camera',
                key: 'camera',
                value: this.getJSON('camera'),
                type: 'device',
                deviceFilter: `interfaces.includes("${ScryptedInterface.RTCSignalingChannel}")`,
            },
            {
                title: 'Chromecasts',
                key: 'chromecasts',
                value: this.getJSON('chromecasts'),
                type: 'device',
                deviceFilter: `interfaces.includes("${ScryptedInterface.RTCSignalingClient}")`,
                multiple: true,
            },
            {
                title: 'Duration',
                key: 'duration',
                value: this.getJSON('duration') || this.DEFAULT_TIMEOUT_SECS,
                type: 'number',
                description: 'Duration in seconds to stream the camera on the Chromecast.',
            }
        ]
    }

    async putSetting(key: string, value: SettingValue) {
        this.storage.setItem(key, JSON.stringify(value));
        deviceManager.onDeviceEvent(this.nativeId, ScryptedInterface.Settings, undefined);
    }

    getJSON(key: string): SettingValue {
        try {
            return JSON.parse(this.storage.getItem(key));
        } catch (e) {
            return [];
        }
    }
}

export default ChromecastCameraButton;