Estimated reading time: 7 minutes

Developing a new Skill

This page will walk you through developing a new Mycroft Skill. It assumes you have read through the basic skills information

Prerequisites

It's a good idea to get prepared before writing your new Skill, as this will make your skill-writing experience go much smoother.

  • Git - You will need to know some basic Git commands in order to create a new Skill for Mycroft. If you're not familiar with Git, that's OK, but you will need to have Git installed on your system. .
  • Python - You will need to know some basic Python programming to get started. If you've programmed in other object-oriented languages, like Javascript or C#, then you'll be able to pick it up, but if you're totally new to programming, you'll need to do an introductory programming course.
  • Naming your Skill - Choose a name for your Skill before creating a new repository. It's a good idea to check the Mycroft Skills Repo so that you don't create a duplicate name.
  • Set up your environment - Most people will find it easiest to test new Skills by setting up Mycroft for Linux. cd into the directory where you have mycroft-core installed and type /start_mycroft.sh debug. This should open a command line interface (CLI) like that shown below:

Starting mycroft-core in debug mode for Skills testing

  • Understand the flow of your Skill - It's a good idea to write down on paper how your Skill will work, including
    • What words will the User speak to activate the Skill?
    • What will Mycroft speak in response?
    • What data will you need to deliver the Skill?
    • Will you need any additional packages or dependencies?

Once you've given these some thought, you can get started.

Skill terminology

You'll notice some new terms as you start to develop Skills.

  • dialog - A dialog is a phrase that is spoken by Mycroft. Different Skills will have different dialogs, depending on what the Skill does. For example, in a weather Skill, a dialog might be the.maximum.temperature.is.dialog.
  • intent - Mycroft matches utterances that a User speaks with a Skill by determining an intent from the utterance. For example, if a User speaks Hey Mycroft, what's the weather like in Toronto? then the intent will be identified as weather and matched with the Weather Skill. When you develop new Skills, you need to define new intents.
  • utterance - An utterance is a phrase spoken by the User, after the User says the Wake Word. what's the weather like in Toronto? is an utterance.

Make a new repo using the Template Skill

In GitHub, fork the Mycroft Skills repo into your own GitHub account.

Do this by clicking the 'Fork' button.

Forking the Mycroft Skills Repo

Then,

git clone

the repo you've just forked to your local machine.

For example, if your GitHub username is "JaneBloggs" then you will need to

git clone

from https://github.com/JaneBloggs/mycroft-skills.git

$ git clone https://github.com/JaneBloggs/mycroft-skills.git
Cloning into 'mycroft-skills'...
remote: Counting objects: 1529, done.
remote: Compressing objects: 100% (60/60), done.
remote: Total 1529 (delta 42), reused 46 (delta 15), pack-reused 1451
Receiving objects: 100% (1529/1529), 7.44 MiB | 565.00 KiB/s, done.
Resolving deltas: 100% (709/709), done.
Checking connectivity... done.

Now, we'll made a new repository for your Skill. The new repository has to follow a strict file structure. A Template Skill is available to clone from. If you're new to GitHub, you might find this guide on how to make a repo useful.

Example Skill Template

Copy the Template Skill into a new directory. Here, we've called the new Skill skill-training, but your Skill will have a different name.

$ cp -R 00__skill_template skill-hello-worldls -las

Structure of the Skill repo

The structure of the Template Skill directory looks like this:

$ ls -las
total 128
 8 drwxrwxr-x   5 kathyreid kathyreid  4096 Oct 27 00:22 .
 8 drwxrwxr-x 136 kathyreid kathyreid  4096 Oct 27 00:22 ..
 8 drwxrwxr-x   3 kathyreid kathyreid  4096 Oct 27 00:22 dialog
 8 -rw-rw-r--   1 kathyreid kathyreid  3768 Oct 27 00:22 __init__.py
56 -rw-rw-r--   1 kathyreid kathyreid 49360 Oct 27 00:22 LICENSE
 8 -rw-rw-r--   1 kathyreid kathyreid   187 Oct 27 00:22 README.md
 8 -rw-rw-r--   1 kathyreid kathyreid   116 Oct 27 00:22 requirements.sh
 8 -rw-rw-r--   1 kathyreid kathyreid    79 Oct 27 00:22 requirements.txt
 8 drwxrwxr-x   3 kathyreid kathyreid  4096 Oct 27 00:22 test
 8 drwxrwxr-x   3 kathyreid kathyreid  4096 Oct 27 00:22 vocab

