How to Add a Timer to an Alexa Skill: Part 1

Anand Kumar Aug 02, 2021
Share:
Build Alexa-Hosted Skills
Blog_Header_Post_Img

Like alarms and reminders, timers are one of the most popular features on Alexa. From following recipe instructions, to completing an exercise routine — or even taking a quick, 20-minute nap — timers are most helpful for customers when they are easy to access and set.

However, until now, Alexa customers have had to navigate outside of a skill session in order to start a timer. To create a better customer experience, we’ve now added the capability for developers to add the Timer feature within an Alexa skill itself. This feature is available in all locales supported by Alexa.

This blog will be a two part series. In this first part, I will cover different ways to obtain user permissions and how you can make a Timer API call after getting user permissions.

Note: In this tutorial all sample codes are created using Node.JS ASK SDK.

Steps to Add a Timer API in skill

 

1. Obtain User Permissions
To create timers, you must obtain explicit user permission to do so. Alexa service provides permission to use timers on a per-skill basis. If the user has not granted permission to the skill to create timers, make sure that your skill informs the user about how to grant permissions, and that the skill sends a home card providing a link to the skill permissions page in the Alexa app.

There are two ways to acquire these two sets of permissions to create a timer: 

  • Creating a Timer read/write permission which will be granted at time of skill enablement, or:
  • Creating a permission card or voice permission to ask first-time skill users to grant permission for timers within Alexa skill


How to send permission card in Alexa app:

Copied to clipboard
const {responseBuilder} = handlerInput;
const consentToken = requestEnvelope.context.System.apiAccessToken;
if(!consentToken){
    return responseBuilder
            .withAskForPermissionsConsentCard(“alexa::alerts:timers:skill:readwrite”)
            .getResponse();
}

This is how permission card will look in Alexa app:

permission requested

How to send voice permission:

Copied to clipboard
let {requestEnvelope} = handlerInput;
const {permissions} = requestEnvelope.context.System.user;
if (! (permissions && permissions.consentToken)){
         console.log('No permissions found!');
         return handlerInput.responseBuilder
                    .addDirective({
                        'type': 'Connections.SendRequest',
                        'name': 'AskFor',
                         'payload': {
                               '@type': 'AskForPermissionsConsentRequest',
                                '@version': '1',
                                'permissionScope': 'alexa::alerts:timers:skill:readwrite'
                          },
                      token: 'verifier'
                 }).getResponse();
            }
console.log('Permissions found: ' + permissions.consentToken);

After sending this directive, Alexa will ask the user to grant the timer permission, which will result in a “Connections.Response” request. You must check the “body.status” value to verify whether the user has accepted, rejected, or not provided any response.

Note : You can’t modify the wordings of any text below that’s labeled as “determined by Alexa”.

When user grants permission by voice:
When user accepted timer permission via voice in Alexa skill to set timer. Let me explain this by example:

In this example an Exercise Timer skill asks user to grant timer permission:

User: Alexa open Exercise Timer

Alexa (determined by skill): Welcome to Exercise Timer skill. This skill will help you with your exercise sets, this skill requires the use of Timers on Alexa.

Alexa (determined by Alexa): You can allow Exercise Timer to create Timers for activities. After those timers complete, I might ask you if you would like to return to the skill to continue the activity. Would you like to allow this?

User (determined by Alexa): Yes

Alexa: OK, you can change this permission at any time by going to Exercise Timer under Your Skills in the Alexa app.


This sends “Connections.Response” directive to Alexa skill with “status” as “ACCEPTED” so that your skill can proceed further to create Timer for user.

Copied to clipboard
{
    "type": "Connections.Response",
    "requestId": "string",
    "timestamp": "string",
    "locale": "string",
    "name": "AskFor",
    "status": {
        "code": "string",
        "message": "string"
    },
    "token": "string",
    "payload": {
    "permissionScope": "alexa::alerts:timers:skill:readwrite",
    "status": "ACCEPTED"
    }
}

When user denies permission by voice:

When user rejected timer permission via voice in Alexa skill. Let me explain this by another example:

In this example user denies permission to set a timer in Exercise Timer skill.

User: Alexa open Exercise Timer

Alexa (determined by skill) : Welcome to Exercise Timer skill. This skill will help you with your exercise sets, this skill requires the use of Timers on Alexa.

Alexa (determined by Alexa): You can allow Exercise Timer to create Timers for activities. After those timers complete, I might ask you if you would like to return to the skill to continue the activity. Would you like to allow this?

User: No

Alexa (determined by Alexa) : OK


