How To Build A Python Weather Notification Bot Using Openweathermap API

Sharing is caring!

Last Updated on July 14, 2022 by Jay

In this tutorial I’ll show you how to build a Python bot to send weather notifications to your phone using the Openweathermap API.

I got tired of checking the weather forecast every morning to see if it will rain during the day (so that I need to bring an umbrella). So I thought, why not make a bot that will only send me a notification IF there will be rain for the day? Let’s get started!

Libraries

We’ll need four libraries for this bot: requests, json, datetime, os, and selenium. Type the following into the command prompt to install if you don’t already have those libraries. Note that datetime, os, and json are build-in libraries so no installation is required.

pip install requests selenium
  • os – for retrieving API keys from the system’s environment variable
  • json – to parse data in json format
  • datetime – to process datetime data
  • requests – for downloading data from the Internet
  • selenium – web-browser automation tool that we’ll use to send Whatsapp message to our phone

Weather Data From Openweathermap

The weather data we’ll be using is from this website https://openweathermap.org. They provide quite a lot of weather-related data through several different APIs. The one we’ll use is called “5 day / 3 hour forecast data”, which gives weather forecast for 5 days with data every 3 hours apart. This is a free service that provides 60 API calls per minute.

The API is usually just a website URL, but a dynamic one. For example, the API we are using today has the following URL patterns – those inside {} are variables:

api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={API key}

## or 

api.openweathermap.org/data/2.5/forecast?q={city name},{country code}&appid={API key}

## or

api.openweathermap.org/data/2.5/forecast?zip={zip code},{country code}&appid={API key}

If you happen to know the latitude and longitude coordinates of your location, feel free to use the first URL. I find the 2nd one (with city and country) easier to use so will stick to that. To get weather data for my location (Toronto, Canada), I just need to enter the following URL into a web browser:

Use Python To Get Weather Data From API
Use Python To Get Weather Data From API

The units parameter is important here. If we don’t include it as part of the URL, it’s going to return the default unit in Kelvin, which is rarely used in daily life. For reference the conversion formula is K-273.15=C. So 273.15K is at 0 degree Celcius.

In most cases, we’ll be setting either units=metric (Celcius) or units=imperial (Fahrenheit).

Getting An API Key

To use their service and weather data, we need to register for a (free) account and obtain an API key. Once registered, click on your user name and then “My API keys”. The API key will show up.

Getting Openweathermap API key
Getting Openweathermap API key

Storing the API Key

The best practice for storing sensitive information such as passwords or API keys is to NEVER store them inside our Python (or any other programming language) code.

Instead, we can save them elsewhere in the computer, such as in the system environment variable. We’ll save this API key with a variable name “OPENWEATHERMAP_API”.

Store the API Key in Environment Variable
Store the API Key in Environment Variable

We can then use the os Python library to retrieve the information from the environment variable called OPENWEATHERMAP_API.

>>> import os
>>> os.getenv('OPENWEATHERMAP_API')
'48xxxxxxxxx19'

Use Python To Get Weather Data From Openweathermap API

We saw that once we enter an appropriate URL, we’ll get some text data (json format) in return. The requests library is perfect for downloading data from the Internet.

In the below code, we’ll use the f-string to plug the api_key into the URL. Then use the requests.get() to download data. The result should be a <Response [200]> object, which means connection OK or Success.

The text data is stored inside the .text attribute of the requests object. We can verify data was downloaded successfully by checking it. I usually just check the first 100 characters since displaying too many characters on screen might freeze the IDE.

import requests
import os
import json

api_key = os.getenr('OPENWEATHERMAP_API')

url = f'https://api.openweathermap.org/data/2.5/forecast?q=toronto,ca&units=metric&appid={api_key}'

r = requests.get(url)
<Response [200]>

r.text[:100]
'{"cod":"200","message":0,"cnt":40,"list":[{"dt":1657000800,"main":{"temp":19.88,"feels_like":19.65,"'

We’ll then use the json library to help parse the text data. Basically now our text data is in a convenient Python dictionary object. Note the “loads” from json.loads() method refers to load string.

data = json.loads(r.text)

Process Weather Data

We notice that the relevant forecast data is stored inside the dictionary with the key ‘list’. There are 40 3-hour block forecasts here.

len(data['list'])
40

Inside each one, there is a lot of useful information. Note the below item is another dictionary with keys which we can use to help extract information:

