make lunch, eat breakfast
don't forget it's free dress
Alexa, help me!Overview
What's the busiest part of my week? No question: 6:45 to 7:15am on a school day morning. Every morning we need to wake up, get dressed (in a uniform unless it's free dress day), eat breakfast, pack lunch, make sure my husband is actually getting up, fill a backpack (completed homework unless it's Monday, library books once in six days, swim gear five of six days), then get out the door in time to meet our carpool buddies. There are no morning people in my house. How could I make this easier? What if, instead of finding my phone, unlocking it, and opening the calendar, I could just ask Alexa about what's going on at school today? With the Alexa Skills Kit, a calendar feed, and a list of grade-specific events, I can!
My school publishes an ical calendar feed, but I rarely consult it on an action-packed school day morning -- it's just too much work. I have to find it on my phone, then try to filter it to just the school-related items. Also, the calendar includes school-wide events, but not grade-specific activities, such as when third graders need to bring back their library books or pack swim gear.
I spent some time thinking about what makes a useful Alexa skill. For me, it's
- something I need while I'm in the kitchen, dining room, or family room (where Alexa can hear me)
- fresh, relevant information that is otherwise hard to collect
- easy to remember without autocomplete or bookmarks
- short; Alexa is amazing, but a bit of an awkward conversationalist
From there, I thought about how I wanted to access and use my new skill. The most common case is also the simplest: what's happening today. I might also want to know about what's coming up in the next few days. I want to hear about grade-specific events, but without having to specifically ask every time. Finally, once the school year is over, I want to stop hearing about this year's grade and move on to the next.
With a basic set of commands in mind, I started work. I looked at the docs and samples, and wasn't sure this whole thing was going to be worth it. It looked like it required a lot of messy boilerplate to do what should be simple stuff. Fortunately, I found the amazing alexa-app and alexa-app-server npm packages. The alexa-app package provides a simple, clean API that let me focus on the problem I was trying to solve. The alexa-app-server made it easy to iterate quickly, by providing a local server that allowed me to test my changes without a deploy.
Before I could really get started, I needed to set up my development environment. I'm a software engineer. I automate things for a living. I followed the directions for creating and updating a Lambda function once. Then I did it again, this time paying careful attention so that I wouldn't ever have to make a zip file, go to my browser, click a button, open a dialog, select a file, wait for it to upload, then click a Test button ever again. I wrapped all these repetitive steps in a command line tool, alcl. Now I could run
alcl push ; alcl test on the command line to push my changes to Lambda and test them.
Finally, I could start work on the actual skill. I already knew where to find my school's ical-formatted calendar feed. I found the ical npm module, which parses an ical feed into a list of objects. Next, I needed a source for the grade-specific events that aren't in the all-school calendar. I decided to store these in a JSON file hosted in an S3 bucket, so that I could update it easily and separately from the Lambda function itself. Finally, I needed a place to store the user's preferences for grade-specific events (for example, get events for 3rd and 5th graders). I chose DynamoDB because it's super simple to use from a Lambda function. I created a table, updated my Lambda role, and, just a few minutes later, I could read and write user/grade preferences to a database.
The Alexa Skills Kit includes a built-in slot type for dates. This was a huge help because it automatically translated relative terms ("tomorrow", "Monday") into concrete dates such as "2016-06-01". Once I had a date, it was simple to find events for that date in the ical feed, add events from the grade-specific document, and output them. There were a few tricky bits that made it interesting:
Since an elementary school doesn't move, I thought I could ignore timzone issues. No such luck. An all-day event in the ical feed (such as "free dress"), has no timezone information, so ical sets it to UTC. Today in the Pacific timezone, we're 7 hours behind UTC. If I search for events starting midnight PDT 5/24 through midnight PDT 5/25, it doesn't include an starting at midnight UTC 5/24; that's 4pm PDT on 5/23. Fortunately, moment-timezone makes short work of all sorts of date and time conversions.
Another issue was making the speech output sound good. Saying it's early dismissal on five twenty-five twenty sixteen is not a great user experience. Moment again came to the rescue in helping me determine how to identify a day: today, tomorrow, or Friday.
Making the speech output easy to read isn't always enough. My school uses a six day rotation: each day is identified by a letter from A through F. In the speech output, I marked these up with say-as to get Alexa to pronounce the letter names. It did, but there's no way to indicate emphasis: "tomorrow is A day" sounds like "tomorrow is a day." I tried adding a short pause after the letter name; that helped a bit, but still was a little hard to understand. To make it sound better and add a bit of whimsy, I looked up five different animal names, starting with each of A through F, and added those to the day sentences. Now, Alexa might say "Today is A, as in anteater, day."
Once I had my Lambda function working well, I turned my attention to the Alexa interaction model. I thought this would be the easiest part; Alexa magically understands what you say, right? Well, not quite.
Working with the dev portal was unexpectedly painful. Mysteriously, the super-powerful AWS CLI doesn't support the Alexa Skills Kit yet. I couldn't find a way to automate updating my intents, custom slot definitions, or utterances list. Every time I wanted to change something, I had to use the somewhat clunky web form on the developer portal.
Utterances have to be explicitly spelled out, with all possible variants. The alexa-app framework helps a lot by defining a regular-expression-like syntax for describing variations of utterances. I added
alcl schema and
alcl utter commands to my alcl toolkit; these output JSON and text that I pasted in to the developer portal forms. Too often, after a round of testing with my Echo and updating my Lambda function, I got the dreaded "Error: Your session has expired. Please save your work and click here to reload." message.
The Alexa Skills Kit documentation has a useful page on how to define phrases for working with Alexa. It's really easy to make all sorts of awkward-sounding phrases; it's much harder to make phrases that flow naturally while avoiding conflicts with the many built-in (and undocumented) phrases. I tried many different variants of phrases involving "calendar" and "events" with frustratingly inconsistent results. Finally, I settled on using the school mascot to identify my skill: "Run Hillbrook Bear."Identifying use cases and implementing a solution
I identified two everyday use cases: getting information about today or a future date. I plan to use this skill to help me get ready for school, so I made the today case map to the launch intent: "Run Hillbrook Bear." I created another event that takes a date as input and returns events for that day. My favorite invocation phrase for this intent is "Ask Hillbrook Bear about
day slot takes advantage of the very flexible built-in date type to accept inputs such as tomorrow, Friday, next week, etc.
I defined two other intents: adding and removing a grade preference. Adding a grade makes all future invocations include relevant grade-specific events; removing it stops does the opposite. These are much less frequent, yet still important for keeping the information relevant. An end user wants an easy, seamless experience; they don't generally care about when or what the app is writing to a database (or even if there is a database at all). To subscribe to grade events, I settled on "Ask Hillbrook Bear about
grade_name grade," where
grade_name is a grade name such as kindergarten, first, second, etc. Asking about a grade saves the requested grade to the database, associated with the unique userID passed to the Lambda function. Similarly, "Remove
grade_name grader from Hillbrook Bear" removes the database record, so that future requests will not reference those grade events.
After lots of testing in my kitchen, I submitted my skill for certification. Within a few days, it was certified and available for use by the whole Hillbrook community. It was recently announced in Hillbrook Happenings, the weekly email newsletter for the school.
Early next school year, I'd love to get some middle-schoolers to help me add new capabilities. For example, instead of just speaking a list of events, it'd be nice to be able to query the calendar: "Alexa, ask Hillbrook Bear when is it free dress?" Also, I'm hoping to replace some of the more awkward synthesized speech with real voices from Hillbrook staff ("Bring back your library books," says the librarian.) The source code for my skill is open source and available from https://github.com/kielni/alexa-hillbrook The skill is available through the Alexa app: search for "Calendar for Hillbrook School." I hope it will be easy to adapt for other parents who could use some help getting out the door on school day mornings.