Sending Weather Alerts to Microsoft Teams

2 weeks ago I published an article called Sending Weather Alerts to Netcool and last week I followed it up with another blog on sending the same alerts to Slack. However, a few Orb Data colleagues told me I had chosen the wrong messaging tool as Microsoft Teams is used far more widely than Slack. Indeed when I did a little research I found Microsoft Teams had 145 Million users as of April 2021 whereas Slack has a little over 12 million daily active users (9 million of which use the free platform). With this in mind, this is the last part of my now 3 part blog on sending weather alerts to various platforms.

If you are serious about monitoring your business services it is not enough to just look at the performance of your traditional IT hardware and software services. You also need to look at external forces that may affect your business from weather to social media to news sentiment. I’ve recently been looking at all of these subjects but this blog will concentrate on a simple method for monitoring the future weather at your business locations. For some businesses this is more important than others; if you have remote sites (such as ATMs, cell sites, factories, data centres, distribution centres, etc) that could cause loss of service or supply chain issues if they are out of service then you should be monitoring the weather in those locations so that you can mitigate these issues. As climate change becomes more extreme these issues will only become more prevalent with larger and more frequent storms, more flooding and an increased likelihood of Forest Fires.

Even in the UK, a country not noted for weather extremes, we are starting to see these issues become more frequent. I worked at a company that had positioned its main data centre in Tewkesbury which was fine until in 2007 there were major floods which were repeated in 2012 and 2019.

In 2016 the Vodafone UK data centre was flooded over Christmas which lead to a disruption to their services.

With this in mind, this blog shows a simple way of using a python script to monitor the forecasted weather in your locations. The script then sends the alerts to a MIcrosoft Teams channel as messages.

Getting the Data

There are a few APIs that offer weather information, but I chose OpenWeatherMap.org as they offered a free starting tier that allows for development with up to 60 calls/minute and 1,000,000 calls/month. This may not be enough for your requirements, but you can see the pricing here if you want to make more calls or get more detailed data. The other advantage to openweathermap.org is that they offer what they call “one callAPI that provides the following weather data for any geographical coordinates:

  1. Current weather
  2. Minute forecast for 1 hour
  3. Hourly forecast for 48 hours
  4. Daily forecast for 7 days
  5. National weather alerts
  6. Historical weather data for the previous 5 days

The 2 items that were important for this purpose were the daily forecast and National weather alerts that are sent from major national weather warning systems. These latter alerts are generally provided in English but as they are local warnings they could be in the local language of the provider.  Items 1-3 and 6 are not useful for alerting and can be excluded from the API call which is made as follows:

https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude={part}&appid={API_key}

Note:   You will need to register to get the API key before you can use it.

Therefore, we want 2 types of data:

Daily

The daily data is provided as follows (the data has been cut down for simplicity):

'daily': [
{'dt': 1634616000, 'sunrise': 1634597595, 'sunset': 1634639375, 'moonrise': 1634637300, 'moonset': 1634592720, 'moon_phase': 0.45, 'temp': {'day': 296.18, 'min': 292.32, 'max': 297.54, 'night': 295.74, 'eve': 296.97, 'morn': 292.44}, 'feels_like': {'day': 296.71, 'night': 296.43, 'eve': 297.57, 'morn': 292.75}, 'pressure': 1017, 'humidity': 83, 'dew_point': 293.14, 'wind_speed': 2.93, 'wind_deg': 319, 'wind_gust': 5.79, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': 100, 'pop': 0.24, 'uvi': 0},
{'dt': 1634702400, 'sunrise': 1634684019, 'sunset': 1634725730, 'moonrise': 1634725680, 'moonset': 1634682180, 'moon_phase': 0.5, 'temp': {'day': 302.93, 'min': 295.13, 'max': 305.06, 'night': 298.19, 'eve': 301.62, 'morn': 295.42}, 'feels_like': {'day': 306.56, 'night': 299.18, 'eve': 305.07, 'morn': 296.18}, 'pressure': 1013, 'humidity': 65, 'dew_point': 295.65, 'wind_speed': 5.84, 'wind_deg': 143, 'wind_gust': 9.7, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': 100, 'pop': 0.21, 'uvi': 0},
{'dt': 1634788800, 'sunrise': 1634770443, 'sunset': 1634812086, 'moonrise': 1634814120, 'moonset': 1634771640, 'moon_phase': 0.52, 'temp': {'day': 291.55, 'min': 291.55, 'max': 298.05, 'night': 291.98, 'eve': 292.32, 'morn': 297.58}, 'feels_like': {'day': 291.72, 'night': 291.82, 'eve': 292.22, 'morn': 298.59}, 'pressure': 1017, 'humidity': 87, 'dew_point': 289.42, 'wind_speed': 8.77, 'wind_deg': 36, 'wind_gust': 15.51, 'weather': [{'id': 502, 'main': 'Rain', 'description': 'heavy intensity rain', 'icon': '10d'}], 'clouds': 100, 'pop': 1, 'rain': 37.5, 'uvi': 0},
…
{'dt': 1635220800, 'sunrise': 1635202572, 'sunset': 1635243881, 'moonrise': 1635258660, 'moonset': 1635219300, 'moon_phase': 0.67, 'temp': {'day': 295.95, 'min': 293.24, 'max': 296.42, 'night': 294.24, 'eve': 296.17, 'morn': 293.33}, 'feels_like': {'day': 296.06, 'night': 294.13, 'eve': 296.36, 'morn': 293.47}, 'pressure': 1019, 'humidity': 68, 'dew_point': 289.84, 'wind_speed': 4.77, 'wind_deg': 17, 'wind_gust': 10.15, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}], 'clouds': 90, 'pop': 0, 'uvi': 0}]}