data['list'][0]
{'dt': 1657000800,
 'main': {'temp': 19.88,
  'feels_like': 19.65,
  'temp_min': 17.66,
  'temp_max': 19.88,
  'pressure': 1017,
  'sea_level': 1017,
  'grnd_level': 997,
  'humidity': 66,
  'temp_kf': 2.22},
 'weather': [{'id': 500,
   'main': 'Rain',
   'description': 'light rain',
   'icon': '10n'}],
 'clouds': {'all': 100},
 'wind': {'speed': 1.74, 'deg': 100, 'gust': 3.48},
 'visibility': 10000,
 'pop': 0.91,
 'rain': {'3h': 0.68},
 'sys': {'pod': 'n'},
 'dt_txt': '2022-07-05 06:00:00'}

The information we care about most is whether it’s going to rain during the course of the day. So let’s extract that from the above dictionary.

  • dt” for datetime, raw data is in a “timestamp” format
  • weather” -> “main” for the main weather (rainy, cloudy, sunny, etc)
def rain_today(location):
    ## get API key from env variable
    api_key = os.getenv('OPENWEATHERMAP_API')

    ## API endpoint
    url = f'https://api.openweathermap.org/data/2.5/forecast?q={location}&units=metric&appid={api_key}'

    r = requests.get(url)
    data = json.loads(r.text)
    
    today = dt.datetime.today().date()
    rain = False
    for d in data['list']:
        temp_dt = dt.datetime.fromtimestamp(d['dt'])  ## convert date
        if temp_dt.date() <= today:
            main_weather = d['weather'][0]['main']        ## extract main weather
            if main_weather in ['Rain', 'rain']:
                rain = True
            print(f"time is: {temp_dt}, and weather is {main_weather}.")
    return rain

time is: 2022-07-08 02:00:00, and weather is Clouds.
time is: 2022-07-08 05:00:00, and weather is Clouds.
time is: 2022-07-08 08:00:00, and weather is Clouds.
time is: 2022-07-08 11:00:00, and weather is Clouds.
time is: 2022-07-08 14:00:00, and weather is Rain.
time is: 2022-07-08 17:00:00, and weather is Rain.
time is: 2022-07-08 20:00:00, and weather is Rain.
time is: 2022-07-08 23:00:00, and weather is Clouds.

Now we know that today it’s going to rain, so let’s send ourselves a reminder on Whatsapp.

Send Notification Via Whatsapp

We have a tutorial on how to send messages via Whatsapp. Feel free to check it out here. So we are going to re-use the Chrome profile created earlier for automating Whatsapp. With the setup we walked about in this tutorial, we should be able to run the below function and control Whatsapp using Python.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
import time

def send_whatsapp(phone_number, msg):
    wassup_url = rf'https://web.whatsapp.com/send?phone={phone_number}&text&type=phone_number&app_absent=0'

    ## setting url for Chrome profile and webdriver options
    opt = Options()
    opt.add_argument(r'user-data-dir=C:\Users\jay\Desktop\PythonInOffice\amazon_price_alert_bot\whatsapp_profile')
    driver = webdriver.Chrome(r'C:\Users\jay\Desktop\PythonInOffice\amazon_price_alert_bot\chromedriver.exe', options=opt)

    ## open chrome browser, ignore error
    ## the error appears to be a bug in the v103 chrome
    try:
        driver.get(wassup_url )
    except Exception as e:
        print(e)

    ## find chat box location and send a message
    time.sleep(20)
    chatbox = driver.find_element_by_xpath('//*[@id="main"]/footer/div[1]/div/span[2]/div/div[2]/div[1]/div/div[2]')

    chatbox.send_keys(msg)
    time.sleep(0.1)
    chatbox.send_keys(Keys.RETURN)

Update Chrome Driver

From time to time, the Chrome browser will automatically update itself. When it does that, we have to update the chrome webdriver. If we get the following error message when trying to open a chrome driver, it means we need an update.

selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 96
Current browser version is 103.0.5060.66 with binary path C:\Program Files\Google\Chrome\Application\chrome.exe

The error tells us that the current Chrome browser version is 103.0.5060.66. Simply head to the chromdriver website (https://chromedriver.chromium.org/downloads) and download the corresponding chromedriver.

Putting It Together

We now have a Python function to check the weather via an API, and another function to send messages via Whatsapp.

if rain_today('toronto,ca'):
    send_whatsapp(your_phone_num, 'Will rain today, bring an umbrella!')

Setting Up Automatic Tasks

The last step of the bot is to set up to run the program automatically every day. Let’s say around 7a.m. This way, we’ll get a notification on our phone every morning if the forecast says it will rain for the day. Check out this tutorial to learn how to set up automatic runs.

Additional Resources

Use Python To Send WhatsApp Message

Automate Python Script In Windows

Leave a Reply

Your email address will not be published. Required fields are marked *