dialog directory

The dialog directory contains subdirectories for each spoken language the skill supports. Each subdirectory has .dialog files which specify what Mycroft should say when a Skill is executed.

The subdirectories are named using the IETF language tag for the language. For example, Brazilian Portugues is 'pt-br', German is 'de', and Australian English is 'en-au'.

Here is an example where one language is supported. By default, the Template Skill contains one subdirectory for United States English - 'en-us'. If more languages were supported, then there would be additional language directories.

$ ls -las -R
.:
total 24
8 drwxrwxr-x 3 kathyreid kathyreid 4096 Oct 27 23:32 .
8 drwxrwxr-x 6 kathyreid kathyreid 4096 Oct 27 23:32 ..
8 drwxrwxr-x 2 kathyreid kathyreid 4096 Oct 27 23:32 en-us

./en-us:
total 40
8 drwxrwxr-x 2 kathyreid kathyreid 4096 Oct 27 23:32 .
8 drwxrwxr-x 3 kathyreid kathyreid 4096 Oct 27 23:32 ..
8 -rw-rw-r-- 1 kathyreid kathyreid   32 Oct 27 23:32 hello.world.dialog
8 -rw-rw-r-- 1 kathyreid kathyreid   91 Oct 27 23:32 how.are.you.dialog
8 -rw-rw-r-- 1 kathyreid kathyreid   88 Oct 27 23:32 welcome.dialog

There will be one file in the language subdirectory (ie. en-us) for each type of dialog the Skill will use. In the example above, there are three types of dialog used by the Skill. Let's take a look at a dialog file.

$ cat hello.world.dialog
Hello world
Hello
Hi to you too

You will notice that each line of dialog is slightly different. When instructed to use a particular dialog, Mycroft will chose one of these lines at random. This is closer to natural speech. That is, many similar phrases mean the same thing.

For example, how do you say 'goodbye' to someone?

  • Bye for now
  • See you round
  • Catch you later
  • Goodbye
  • See ya!

vocab directory and defining Intents

Each Skill defines one or more Intents. Intents are defined in the 'vocab' directory. The 'vocab' directory is organized by language, just like the 'dialog' directory.

In this example, we can see that there are three Intents, each defined in

'IntentKeyword.voc`

vocab files:

mycroft-skills/skill-hello-world/vocab/en-us$ ls -las
total 40
8 drwxrwxr-x 2 kathyreid kathyreid 4096 Nov  9 00:11 .
8 drwxrwxr-x 3 kathyreid kathyreid 4096 Nov  9 00:11 ..
8 -rw-rw-r-- 1 kathyreid kathyreid   22 Nov  9 00:11 HelloWorldKeyword.voc
8 -rw-rw-r-- 1 kathyreid kathyreid   52 Nov  9 00:11 HowAreYouKeyword.voc
8 -rw-rw-r-- 1 kathyreid kathyreid   17 Nov  9 00:11 ThankYouKeyword.voc

Just like dialog files, vocab files can have multiple lines. Mycroft will match any of these phrases with the Intent. If we have a look at the

ThankYouKeyword.voc

file, we can see this in action:

$ cat ThankYouKeyword.voc
thank you
thanks

If the User speaks either

thank you

or

thanks

Mycroft will match this to the

ThankYou

intent in the Skill.

NOTE: One of the most common mistakes when getting started with Skills is that the vocab file doesn't include all the phrases that the User might use to trigger the intent.

__init__.py

__init__.py

is where most of the Skill is defined, using Python code.

Let's take a look:

from adapt.intent import IntentBuilder
from mycroft.skills.core import MycroftSkill
from mycroft.util.log import getLogger

This section of code imports the required libraries. These libraries will be required on every Skill. Your skill may need to import additional libraries.

__author__ = 'eward'

This section defines the author of the Skill. This value is usually set to the GitHub username of the author.

LOGGER = getLogger(__name__)