Alerts

And the alerts data is provided as follows:

    "alerts": [
    {
      "sender_name": "NWS Tulsa",
      "event": "Heat Advisory",
      "start": 1597341600,
      "end": 1597366800,
      "description": "...HEAT ADVISORY REMAINS IN EFFECT FROM 1 PM THIS AFTERNOON TO\n8 PM CDT THIS EVENING...\n* WHAT...Heat index values of 105 to 109 degrees expected.\n* WHERE...Creek, Okfuskee, Okmulgee, McIntosh, Pittsburg,\nLatimer, Pushmataha, and Choctaw Counties.\n* WHEN...From 1 PM to 8 PM CDT Thursday.\n* IMPACTS...The combination of hot temperatures and high\nhumidity will combine to create a dangerous situation in which\nheat illnesses are possible.",

      "tags": [
        "Extreme temperature value"
        ]
    },
    ...
  ]

Data Analysis and Conversion

There are a few things to note about this data.

  1. Firstly, all dates are provided as Unix time (UTC zone) which need to be converted.

e.g.

timestamp = datetime.datetime.fromtimestamp(time)
curr_time = timestamp.strftime('%d/%m/%Y %H:%M:%S')

*where time is the Unix time.

  1. Secondly, if the forecast does not include rain or snow, they are simply not returned at all so you need to test for their existence before getting the value.
  2. All data is returned as standard metric measurements by default. This can be changed to imperial measurements, but this is important as the default temperature is Kelvin (which I convert to Celsius) and wind speed is metre/sec (which I leave) rather than miles per hour.
  3. As you may have noticed the OpenWeatherMap one call API call needs to be made using longitude and latitude instead of a location name. This has its advantages as it gives some accuracy to the location but is a pain for my script as I wanted to create the config file using place names for simplicity. Therefore to do this I used the Nominatim API. Nominatim is a geocoder that can identify geometries in OSM data corresponding to a given string. In this example Python code I supply a city name and get 2 variables back: location.latitude and location.longitude. Occasionally this API times out and so you would need to allow for that in your script.

e.g.

import geopy
from geopy import Nominatim
city_name = “Bangkok”
locator = Nominatim(user_agent="myGeocoder")
location = locator.geocode(city_name)
logging.info("Latitude: {}, Longitude: {}".format(location.latitude, location.longitude))

Microsoft Teams Integration

To send the alerts we are going to add an incoming webhook to a Teams channel.

Note: to complete this integration you will need to be the admin in your Teams workspace. In my case, I had an Orb Data Workspace and I created a new channel called weather-alerts that I wanted to send my weather warnings.

Once you’ve created the channel navigate to it within Teams where and select (•••) More Options from the top navigation bar and then choose Connectors from the drop-down menu.

Choose Incoming Webhook in the list and click Add or Configure (if it has been used before).In the resulting dialogue provide a name (in my case weather-alerts), and optionally, upload an image avatar for your webhook.

At the bottom of the window, there is a unique URL that maps to your unique channel. Copy and save this URL as we will need it later in Python.

Select the Save button and now the Webhook will be available in your MS Teams channel.

