Videos

Devices can add support for streaming video, which can be viewed from the mobile app & dashboards.

Videos are available on Homey Pro (Early 2023) and Homey Pro mini since v12.7.0, and Homey Cloud.

Your app's devices can let Homey know that they support a video stream. Once a front-end (e.g. the Homey app for iOS & Android) requests to start watching, your app will receive a request to share the stream's details, such as URL and authentication. The app, nor Homey, does any transcoding, but acts as a broker between the camera and the front-end.

Supported Video Types

Homey supports video streams with WebRTC, RTSP, RTMP, HLS, DASH. Other types may be supported. Because the Homey Mobile App embeds a VLC media player, you can always try to see if your video type works.

Getting Started with Videos

To register a camera stream, your app needs to ask ManagerVideos first to create a video. Then, attach the video to your Device by calling Device.setCameraVideo. Note that when a device has both an image and video with the same id then the image will be used as a background image for the video while it is loading.

Example — WebRTC

This example shows a basic WebRTC camera stream.

/drivers/my-webrtc-camera/device.mjs
import Homey from 'homey';

export default class MyWebRTCDevice extends Homey.Device {

    /*
     * WebRTC works by creating an offer SDP in the frontend, exchanging it for 
     * an answer SDP through the cameras API, and using that answer SDP in the
     * frontend to set up the connection.
     */
    async onInit() {
        try {
            const video = await this.homey.videos.createVideoWebRTC();

            /*
             * This listener is called when the user opens the camera stream in the
             * mobile app. The argument is an SDP offer generated by the mobile appp.
             */
            video.registerOfferListener(async (offerSdp) => {
                // Normally, you would call an API to exchange an SDP offer for an SDP answer
                const result = await this.oAuth2Client.createStream(offerSdp);
                return {
                    answerSdp: result.answerSdp,
                };
            });

            /*
             * Attach the camera to the device.
             */
            await this.setCameraVideo('main', 'Main Camera', video);
        } catch (err) {
            this.error('Error creating camera:', err);
        }   
    }
    
}

Example — WebRTC without Data Channel

This example shows a WebRTC camera without a data channel, and a keepalive listener.

/drivers/my-webrtc-camera/device.mjs
import Homey from 'homey';

export default class MyWebRTCDevice extends Homey.Device {

    /*
     * Some cameras require a data channel in order to work, while other cameras
     * only work when the offer does not contain a data channel. This can be
     * customized through the options object in the createCamera method.
     *
     * There are also cameras that only keep their stream open for a few minutes.
     * These can often be extended through an API call. The keep alive listener
     * can be used to send such a request.
     */
    async onInit() {
        try {
            const video = await this.homey.videos.createVideoWebRTC({
                dataChannel: false, // default: true
            });

            /*
             * The offer listener now also returns a stream ID next to the anwer
             * SDP. This stream ID can be used to identify the stream in the
             * keep alive listener.
             */
            video.registerOfferListener(async (offerSdp) => {
                // Normally, you would call an API to exchange an SDP offer for an SDP answer
                const result = await this.oAuth2Client.createStream(offerSdp);
                return {
                    answerSdp: result.answerSdp,
                    streamId: result.streamId,
                };
            });

            /*
             * The keep alive callback has a streamId argument that can be used
             * to identify the stream. Most APIs require such an identifier in
             * the request to extend the stream.
             */
            video.registerKeepAliveListener(async (streamId) => {
                // Normally, you would call an API to keep the stream alive
                await this.oAuth2Client.extendStream(streamId);
            });

            await this.setCameraVideo('main', 'Main Camera', video);
        } catch (err) {
            this.error('Error creating camera:', err);
        }   
    }
    
}

Example — RTSP

This example shows an RTSP camera, which is as simple as providing an URL. In this example, we use HTTP Basic Authentication (username:password@...) in the URL.

/drivers/my-rtsp-camera/device.mjs
import Homey from 'homey';

export default class MyRTSPDevice extends Homey.Device {

    /*
     * To play an RTSP stream, you simply need to return the URL to the stream
     * from the video url listener. Some streams require authentication in
     * different formats. This example uses query parameters for authentication.
     */
    async onInit() {        
        try {
            const video = await this.homey.videos.createVideoRTSP({
                allowInvalidCertificates: true,
                demuxer: 'h265',
            });

            /*
             * The video url listener takes no arguments. It simply builds the
             * URL to the RTSP stream using the username and password.
             */
            video.registerVideoUrlListener(async () => {
                // Get the username and password that were set during pairing
                const {
                    username,
                    password,
                 } = this.getSettings();
         
                // Normally, you would get the device's IP from Discovery, or another method
                return {
                    url: `rtsp://${username}:${password}@192.168.1.100:554/stream`
                };
            });

            /*
             * Attach the camera to the device.
             */
            await this.setCameraVideo('main', 'Main Camera', video);
        } catch (err) {
            this.error('Error creating camera:', err);
        }    
    }
}

Example — RTMP

This example shows an RTMP camera, which is as simple as providing an URL. It's very similar to RTSP.

/drivers/my-rtmp-camera/device.mjs
import Homey from 'homey';

export default class MyRTMPDevice extends Homey.Device {

    async onInit() {        
        try {
            const video = await this.homey.videos.createVideoRTMP();

            /*
             * The video url listener takes no arguments. It simply builds the
             * URL to the RTMP stream.
             */
            video.registerVideoUrlListener(async () => {         
                // Normally, you would get the device's IP from Discovery, or another method
                return {
                    url: `rtmp://@192.168.1.100:1935`
                };
            });

            /*
             * Attach the camera to the device.
             */
            await this.setCameraVideo('main', 'Main Camera', video);
        } catch (err) {
            this.error('Error creating camera:', err);
        }    
    }
}

Example — HLS

This example shows an HLS camera, which is as simple as providing an URL. It's very similar to RTSP.

/drivers/my-hls-camera/device.mjs
import Homey from 'homey';

export default class MyHLSDevice extends Homey.Device {

    async onInit() {        
        try {
            const video = await this.homey.videos.createVideoHLS();

            /*
             * The video url listener takes no arguments. It simply builds the
             * URL to the HLS stream.
             */
            video.registerVideoUrlListener(async () => {         
                // Normally, you would get the device's IP from Discovery, or another method
                return {
                    url: `http://@192.168.1.100/stream.m3u8`
                };
            });

            /*
             * Attach the camera to the device.
             */
            await this.setCameraVideo('main', 'Main Camera', video);
        } catch (err) {
            this.error('Error creating camera:', err);
        }    
    }
}

Example — DASH

This example shows an DASH camera, which is as simple as providing an URL. It's very similar to RTSP.

/drivers/my-dash-camera/device.mjs
import Homey from 'homey';

export default class MyDASHDevice extends Homey.Device {

    async onInit() {        
        try {
            const video = await this.homey.videos.createVideoDASH();

            /*
             * The video url listener takes no arguments. It simply builds the
             * URL to the DASH stream.
             */
            video.registerVideoUrlListener(async () => {         
                // Normally, you would get the device's IP from Discovery, or another method
                return {
                    url: `http://@192.168.1.100/stream.mpd`
                };
            });

            /*
             * Attach the camera to the device.
             */
            await this.setCameraVideo('main', 'Main Camera', video);
        } catch (err) {
            this.error('Error creating camera:', err);
        }    
    }
}

Apps SDK Reference

Please refer to ManagerVideos in the Apps SDK Reference to learn more about videos in your app.

Last updated

Was this helpful?