Alexa Smart Properties: WebRTC skill code and deployment

Paul Socha Aug 01, 2023
Share:
Alexa Skills
Blog_Header_Post_Img

 

In the last blog post of our series devoted to WebRTC titled “Alexa Smart Properties: How to easily get started with WebRTC, we shared how you can prepare your property to enable WebRTC. 

In this blog post, we will focus on setting up your Alexa skill (that we described in the prior blog) with the needed functions and API integrations. 

Below are the steps and code examples. For the development of your skill, you will need to setup your skill code to retrieve the correct access tokens.  

Step #1: Retrieve Alexa Client ID and Client Secret

This is required for your WebRTC skill to be able to connect to Alexa Communication services to perform the video and audio calling. 

1.     You can locate this in the Alexa Skill Messaging section in the Alexa Developer Console.

 

Step #2: Adding Alexa Client ID and Client Secret to your skill code

Once you have obtained the Alexa Client ID and Client Secret you will need to start implementing the code. 

1.     Add your Alexa Client ID and Alexa Client Secret into your Index.js file located in our GitHub repo.

 

Copied to clipboard
const ALEXA_CLIENT_ID  = 'INSERT Alexa Client ID HERE';
const ALEXA_CLIENT_SECRET = 'INSERT Alexa Client Secret HERE ';

 

Step #3: Implementing Account Linking for your WebRTC skill

For the account linking process we are using the Authorization Grant Request process to retrieve the necessary access and refresh tokens. Implementing the Authorization Grant Request for your skill will require you to implement the AuthorizationGrantRequestHandler in your skill. 

1.     You will need to add the AuthorizationGrantRequestHandler to your skills index.js file. This is the handler that is triggered when the skill is account linked from the Alexa mobile application. 

Copied to clipboard
const AuthorizationGrantRequestHandler = {
    canHandle(handlerInput) {
        try {
            return (Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Authorization.Grant');
        }
        catch (error) {
            return false;
        }
    },
 
    async handle(handlerInput) {
        console.log("----- AuthorizationGrantRequestHandler request  -----");
        const auth_code = handlerInput.requestEnvelope.request.body.grant.code;
        let lwa_token = await getToken(ALEXA_CLIENT_ID, auth_code, 'profile');
        console.log("Exchanged access token with LWA:", lwa_token);
        return {
            "event": {
                "header": {
                    "messageId": `abc-123-def-456-${Date.now()}`,
                    "namespace": "Alexa.Authorization",
                    "name": "AcceptGrant.Response",
                    "payloadVersion": "3"
                },
                "payload": {
                }
            }
        }
    }
}

 

2.     After adding the AuthorizationGrantRequestHander to your index.js file, you will have to implement the getTokenOptions, getTokenQueryParameters, and getToken functions.  These functions will execute the necessary steps to obtain the access tokens. 

 

Copied to clipboard
function getTokenOptions() {
    return {
        hostname: 'api.amazon.com',
        port: 443,
        path: '/auth/O2/token',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    }
}

function getTokenQueryParameters(clientId, auth_code, scope) {
    const thescope = `&scope=${scope}`;
    return 'grant_type=authorization_code&client_id=' + clientId + '&client_secret=' + CLIENT_SECRET + '&code=' + auth_code + thescope;
}
 
function getToken(clientId, auth_code, scope) {
    return new Promise(resolve => {
        const TokenQueryParameters = getTokenQueryParameters(clientId, auth_code, scope);
        const req = https.request(getTokenOptions(TokenQueryParameters.length), (res) => {
            res.setEncoding('utf8');
            let returnData = '';
            res.on('data', (chunk) => {
                returnData += chunk;
            });
            res.on('end', () => {
                //const tokenRequestId = res.headers['x-amzn-requestid'];
                console.log("LWA output:", returnData);
                resolve(JSON.parse(returnData).access_token);
            });
        });
        req.write(TokenQueryParameters);
        req.end();
    });

 

Once you have implemented the above code and obtained access to the token, you can implement outbound (Alexa to 3P) and inbound calling (3P to Alexa). 

Step #4: Implementing Outbound Calling for your WebRTC skill


Your Alexa skill will need to use the following Alexa APIs from the Technical Documents.

·      Alexa.Comms.CallSignaling.InitateOutboundcall

·      Alexa.Comms.CallSignaling.CallRingingAck

·      Alexa.Comms.CallSignaling.CallInProgressAck

·      Alexa.Comms.CallSignaling.CallEnded (will be used for both inbound and outbound).

Below are the handlers that will need to be implemented into your skill for outbound calling. Note that these are example handlers and do not include the custom code that you will need to add to send the Session Description Protocol (SDP) offer to your WebRTC infrastructure. For the full example, please see the WebRTC GitHub repo.

Copied to clipboard
//Alexa Smart Properties Outbound call skill handlers.
const initateOutBoundCallHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.InitiateOutboundCall'
    },
    handle(handlerInput) {
        console.log("Handling InitiateOutboundCall");
        const sdpOffer = handlerInput.requestEnvelope.request.sdpDetails.value;
        const sessionID = handlerInput.requestEnvelope.request.sessionId;
        const consentToken = handlerInput.requestEnvelope.context.System.user.permissions.consentToken;
        const participants = handlerInput.requestEnvelope.request.participants;
        return // the return can be the post request sending the required data to your WebRTC infrastucture.
    }
};

