How to make a Snips skill – Part 5 – Develop the actions - tell Snips what to do

01. January 2019 13:34
4 min reading
This post thumbnail

We are still in the process to write our first Snips App. In the last parts of this Blog Series we were looking at how we can acquire data from an API, how we can translate the data into text we can speak later and how we can make our App Multilingual from the beginning. All this topics are not mainly restricted to a Snips App. It is more “common Python programming”. In this Part we are looking at the heart of a Snips App. How do we route the Intents we receive to the proper function to get data and text and how do we extract and handle the Slot Values we get along with an intent. Here again the different parts of this walk trough journey.

Table of Content

  1. Overview
  2. Intents – how to get data for slots and intents
  3. All about API – get the needed public transport data
  4. Data to spoken text – what about Multilanguage
  5. Develop the actions – tell Snips what to do
  6. Put everything together – publish and debug the App and Actions

What exactly is a Snips App?

When you install an Assistant onto your Snips hardware it does nothing without an App. Every App has some trained Intents with some trained Slots. After Snips hears the Hotword it will try to understand what we speak afterwards. If Snips will recognize an Intent out of the spoken words, it will call the appropriate Action associated with the Intent. That’s all.

If you look at the technology behind a Snips Action it’s actually pretty straight forward and easy to understand. Every Snips App can have actions. Those actions can be either:

  • a Code Snippet – which will directly be called when a intent is recognized
  • a Home Assistant Component – to talk directly to Home Assistant via its API
  • a GitHub repository which holds a Python program (the Actions) that gets called when our Intent is recognized

We go for possibility number 3, the GitHub repo. When we publish this App with our Assistant, Snips will create a virtual Python environment just for our Actions and will start this program trough the snips-skill-server. We will see more about snips-skill-server later on when we talk about debugging our action code.

The Snips App Template

To make it easier to start with Actions the Snips engineers provided a App Template we can use as a starting point. And as we will see, we don’t have to add that much to get an App with working Actions

The first thing we do is to rename into If you create such a Skill for yourself, please make sure that this filename starts with action-***. This is the name pattern snips-skill-server is looking for.

Next we need to adjust the code of the “Master callback function”. This function is called whenever snips-skill-server receives an MQTT message with an Intent. We have to make sure that we check those Intents and react upon the ones we are interested in. This is done in this function:

def master_intent_callback(self, hermes, intent_message):
coming_intent = intent_message.intent.intent_name
_LOGGER.debug(u"[master_intent_callback] - Intent: {}".format(coming_intent))
if coming_intent == "cellerich:train_schedule_to":
self.train_schedule_to(hermes, intent_message)
if coming_intent == "cellerich:train_schedule_from_to":
self.train_schedule_from_to(hermes, intent_message)
if coming_intent == "cellerich:station_timetable":
self.station_timetable(hermes, intent_message)

I put in some _LOGGER.debug code which helps us in the beginning to see what Snips is doing if we have any issues. Them main thing is to call one of our Action functions for every Intent we are interested in.

Please notice how the Intents are called:

  • cellerich:train_schedule_to
  • cellerich:train_schedule_from_to
  • cellerich:station_timetable

This resembles your Username in Snips Console and the Intent names. You will also find the proper names in the Snips Console where you edit your App:

Intent Naming Conventions

Ok, let’s suppose that our cellerich:train_schedule_from_to Intent is received. Then we call our train_schedule_from_to(hermes, intent_message) function with the same parameters we received in our Master callback function. And here is what we do inside this function:

def train_schedule_from_to(self, hermes, intent_message):

# get our API call - translation class
self.sti = STI.SwissTransportInfo(self.config["secret"]["language"])

# get the slots from intent
for (slot_value, slot) in intent_message.slots.items():
if slot_value == "transport_type":
self.transport = slot.first().value.encode("utf8")
if slot_value == "from_station":
self.origin = slot.first().value.encode("utf8")
if slot_value == "to_station":
self.destinantion = slot.first().value.encode("utf8")

# call our API
text_to_speak = self.sti.get_connection(self.origin, self.destinantion)
except Exception as e:
text_to_speak = unicode(str(e), "utf-8")

# terminate the session and send the text we want to speak out
hermes.publish_end_session(intent_message.session_id, text_to_speak)

Just 4 simple steps:

  • we instantiate our API caller, Text Translator class with the Language Parameter we get from the config.ini file.
  • we look trough the Slots to get information about the origin and destination of the transport the user is asking. (Attentive readers might have noticed that we don’t use the transport_type at all. More about that later in the conclusion.)
  • we call our get_connection() function with origin and destination as parameters to get back the text we want to speak to the user.
  • we publish our text_to_speak with a publish_end_session back to Snips, so Snips can take care about the routing of our text to the TTS engine.

Action Parameters and config.ini

There is one little peace missing still. When we ask Snips about the next connection, Snips need to know the station name of our Home Base. Another thing Snips needs to know is in what Language we want the answers in.

This two information’s are the perfect candidates to put into our config.ini file. We can enter the default values in our Snips Console as App Parameters:

Global Parameters

Are just copied into the Global section of the config.ini file. Those parameters are usually set before we install an App.

End-User Parameter

Are copied into the Secret section of the config.ini file. The difference here is, those parameters can be set from the end user when he is installing the Assistant or just the Actions with sam the command line interface of Snips.

Snips App Parameters

That’s all we need to do! If you look at the code in my GitHub Repo, you will see that a lot of code is written just to handle errors, missing parameters we might end up from missing Slot values from Snips and Error-Logging and Debug-Logging code. Otherwise we can write a Snips App with Actions in just a couple of code lines.

In the next/last Part we will look how we put everything together, run and debug the App and finally publish the App for the general public.