This sends “Connections.Response” directive to Alexa skill with “status” as “DENIED” so your skill can exit after providing response.

Copied to clipboard

{
    "type": "Connections.Response",
    "requestId": "string",
    "timestamp": "string",
    "locale": "string",
    "name": "AskFor",
    "status": {
        "code": "string",
        "message": "string"
    },
    "token": "string",
    "payload": {
    "permissionScope": "alexa::alerts:timers:skill:readwrite",
    "status": "DENIED"
    }
}

 

When user response is unintelligible:

User provided unintelligible response when Alexa ask user to grant permission to set timer. Alexa re-prompt the user with rephrased question and wait for user to provide response and received no response or unintelligible response like below:

User: Alexa open Exercise Timer

Alexa (determined by skill) : Welcome to Exercise Timer skill. This skill will help you with your exercise sets, this skill requires the use of Timers on Alexa.

Alexa (determined by Alexa) : You can allow Exercise Timer to create Timers for activities. After those timers complete, I might ask you if you would like to return to the skill to continue the activity. Would you like to allow this?

User: <Unintelligible or No response>

Alexa (determined by Alexa) : Would you like to allow Exercise Timer to create timers for activities?

User: <Unintelligible or No response>


This sends “Connections.Response” directive to Alexa skill with “status” as “NOT_ANSWERED” so your skill can exit after providing response.

Copied to clipboard
{
    "type": "Connections.Response",
    "requestId": "string",
    "timestamp": "string",
    "locale": "string",
    "name": "AskFor",
    "status": {
        "code": "string",
        "message": "string"
    },
    "token": "string",
    "payload": {
    "permissionScope": "alexa::alerts:timers:skill:readwrite",
    "status": "NOT_ANSWERED"
    }
}

Create Time Once  the User is Granted Permission

To create timer, you need to make (POST) call to an API endpoint using an in-session access token. Your skill can trigger a timer to provide a simple notification, to provide an announcement, or to launch a task.

Remember, you can’t create a Timer outside of the skill session, and you need to use an in-session access token to create one.

Note: Trigger time calculates based on the moment a request is received and the timer label should be unique in each request.

In the example below, we are calling the Timer API to create an “ANNOUNCE” timer for 10 minutes, after which a "That's enough stretching, start to run" message will be announced to the user.

To create a timer, you need to make POST API call as below:

Copied to clipboard
POST /v1/alerts/timers
Host: https://api.amazonalexa.com
Authorization: Bearer <Access token>
Content-Type: application/json

Request Body:

{
            "duration": "PT10M",  
            "timerLabel": "exercise",
            "creationBehavior": {
                "displayExperience": {
                    "visibility": "VISIBLE"
                }
            },
            "triggeringBehavior": {
                "operation": {
                    "type": "ANNOUNCE",
                    "textToAnnounce": [{
                        "locale": "en-US",
                        "text": "That's enough stretching, start to run"
                    }]         
                },
                "notificationConfig": {
                    "playAudible": True
              }
        }
 }

We also provide ASK SDK support to create timer, with which you don’t have to worry about API calls.

Check below sample:

Copied to clipboard
async handle(handlerInput) {
        const {requestEnvelope, serviceClientFactory} = handlerInput;

        const timer = {
            "duration": "PT10M",  
            "timerLabel": "exercise",
            "creationBehavior": {
                "displayExperience": {
                    "visibility": "VISIBLE"
                }
            },
            "triggeringBehavior": {
                "operation": {
                    "type": "ANNOUNCE",
                    "textToAnnounce": [{
                        "locale": "en-US",
                        "text": "That's enough stretching, start to run"
                    }]         
                },
                "notificationConfig": {
                    "playAudible": True
                }
            }
        };

        console.log('About to create timer: ' + JSON.stringify(timer));
        
        try {
            const timerServiceClient = serviceClientFactory.getTimerManagementServiceClient();
            const timerResponse = await timerServiceClient.createTimer(timer);
            console.log('Timer creation response: ' + JSON.stringify(timerResponse));
            
            const timerId = timerResponse.id;
            const timerStatus = timerResponse.status;

        } catch(error) {
            return handlerInput.responseBuilder.speak("You have not granted permission.").getResponse();
        }
    }

You can also launch a custom task once the timer is completed, but this feature is currently in developer preview and to use it you need to register here.

Conclusion

If you have any question about what we’ve talked about in this blog, please visit the tech docs here. In Part 2 of this blog we will cover how you can implement other operations like “Read”, “Update” and “Delete” so stay tuned!

Subscribe