const callRingingAck = {
    canHandle(handlerInput) {
        console.log(handlerInput.requestEnvelope)
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallRingingAck'
    },
    handle(handlerInput) {
        return 
    }
};

const callInProgressAck = {
    canHandle(handlerInput) {
        console.log(handlerInput.requestEnvelope)
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallInProgressAck'
    },
    handle(handlerInput) {
        return 
    }
};

 

Step #5: Implementing Inbound Calling for your WebRTC skill. 

Your Alexa skill will need to use the following Alexa APIs from the Technical Documents.

  • Alexa.Comms.CallSignaling.CallRinging
  • Alexa.Comms.CallSignaling.CallInProgress
  • Alexa.Comms.CallSignaling.CallAccepted
  • Alexa.Comms.CallSignaling.CallUpdate
  • Alexa.Comms.CallSignaling.CallUpdateAck
  • Alexa.Comms.CallSignaling.CallUpdateResponse
  • Alexa.Comms.CallSignaling.CallAcceptedAck

Below are the handlers that will need to be implemented into your skill for inbound calling. 

Copied to clipboard
//Inbound Event Calling Section how to call follow  / skill works. 
 const callRingingInboundHandler = {
     canHandle(handlerInput) {
         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallRinging'
     },
     handle(handlerInput) {
         return 
     }
 };
 
 const callInProgessInboundHandler = {
     canHandle(handlerInput) {
         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallInProgress'
     },
     handle(handlerInput) {
         return 
     }
 };
 
 const callAcceptedInboundHandler = {
     canHandle(handlerInput) {
         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallAccepted'
     },
     handle(handlerInput) {
         return 
     }
 };
 
 const callUpdatedInboundHandler = {
     canHandle(handlerInput) {
         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallUpdate'
     },
     handle(handlerInput) {
         return 
     }
 };
 
 
 
 const callUpdateAckInboundHandler = {
     canHandle(handlerInput) {
         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallUpdateAck'
     },
     handle(handlerInput) {
         return 
     }
 };
 
 const callUpdateResponseInboundHandler = {
     canHandle(handlerInput) {
         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallUpdateResponse'
     },
     handle(handlerInput) {
         return 
     }
 };
 
 const callAcceptedAckInboundHandler = {
     canHandle(handlerInput) {
         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallAcceptedAck'
     },
     handle(handlerInput) {
         return 
     }
 };

 

Step #6: Implementing End Call for your WebRTC skill

Finally, you need to enable your skill to handle the event when an inbound or outbound call has been ended using the below EndCall Handler. 

 

Copied to clipboard

const endCallHandler = {
    canHandle(handlerInput) {
        const sessionID = handlerInput.requestEnvelope.request.sessionId;
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Alexa.Comms.CallSignaling.CallEnded'
    },
    async handle(handlerInput) {
        return 
    }
};

 

Now that you skill has been developed and you have added the necessary integrations in your WebRTC infrastructure, the last step is to test this in your ASP deployment. 

Contact the Alexa Smart Properties team to learn more about offering voice-enabled experiences powered by ASP. Learn how to enable WebRTC at your property by reviewing the technical documents and accessing the full sample in the GitHub Repo.

Related articles

Alexa Smart Properties enables audio and video calling between Echo and non-Echo devices with WebRTC
Alexa Smart Properties: How to easily get started with WebRTC
Alexa Smart Properties: Enabling WebRTC in property rooms

Subscribe