This section starts logging of the Skill in the mycroft-skills.log file. If you remove this line, your Skill will not log any errors, and you will have difficulty debugging.

The

class

definition extends the

MycroftSkill

class:

class HelloWorldSkill(MycroftSkill):

The class should be named logically, for example "TimeSkill", "WeatherSkill", "NewsSkill", "IPaddressSkill". If you would like guidance on what to call your Skill, please join the ~skills Channel on Mycroft Chat.

Inside the class, methods are then defined.

def __init__(self):
        super(HelloWorldSkill, self).__init__(name="HelloWorldSkill")

This method is the constructor, and the key function it has is to define the name of the Skill.

NOTE: You don't have to include the constructor unless you plan to declare state variables for the Skill object. If you plan to declare state variables, then they should be defined in this block. If you don't include the constructor, the name of the Skill will be taken from the name of the class, in this case 'HelloWorldSkill'.

Example:

def __init__(self):
        super(HelloWorldSkill, self).__init__(name="HelloWorldSkill")
        self.already_said_hello = False
        self.be_friendly = True
        self.hello_phrases = ['Hello', 'Hallå', 'Olá']
def initialize(self):
        thank_you_intent = IntentBuilder("ThankYouIntent"). 
            require("ThankYouKeyword").build()
        self.register_intent(thank_you_intent, self.handle_thank_you_intent)

        how_are_you_intent = IntentBuilder("HowAreYouIntent"). 
            require("HowAreYouKeyword").build()
        self.register_intent(how_are_you_intent,
                             self.handle_how_are_you_intent)

        hello_world_intent = IntentBuilder("HelloWorldIntent"). 
            require("HelloWorldKeyword").build()
        self.register_intent(hello_world_intent,
                             self.handle_hello_world_intent)

The

initialize()

function defines each of the Intents of the Skill. Note that there are three Intents defined in

initialize()

, and there were three Intents defined in vocab files.

Next, there are methods that handle each of the Intents.

def handle_hello_world_intent(self, message):
        self.speak_dialog("hello.world")

In the

handle_hello_world_intent()

method above, the method receives two parameters,

self

and

message

self is the reference to the object itself, and message is an incoming message from the messagebus. This method then calls the

speak_dialog()

method, passing to it the

hello.world

dialog. Remember, this is defined in the file "hello.world.dialog".

Can you guess what Mycroft will Speak?

You will usually also have a

stop()

method. This method tells Mycroft what to do if a stop intent is detected.

def stop(self):
    pass

In the above code block, the pass statement is used as a placeholder; it doesn't actually have any function. However, if the Skill had any active functionality, the stop() method would terminate the functionality, leaving the *Skill** in a known good state.

Simplifying your Skill code with intent_handler decorators

Your Skill code can be simplified using the intent_handler() decorator. The major advantage in this approach is that the Intent is described together with the method that handles the Intent. This makes your code easier to read, easier to write, and errors will be easier to identify.

Learn more about what decorators are in Python at this link.

The intent_handler() decorator tags a method to be an intent handler for the intent, removing the need for separate registration.

    @intent_handler(IntentBuilder('IntentName').require('Keyword'))
    def handler_method(self):
        # [...]

Using these decorators the Skill becomes:

class HelloWorldSkill(MycroftSkill):
    def __init__(self):
        super(HelloWorldSkill, self).__init__(name="HelloWorldSkill")

    @intent_handler(IntentBuilder("ThankYouIntent").require("ThankYouKeyword"))
    def handle_thank_you_intent(self, message):
        self.speak_dialog("welcome")

    @intent_handler(IntentBuilder("HowAreYouIntent") 
                    .require("HowAreYouKeyword"))
    def handle_how_are_you_intent(self, message):
        self.speak_dialog("how.are.you")

    @intent_handler(IntentBuilder("HelloWorldIntent") 
                    .require("HelloWorldKeyword"))
    def handle_hello_world_intent(self, message):
        self.speak_dialog("hello.world")

    def stop(self):
        pass

As seen above the entire initialize() method is removed and the Intent registration is moved to the the method declaration.

Ideally, you should use approach to Intent registration.

How do I find more information on Mycroft functions?

You can find documentation on Mycroft functions and helper methods at the Mycroft Core API documentation


Help us serve you better by rating this documentation