Documentation

Powered by Algolia

Making an API call from the voice script

If your app requires information from external services, you can make an API call from the voice script and provide the retrieved information to Alan. In this tutorial, we will make a call to the OpenWeatherMap API to get real time weather data and let Alan report about the weather conditions. We will also display the obtained information in a weather card on the webpage.

What you will learn

  • How to make an API call (API request) with voice
  • How to request data from external services
  • How to parse JSON data in the voice script
  • How to get weather conditions with voice

What you will need

To go through this tutorial, make sure the following prerequisites are met:

Step 1: Create a webpage

First, let's create a webpage and embed the Alan button to it:

  1. In the Alan Studio, open your project.
  2. At the top of the code editor, click Integrations.
  3. In the Embed Code Example section, click Copy All. Save the copied code as an HTML page.

    <!DOCTYPE html>
    <html lang="en">
    	<head>
    		<meta charset="UTF-8">
    		<title>Alan Example</title>
    		<link href="https://fonts.googleapis.com/css?family=Lato:100,100i,300,300i,400,400i,700,700i,900,900i"
    			rel="stylesheet">
    	</head>
    	<body>
    		<div class="alan-btn"></div>
    		<script type="text/javascript"
    			src="https://studio.alan.app/web/lib/alan_lib.js"></script>
    		<script>
    			// Define alanBtnInstance
    			var alanBtnInstance = alanBtn({
    			key: "b23c22fa051d81b47d43485dbc3cb44a2e956eca572e1d8b807a3e2338fdd0dc/stage",
    			onCommand: function (commandData) {
    				if (commandData.command === "go:back") {
    					//call client code that will react on the received command
    				}
    			},
    			rootEl: document.getElementById("alan-btn"),
    			});
    		</script>
    	</body>
    </html>
  4. To the <body> tag of our page, add a container for the card in which weather details will be displayed:

    <body>
    	<div id="card">
    		<header id="header">
    		</header>
    		<div id="details">
    		</div>
    	</div>
    	...
    </body>
  5. And add some styles:

    #card {
    	font-family: Roboto;
    	box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
    	width: 200px;
    	height: 200px;
    	margin-left: auto;
    	margin-right: auto;
    }
    
    #header {
    	background-color: #20c1ff;
    	height:50px;
    	text-align:center;
    }
    
    #header h3 {
    	color: #ffffff;
    	margin:0px;
    	padding:15px;
    }
    
    #details {
    	padding-left:20px;
    }

Open the created webpage and make sure you see the empty card and the Alan button in the bottom right corner.

Step 2: Get an API key for OpenWeather

To get weather data, you need to sign up to OpenWeather and get the API key.

  1. Go to the OpenWeather signup page.
  2. Specify your personal data and click Create Account.
  3. In the user profile section, open the API keys section.
  4. Copy the key value and save it for later.

Step 3: Add a voice command for an API call

In Alan, you can make an API call from the voice script with:

For this tutorial, we will be using the axios HTTP client.

  1. To make an API call to OpenWeather, we need to form the request URL in the following format:

    api.openweathermap.org/data/2.5/weather?q={city name}&appid={your api key}

    In the voice script in the Alan Studio, declare variables for the service URL and the API key we got.

    const SERVICE_URL = "http://api.openweathermap.org/data/2.5/weather";
    const appid = "5b4430d9d60cfddb16666807d0e1e726";

    Skip the city name for now, we will retrieve it from the user input.

  2. Add the following voice command to the voice script:

    intent("what is the weather in $(LOC)", p => {
    	const request_url = `${SERVICE_URL}?appid=${appid}&q=${p.LOC}&units=imperial`;
    	api.axios.get(request_url)
    		.then((response) => {
    			console.log(response.data);
    			let data = response.data;
    			p.play(`The temperature in ${p.LOC} is ${Math.floor(data.main.temp)} degrees in Fahrenheit`);
    			p.play(`Feels like ${Math.floor(data.main.feels_like)} degrees`);
    			p.play(`Clouds cover is ${data.clouds.all} percent`);
    		})
    		.catch((error) => {
    			console.log(error);
    			p.play(`Could not get weather information`);
    		});
    });