Back in Python, you need to add a section in your script to send the message/alert.  The code is relatively simple and can use an existing Python library called pysteams which will need to be imported at the top of the scriptThe version I’m showing here is not the final script I used as I decided to add buttons, external links, images and sub-sections (cards) which make the output look a lot nicer. This version though is enough to send basic alerts.

import pymsteams
#########################
# MS Teams alert
#########################
def teams_alert(alert_type, alert_string):
    logging.info("Sending MS Teams Alert from " + city_name)
    # Use the Microsoft Webhook URL we found earlier
    weatherTeams = pymsteams.connectorcard(msteams_url)
    
    # Give the message a title
    weatherTeams.title(alert_type)

    # Add text to the message.
    weatherTeams.text(alerts_string)

    # send the message.
    weatherTeamsMessage.send()

Configuration

I wanted to make the configuration file (in my case config.ini) as simple as possible and so it has only 5 sections. I may add more based on customer requirements later but for now this is enough. These are the sections:

GENERAL

This section has a polling interval, a customer name to be used if you have multiple customers (as we do) and an alerts parameter to allow for the Critical National Weather alerts to be turned off. The polling interval in my example is 15 minutes (900 seconds) but this could be much larger as the forecast does not change too often. I have opted to allow for MS Teams alerts to be turned on or off as well as socket alerts which in the last blog were hard coded to always be on.

e.g.

[GENERAL]
poll=900
customer=ORB
alerts=TRUE
slack=TRUE
socket=TRUE
msteams=TRUE

MSTEAMS

The Microsoft Teams section contains the URL we previously created when we configured Microsoft Teams itself. I’ve changed the values as I don’t want random messages on our channel.

[MSTEAMS]
msteams_url = "https://orbdatalimited.webhook.office.com/webhookb2/yyyyyyyyyyyyyyyy/zzzzzzzzxxxxxxxxxxxx"

GEO

The GEO section is a comma-separated list of placenames. You can put in as many as you want but remember the limitation on calls per day. e.g.

[GEO]
locations=Tokyo,Kamchatka Krai,Bronx

LOGFILE

The log parameter contains the location of the log.

e.g.

[LOGFILE]
log=weather.log

DAILY_THRESHOLDS

As I said earlier the script checks both the daily forecasts and national alerts. The alerts do not need any configuration but to create alerts from the forecasts you need to add thresholds for what you consider unacceptable conditions. Most of the parameters are greater than (gt) but I also included one less than (lt) for temperature as a sudden freeze could also include issues for remote sites both for access and potential damage to IT equipment.  You can add your thresholds as you see fit. Remember when setting these values that they need to match the units you are retrieving the data (e.g. metres per second for wind speed).

e.g.

[DAILY_THRESHOLDS]
rain_gt = 10
snow_gt = 10
wind_gt = 12
temp_gt = 40
temp_lt = -5
humidity_gt = 90
pressure_gt = 1000

Execution and Data Flow

The simplest way to describe how the script works is to look at the flowing whiteboard diagram.

Let’s start with the execution of the script.

  1. The script can be executed anywhere as either a long-running daemon type process or scheduled intermittently as a one-off. I chose the former as was easier to allow for the polling interval to be added to the config file (which I called config.ini). Once it is run it looks for the config file and gets the GEO locations to report on, the thresholds to alert upon and the location to send alerts to. In my example, this script can be located anywhere, even on a laptop with internet access.
  2. The locations are retrieved and converted into longitude and latitude from the Nominatim API.
  3. For each named location (now longitude and latitude) the One Call API is run against OpenWeatherMap.org and the data is retrieved.
  4. The script will then cycle through this data and based on thresholds retrieved from the config file send Warning alerts via a socket connection to the Socket Probe. I have used this as an example, but the alerts could be sent using any other mechanism such as email or via another monitoring products API. It will also check if there are any National Weather alerts and send those too.
  5. The alerts are then forwarded as messages via a WebHook to a Teams Channel.
  6. The Microsoft Teams channel should now show these messages.

In this example, below, you can see both the threshold driven alerts for high Wind Speed and Low Temperature. There was also a national weather alert in Russian for Wind.

I haven’t provided the script in this blog as this work was created for a customer however if you would like something similar, we can modify the work to meet your requirements and provide a completed solution. Alternatively, feel free to create your solution based on the ideas I have shown here.

If you have questions, please don’t hesitate to contact me.

Visits: 625