Creating a Weather Skill

We will create a skill, that will make opsdroid tell us the current weather in a town. This skill will be quite similar to the official weather skill that can be found in the opsdroid repo.

This tutorial will use the OpenWeatherMap API to get the weather information from a city. We will only need the free version of the API, so register and get your key from the API menu.

Setting Up

In the tutorial, you can choose the city that you want the weather information about and which system to use (metric or imperial). These settings will be specified in our opsdroid configuration.yaml file.

Our __init__.py init file will contain two functions, one that interacts with the OpenWeatherMap API and one that opsdroid will use to tell us the weather in our city.

Building the Skill

We are ready to start working on our skill. First, you need to create a folder for the weather skill. Choose a location and name it weather-skill.

Now, let's open opsdroid configuration.yaml file and add our weather skill to the skills section.

skills:
  - name: weather
    city: < Your city, your country >     # For accuracy use {city},{country code}          
    units: < metric/imperial >       # Choose metric/imperial
    api-key: < Your Api Key >
    # Developing the skill
    path: < full path of your weather-skill folder >
    no-cache: true

Note: We will need to set no-cache to true, in order to tell opsdroid to install the skill on every opsdroid run.

Weather Skill

Now that our skill has all the configuration details set up in the configuration.yaml file, let's create the __init__.py inside our weather-skill folder and start working on the skill.

The first thing we need to do, is to import the regex_matcher from opsdroid and the aiohttp module. Your __init__.py file should look like this:

from opsdroid.matchers import match_regex

import aiohttp

Get Weather Data

Now we need to get the weather data from OpenWeatherMap. Let's create an asynchronous function to get the weather data.

If you read the current weather data documentation from OpenWeatherMap, you will see that the API endpoint that we need to use is http://api.openweathermap.org/data/2.5/weather?q=

Make your __init__.py file look like this:

from opsdroid.matchers import match_regex

import aiohttp


async def get_weather(config):
    api_url = "http://api.openweathermap.org/data/2.5/weather?q="

We will need to pass the following things to OpenWeatherMap: - City - Units - API-key

Since these details are already in our opsdroid configuration.yaml we can simply get them from the config.

Make your function look like this:

async def get_weather(config):
    api_url = "http://api.openweathermap.org/data/2.5/weather?q="
    parameters = "{}&units={}&appid={}".format(
        config['city'], config['units'], config['api-key'])

If we join api_url and parameters we get the full URL that we need to get our weather data. We will now use aiohttp to start a session and get the data from OpenWeatherMap.

Your function should look like this now:

async def get_weather(config):
    api_url = "http://api.openweathermap.org/data/2.5/weather?q="
    parameters = "{}&units={}&appid={}".format(
        config['city'], config['units'], config['api-key'])

    async with aiohttp.ClientSession() as session:
        response = await session.get(api_url + parameters)

Our get_weather function is starting to look good. What's left to do is returning our response in a JSON format. Luckily aiohttp makes that quite easy for us, all we need to do is add .json() to our response.

Make our function to look like this:

async def get_weather(config):
    api_url = "http://api.openweathermap.org/data/2.5/weather?q="
    parameters = "{}&units={}&appid={}".format(
        config['city'], config['units'], config['api-key'])

    async with aiohttp.ClientSession() as session:
        response = await session.get(api_url + parameters)
    return response.json()

Now when we call our get_weather function, aiohttp will get all the data from the OpenWeatherMap API and return it to us in a json format.

response.json() will give us something that looks like this:

{
     'coord': 
         {
             'lon': -0.13, 
             'lat': 51.51
         }, 
     'weather': 
         [
             {
                 'id': 800, 
                 'main': 'Clear', 
                 'description': 'clear sky', 
                 'icon': '01n'
             }
         ], 
     'base': 'stations', 
     'main': 
         {
             'temp': 3.37, 
             'pressure': 1022, 
             'humidity': 86, 
             'temp_min': 2, 
             'temp_max': 5
         }, 
     'visibility': 10000, 
     'wind': 
         {
             'speed': 2.1, 
             'deg': 310
         }, 
     'clouds': 
         {
             'all': 0
         }, 
     'dt': 1511076000, 
     'sys': 
         {
             'type': 1, 
             'id': 5089, 
             'message': 0.1668, 
             'country': 'GB', 
             'sunrise': 1511076308, 
             'sunset': 1511107601
         }, 
     'id': 2639545, 
     'name': 'London', 
     'cod': 200
 }

That's all we need to do. Now if we call our function we will be able to get our weather data. The next step is to make opsdroid tell us the current weather.

Tell the Weather

This skill is quite easy to understand and it will simply call our get_weather function and then get the details from our weather data.

We also need to decorate the skill with our chosen matcher (regex in this case).

Make your __init__.py file look like this:

from opsdroid.matchers import match_regex

import aiohttp


async def get_weather(config):
    api_url = "http://api.openweathermap.org/data/2.5/weather?q="
    parameters = "{}&units={}&appid={}".format(
        config['city'], config['units'], config['api-key'])

    async with aiohttp.ClientSession() as session:
        response = await session.get(api_url + parameters)
    return response.json()


@match_regex()
async def tell_weather(opsdroid, config, message):
    pass

We need to chose what should trigger opsdroid to tell us the weather. Let's make opsdroid trigger when we type How's the weather.

@match_regex("How's the weather?")
async def tell_weather(opsdroid, config, message):
    pass

Now that we have a way to trigger the skill we will use our get_weather function to get the weather data. For the sake of readability, we will create some variables to hold some of the weather data as well.

Make your function look like this:

async def tell_weather(opsdroid, config, message):
    weather_data = await get_weather(config)
    temp = weather_data['main']['temp']
    humidity = weather_data['main']['humidity']
    city = weather_data['name']

What's left to do is to make opsdroid say the temperature in your city. We can do that by calling message.respond()

Change tell_weather function to the following:

async def tell_weather(opsdroid, config, message):
    weather_data = await get_weather(config)
    temp = weather_data['main']['temp']
    humidity = weather_data['main']['humidity']
    city = weather_data['name']

    await message.respond("It's {} and {}% humidity in {}.".format(temp, humidity, city))

Now every time you type How's the weather opsdroid will tell you the current weather. Hopefully this tutorial was helpful to you, if you need any help join us in the opsdroid gitter channel.