Now the user can say: What is the weather in <City>, and Alan will reply to the user. Let's see how the added command works:

  1. In the voice command, we are using the LOC predefined slot. This slot allows us to identify a specific piece of data from the user input as a location.
  2. With request_url, we are forming the request URL for OpenWeather. With {p.LOC}, we are passing the location retrieved from the voice command to it. Beside the SERVICE_URL, appid and location, the URL contains the units format, in our case, Fahrenheit.
  3. We are then using axios to make an API call by this URL.
  4. When the response from the server is received, the obtained data in the JSON format is logged to the console in the Alan Studio. To see the logged JSON object, at the bottom of the Alan Studio, expand the logs section and hover over the necessary line in logs.

    The JSON data is also saved to the data variable. We are using this variable to access information in the main.temp, main.feels_like and clouds.all keys of the JSON object and play the weather details to the user.

  5. If the API call returns an error, Alan logs the error description and plays: Could not get weather information.

You can test it: in the webpage, click the Alan button and ask: What is the weather in Boston? Alan will tell you about the weather in Boston.

Step 4: Display weather data in the card

Let's take it one step further: we want to display the weather details in the card as Alan pronounces them.

  1. In the Alan Studio, update the voice command to the following:

    const SERVICE_URL = "http://api.openweathermap.org/data/2.5/weather";
    const appid = "5b4430d9d60cfddb16666807d0e1e726";
    
    intent("what is the weather in $(LOC)", p => {
    
    	// Send the location to the app
    	p.play({command:"sendCity", details: 'City: ' + p.LOC});
    
    	const request_url = `${SERVICE_URL}?appid=${appid}&q=${p.LOC}&units=imperial`;
    	api.axios.get(request_url)
    		.then((response) => {
    			console.log(response.data);
    			let data = response.data;
    			p.play(`The temperature in ${p.LOC} is ${Math.floor(data.main.temp)} degrees in Fahrenheit`);
    			// Send the temp to the app
    			p.play({command:"sendData", details: 'Temperature: ' + data.main.temp});
            
    			p.play(`Feels like ${Math.floor(data.main.feels_like)} degrees`);
    			// Send the real feel temp to the app
    			p.play({command:"sendData", details: 'Feels like: ' + data.main.feels_like});
            
    			p.play(`Clouds cover is ${data.clouds.all} percent`);
    			// Send the cloud cover level to the app
    			p.play({command:"sendData", details: 'Clouds cover: ' + data.clouds.all});
    		})
    		.catch((error) => {
    			console.log(error);
    			p.play(`Could not get weather information`);
    		});
    })

    We have added two commands:

    • sendCity
    • sendData

    Now, when the user names a city in the voice command, a JSON object with information about this city is sent to the app. Similarly, once Alan plays some weather details, this information is also sent to the app.

  2. Let's handle these events on the app side. In the webpage, update the onCommand block for the Alan button to the following:

    ....
    var alanBtnInstance = alanBtn({
    	key: "b23c22fa051d81b47d43485dbc3cb44a2e956eca572e1d8b807a3e2338fdd0dc/stage",
    	onCommand: function (commandData) {
    		if (commandData.command === "sendCity") {
    			// Append city to the card
    			var par = document.createElement("h3");                       
    			var text = document.createTextNode(commandData.details);      
    			par.appendChild(text);                                       
    			document.getElementById("header").appendChild(par);           
    		}
    		
    		else if (commandData.command === "sendData") {
    			// Append weather details to the card
    			var par = document.createElement("p");                       
    			var text = document.createTextNode(commandData.details);      
    			par.appendChild(text);                                       
    			document.getElementById("details").appendChild(par);           
    		}
    	},
    	rootEl: document.getElementById("alan-btn"),
    });
    ...

    Now, when our webpage receives the sendCity or sendData command, it appends an element with the text passed in details to the card.

You can test it: in the webpage, click the Alan button and ask: What is the weather in London? Alan will tell you about the weather conditions in London and, additionally, display the text in the card.

See also

Built-in JavaScript libraries