Tutorial: Lambda Function / OurWeather – Amazon Alexa and OurWeather – Part 4
We are pleased to announce Version 026 of the OurWeather software and the big change is that we now support the Amazon Alexa, Echo, Echo Plus, and all other Amazon Voice systems. In this series of tutorials we will show you how to connect up the Alexa systems in your house to your OurWeather station. We will be rolling this really cool feature set into our other major kit offerings including:
- – GroveWeatherPi – Raspberry Pi Based Weather Station Kit
- – SmartPlantPi – Raspberry Pi Based Plant Watering and Environmental Kit
- – The ThunderBoard Raspberry Pi IOT Lightning Detection Kit
This is a five part instructional tutorial to get your Amazon Alexa to OurWeather Interface working. All source code is provided!
While we are focusing on OurWeather in this series, the techniques we developed are good for many projects.
- - Part 1 - Voice Time! OurWeather and Amazon Alexa - Part 1
- - Part 2 - Setting up PubNub MQTT Broker
- - Part 3 - The Alexa Skill
- - Part 4 - The AWS Lambda Serverless Function
- - Part 5 - Setting OurWeather MQTT to Alexa up and System Tests
The Alexa Voice to OurWeather Project
There are four major parts to this project.
- – PubNub – MQTT Broker between OurWeather and the AWS Lambda function
- – The Alexa Skill – Controls Alexa on Echos in your house
- – The AWS Lambda Serverless Function for brokering information from PubNub to the Alexa Skill
- – OurWeather – MQTT Publisher Weather Information to PubNub
In part 4, we address the Lambda function on Amazon AWS and hooking it up to OurWeather.
Review: What is a Lambda Function?
The Lambda function is the most difficult to understand part of this project. Why do we need this? An Amazon Alexa Skill is concerned with “utterances” and how to interpret them. When you talk to Alexa, Alexa translates your speech to text, sends a request to the Lambda function. The Lambda function interprets this Alexa JSON request, gathers the weather data via an MQTT history request from PubNub (which came from OurWeather via MQTT), and sends a text response (JSON again) to Alexa with the required weather data embedded, and Alexa speaks the result.
AWS Lambda is an event-driven, serverless computing platform provided by Amazon as a part of the Amazon Web Services. It is a compute service that runs code in response to events and automatically manages the compute resources required by that code. It was introduced in 2014.
We are using node.js to write our Lambda Function. Parts were very challenging (dealing with asynchronous callbacks in a environment that will terminate when you are done running code), but we got it to work perfectly. You will find the open source code in Part 3.
The purpose of Lambda, as compared to AWS EC2 (cloud based servers), is to simplify building smaller, on-demand applications that are responsive to events and new information. AWS targets starting a Lambda instance within milliseconds of an event. Node.js, Python, Java and C# through .NET Core are all officially supported as of 2016, and other languages can be supported via call-outs. However, some runtimes, such as the Java Virtual Machine, may be slower than others to start.
We are using node.js to program the lambda function. Other language, such as C#, Java 8 and Python are available.
What is Node.js?
Node.js is a server-side platform built on Google Chrome’s JavaScript Engine (V8 Engine). Node.js was developed by Ryan Dahl in 2009.
Node.js is an open source, cross-platform runtime environment for developing server-side and networking applications. Node.js applications are written in JavaScript, and can be run within the Node.js runtime on OS X, Microsoft Windows, and Linux.
Node.js also provides a very, very rich library of various JavaScript modules which simplifies the development of web applications.
Features of Node.js
The main features of node.js
- Asynchronous and Event Driven − All APIs of Node.js library are asynchronous, that is, non-blocking. It essentially means a Node.js based server never waits for an API to return data. The server moves to the next API after calling it and a notification mechanism of Events of Node.js helps the server to get a response from the previous API call. Since the Lambda function will completely terminate at the end of running your code, we need to use a method for making sure that the Lambda function sticks around for the return of the asynchronous calls. We use a node.js programming construct called “promises” to make that happen. More on promises below.
- Very Fast − Node.js library is very fast in code execution.
- Single Threaded but Highly Scalable − Node.js uses a single threaded model with event looping. The Event mechanism helps the server to respond in a non-blocking way and makes the server highly scalable as opposed to traditional servers which create limited threads to handle requests. Node.js uses a single threaded program and the same program can provide service to a much larger number of requests than traditional servers like Apache HTTP Server. This programming paradigm fits extremely well with our event-driven Lambda functions.
- No Buffering − Node.js applications never buffer any data. These applications simply output the data in chunks. Again, this fits well with Lambda Functions.
Setting up the Lambda Function
Step 1) Sign up for a free account at aws.amazon.com. We would suggest using the same email address as you used for signing up at developer.amazon.com for Alexa, but it is not required. Using the same account you use for you Amazon Alexa (which we used for the Alexa Skill set on developer.amazon.com) is not required as it was for developer.amazon.com since we are not connecting directly from the lambda function to your local Alexa Echo, etc.
You will still require a credit card number, even though we are using the free tier services.
Once you have signed up, you are presented with many, many options. AWS is a very robust and multi-facited cloud server provider. Type “lambda” in the search bar.
Note. Amazon seems to be always changing the initial screens so there may be differences in what you see.
Step 2) Click on “Create Function” and you will see the screen below:
Enter “SDL2Alexa” into the lambda function Name field.
Leave the runtime at the default, “Node.js 6.10”
Click on Creeate Custom Role in the dropdown under “Role”
Leave the defaults on the page below and click “allow”
Now select “lambda_basic_execution” under existing role. Sometimes this page glitches and nothing will show under “Role”. Click on “Create Function”. Now you will have the screen below. Most of these options below, we will not touch.
Next click on Add Triggers-> Alexa Skills Kit and then on “Add” under Configure Triggers.
Finally click on “Save” in the upper right corner of the page. Now, you should have this.
At the top of this screen you will see your ARN (Amazon Resource Name) that we need to connect to the Amazon Alexa Application . Copy the ARN number (behind the “- “) and then open up your Alexa Skill that you set up in Part 3 in another browser (login in to developer.amazon.com). Our example ARN above (which will not work for you, you need your own) was “arn:aws:lambda:us-west-2:480327688318:function:SDL2Alexa”.
Click on the configuration tab in your OurWeather Alexa App from Part 3 and click on the radio button “AWS Lambda ARN” and then enter your ARN number as the default under endpoint as below:
Our Alexa Skill configuration is now complete. We now go back to the Lambda function and install our software.
Add the Alexa Skills Kit Trigger, if it isn’t shown. (Add triggers->Alexa Skill Kit, then Add). Now finally, click on the SDL2Alexa button at the top of the Designer Block to look at your code.
Your Lambda screen should now look like this:
Now we can install the Lambda Software
Installing the Lambda Software
We have the Lambda software and all required libraries, here in a zip file. Download this to your computer.
Under Function code, click on the drop down “Code entry type” and select “Upload a .ZIP file”. Then click on Upload and select the Zip file from your computer you just downloaded. It should be “SDL2AlexaOW.zip”.
If you are on a Mac, it may automatically unzip the file, in which case go into the SDL2AlexaOW directory, select all the files, right click, and compress it again (it will be archive.zip in the program directory).
The Click “Save” in the top right corner.
Now go down to the Function code block to the Code entry type dropdown and select “Edit code inline” and you should now see this:
Configure the Lambda Function
Almost done now. We have a couple of things to configure in the Lambda code. In the function code block
Step 1) Grab your PubNub Subscribe code (from Part 2) and paste it into the text slot string for pub_key and sub_key. Your code should like this after doing this. (Note: Don’t use these keys, use your keys).
var pubnub = new PubNub({ ssl : true, publish_key : "pub-c-00d2859b-dd48-44f1-8e23-282aac34129f", subscribe_key : "sub-c-d541ff28-e691-11e7-b8a6-46d99af2bb8c", }); var channel = 'OWIOT1';
Step 2) Put in the Alexa Skill ID. The one in the file will NOT WORK. You must grab your Alexa Skill Application ID (see Part 3 – it will be under the Skill Information Tab in your Alexa App).
Now that part of the lambda code should look like this:
/** * App ID for the skill */ var APP_ID = 'amzn1.ask.skill.2ff2989c-6a37-4166-9f69-b16e9545dab8'; //replace with "amzn1.echo-sdk-ams.app.[your-unique-value-here]";
That is it for configuration. Save the function. Now on to testing.
Testing the Alexa Skills
OK. Back to the Alexa Skill. Go to the “Test” tab and enable the test if you haven’t already.
Down in the Service Simulator in the text tab, type “temperature” and click, “Ask OurWeather Alexa App”. You will get a failure for the Service response (we have no data yet because we haven’t set up OurWeather yet), but what we really want is the JSON Service request. We are going to use that in the Lambda Function test. Copy all of the JSON code out of the Service Request. It will look something like this:
{ "session": { "new": true, "sessionId": "SessionId.4c2e6d85-4fdd-4685-97a4-ec261dcf5140", "application": { "applicationId": "amzn1.ask.skill.2ff2989c-6a37-4166-9f69-b16e9545dab8" }, "attributes": {}, "user": { "userId": "amzn1.ask.account.x" } }, "request": { "type": "IntentRequest", "requestId": "EdwRequestId.7cc5ef67-665d-4aea-99a5-340fa26c94cf", "intent": { "name": "temperature", "slots": {} }, "locale": "en-US", "timestamp": "2018-01-05T19:40:08Z" }, "context": { "AudioPlayer": { "playerActivity": "IDLE" }, "System": { "application": { "applicationId": "amzn1.ask.skill.2ff2989c-6a37-4166-9f69-b16e9545dab8" }, "user": { "userId": "amzn1.ask.account.x" }, "device": { "supportedInterfaces": {} } } }, "version": "1.0" }
Now let’s go test the Lambda Function.
Testing the the Lambda Function
Click on the drop down menu next to the test button and select “Configure test events”. On the next screen, call your Event name, “Temperature” and then paste the JSON Service Request code from the previous step.
Clicking the Test button now will show a failure. We need to load up some data on PubNub first for the Lambda Function to work.
Setting a Dummy PubNub Message
After we set up OurWeather in the next Part, we will have live data to work on. But right now, let’s publish a message that the Lambda Node will be able to pick up. Here is a JSON message from OurWeather to PubNub that we will use3 in our testing.
{ "FullDataString": "2.20,65.00,22.85,102428.00,562.27,10.88,0.00,90.00,5.00,10.76,10.88,0.00,0.00,90.00,90.00,0,01/05/2018 11:40:18,SwitchDoc Labs,0,-1,0.00,0.00,0.00,0.00,0.00,0.00,3.58,18.00,4.69,30.40,0.00,26.00,V:0,WXLMG ,1" }
Go to your PubNub account (that you set up in Part 2) and go down into your SDL2Alexa App and then select your subscribe key from the OurWeather Key (you should have this saved somewhere else too).
Next, Click on the side tab to Debug Console. Make the Default channel “OWIOT1” and place your pub-sub key into the Authorization key. Click on Add Client.
Now you should have a screen that looks like this:
Paste the JSON message from OurWeather (above) into the Test Message slot and press Send. You should then look like this:
Now our Lambda will have something to chew on.
Go back to to your Lambda page and click on Test (running the Temperature Test that we sent up earlier!).
Look down at the Execution Results in the Function code block and you should have your first success full Response.
Now let’s go back to the Alexa Skill.
Go back to the Service Simulator, type “Temperature” into the Enter Utterance box and hit the “Ask OurWeather Alex App”. Now you have connected all the way from your Alexa Skill to your Lambda Function, which reads the PubNub channel and sends back the temperature. 35.8 Degrees. Hit the listen button to hear what it would sound like on your Echo. Sometimes the browser aren’t quite wired into the speaker on some computers, so don’t be surprised it if doesn’t work.
One More Test – Try Your Alexa
OK, now the rubber hits the road. We have the Alexa Skill working, we have the Lambda Function working, let’s go to your phone and add the Alexa App. Note you must be logged into the same email address and account for your Alexa App on your phone (and have registered your Echos to the same account (email address) for your app to show up.
On the iPhone, Click the menu, then select Skills. Then click on Your Skills at the top right. Look under All Skills or just updated and you will find your new skill (OurWeather in this case). Select your new skill. Finally slide over the tabs from Recently Added all the way to the right and you should see DEV SKILL. Click that and you should see something like this:
Next is the big moment. Ask Alexa “Alexa, Ask OurWeather Temperature”. You should be rewarded as below. In Part 5 we hook up real Weather data from the OurWeather Weather Station Kit to our system.
The Lambda Node.js Intent Software – Promises, Promises!
Before we move on to Part 5, we wanted to talk a little about the Lambda node.js code. If you recall, lambda functions quit when they stop running code. This is a problem for a node.js program that deals with asynchronous callbacks. In our lambda code, we request a history on the the weather station channel, OWIOT1 from the MQTT channel on PubNub. This takes a while to come back (on the order of 100ms or so) and if we don’t stop the lambda function somehow, it will stop before the callback comes back because of the nature of a node.js program. How to solve this?
The key is Promises. We essentially make the node.js lambda function “promise” to wait until the callback routine returns (or the 3 to 7 second default lambda function time out happens). When the callback is processed (the promise being kept) and the response is then sent to the Alexa skill and the lambda function terminates.
What are Promises?
The core idea behind promises is that a promise represents the result of an asynchronous operation. A promise is in one of three different states:
- pending – The initial state of a promise.
- fulfilled – The state of a promise representing a successful operation.
- rejected – The state of a promise representing a failed operation.
Once a promise is fulfilled or rejected, it is immutable (i.e. it can never change again).
An example promise for the “temperature” intent in the SDL2Alexa lambda code:
// temperature intent temperature: function (intent, session, response) { var first = pubnub.history({ channel: 'OWIOT1', reverse: false, // Setting to true will traverse the time line in reverse starting with the oldest message first. count: 1, // how many items to fetch }).then((response) => { var obj = JSON.parse(JSON.stringify(response)); var msgs = obj.messages; var msg = msgs[0]; var parsedJSON = JSON.parse(JSON.stringify(msg.entry)); weatherArray = parsedJSON.FullDataString.split(","); return; }).catch((error) => { console.log(error) }); return Promise.all([first]) .then(function (responses) { var myText; if (weatherArray[15] == '0') // English { var temperature = Number(weatherArray[0]) *9/5 + 32.0; myText = "Outside Temperature is " + temperature.toFixed(1) + " degrees F"; } else { myText ="Outside Temperature is " + weatherArray[0] + " degrees C"; } response.tell(myText); return ; }); },
The syntax is somewhat obscure, but you can see the return from the history call (the”first” function) is in the Promise.all function call that waits for the history PubNub return and builds the response for the Alexa Skill.
Coming Next
In Part 5, we set up OurWeather and tie it all together.
I had to switch to the Oregon regional as when the Lambda was being set up. Ohio did not show up under “add triggers”.
Ran thru the tutorial and was able to obtain a temperature of 36.0 F. How do I get 3 degrees C?
When I ask Alexa, I get the weather that she provides for the city, not OurWeather.
Scott,
First of all, you need to make sure you have installed your own skill on Alexa. See Part 4.
Once you have that done, “Alexa, Ask Our Weather Status” will work.
BP
I did do all of part 4, and have the skill added and enabled in Alexa, but my station went down last night and I’ve been fighting most of the day to keep it up. I saw there was an update to 027 and having troubles getting it to update. I can ping the IP address with no problems, but the station says it is down.