Recently I needed to get a daily weather forecast, but wanted to get it over text message rather than an app or email.
After looking at a few technologies, I realized I could build my own daily alert with a little JavaScript.
This post is going to cover how I setup a daily weather forecast that was delivered via AWS Lambda and Twilio. My project was written in JavaScript and uses the AWS node SDK.
An open source version of my project is available on GitHub here.
The Overall Flow
Before we get too heavy in the details, I wanted to highlight the overall flow.
The project that I built has the following flow:
- Lambda is triggered by a CloudWatch scheduled cron job
- Lambda calls the OpenWeatherMap API for current conditions
- Lambda calls the NOAA API for forecast data
- A message is created with all of the content
- A text message is sent via Twilio's APIs
Creating the AWS Lambda and ClaudiaJS
The first step to this project was to create an AWS Lambda. Lambda's are AWS serverless platform, and have become very popular in recent years. The whole premise of Lambdas is to have code running in the cloud. Developers can deploy their code and it will run when necessary. Ultimately, alleviating developers from the need to maintain hosted infrastructure. To learn more I recommend checking out the AWS Documentation on Lambdas here.
To create the Lambda that I used, I used the ClaudiaJS CLI. ClaudiaJS makes creating Lambdas super simple because it saves you time and effort from working in the AWS console. The CLI enables you to use prebuilt scripts with parameters to build and deploy your infrastructure. Once you've built a Lambda, ClaudiaJS also enables you to update it and test it locally. ClaudiaJS has lots of great tutorials, and I recommend you start with the tutorial here.
I also have written a separate post that goes into more detail about using ClaudiaJS to build a serverless API here.
Following the ClaudiaJS guidelines I used the basic claudia create
and claudia update
commands. In order to do this yourself, you'll first need to have ClaudiaJS CLI and the AWS CLI setup.
Once you've got the initial setup, do a git clone
of my project. Please consult my project's README on how to setup the environment variables.
Once you've added the environment variables to your terminal's profile, you can use the project's npm scripts to do the rest.
In the terminal, navigate to where you cloned the project and run npm run create-lambda
.
If you want to do updates to the lambda, you can also use my npm script with npm run update-lambda
, and that will apply any code changes directly to your Lambda in AWS.
Once your Lambda is built, you can also test it with ClaudiaJS using npm script in my project. In the terminal just run npm run local-test
to test your Lambda locally.
Getting the Weather Forecast
In order to get the Weather Forecast I used both the OpenWeatherMap API and the United States National Oceanic and Atmospheric (NOAA) APIs. The NOAA APIs are free, but you'll need to create a (free) account with the OpenWeatherMap API to use their endpoints. Checkout the OpenWeatherMap API site here to get a key.
In my Lambda I retrieved the forecast from both the OpenWeatherMap API and NOAA with the following:
const APIKey = process.env.OPEN_WEATHER_MAP_API_KEY;
const latitude = process.env.LATITUDE;
const longitude = process.env.LONGITUDE;
const units = "imperial";
// OpenWeatherMapAPI
const openWeatherMapAPIURL = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&units=${units}&appid=${APIKey}`;
const currentWeather = await axios.get(openWeatherMapAPIURL).catch(error => {
console.log(error);
return;
});
// NOAA Metadata
const NOAAMetadata = await axios
.get(`https://api.weather.gov/points/${latitude},${longitude}`)
.catch(error => {
console.log(error);
return;
});
// NOAA Weekly
const NOAAWeeklyForecast = await axios
.get(NOAAMetadata.data.properties.forecast)
.catch(error => {
console.log(error);
return;
});
// NOAA Hourly
const NOAAHourlyForecast = await axios
.get(NOAAMetadata.data.properties.forecastHourly)
.catch(error => {
console.log(error);
return;
});
If you notice, I'm using axios here to do the HTTP calls. This made each call super simple and easy to work with.
I used the Latitude and Longitude coordinates of the location I wanted weather for to make the calls. I first called the OpenWeatherMap API to get current weather conditions. I then called the metadata
, weekly forecast
, and hourly
NOAA endpoints to get the forecast.
Once I successfully retrieved the weather information, it was just a matter of parsing the response as you see here:
const hoursToday = retrieveHours(NOAAHourlyForecast.data.properties.periods);
let highTemp = 0;
hoursToday.forEach(period => {
if (parseInt(period.temperature) > highTemp) {
highTemp = period.temperature;
}
});
let lowTemp = highTemp;
hoursToday.forEach(period => {
if (parseInt(period.temperature) < lowTemp) {
lowTemp = period.temperature;
}
});
const sunrise = formatTime(currentWeather.data.sys.sunrise);
const sunset = formatTime(currentWeather.data.sys.sunset);
const message =
"WEATHER TEXT:\n" +
"\n" +
"Good Morning! ☀️ 💦 🌤 ⛈ \n" +
"Here's the lowdown for today...\n" +
"\n" +
// to show degree symbol on OSX hold shift + option + 8
`temp: ${currentWeather.data.main.temp.toFixed(0)}°\n` +
`high: ${highTemp.toString()}°\n` +
`low: ${lowTemp.toString()}°\n` +
`wind: ${currentWeather.data.wind.speed.toFixed(0)} MPH\n` +
`sunrise: ${sunrise} AM\n` +
`sunset: ${sunset} PM\n` +
"\n" +
`forecast: ${NOAAWeeklyForecast.data.properties.periods[0].detailedForecast}\n` +
"\n" +
"Have a good day! 🎉🎉 🎉 🎉";
Note I also used a helper function for the sunrise
and sunset
values. These were just to format the date representation as a number that was returned by the OpenWeatherMap API. I know there are more elegant ways to handle this, but my small functions achieved what I was looking for. Note that the OpenWeatherMap API returns the time in terms of UTC, so you'll need to accommodate that in your message based on your timezone.
I also created a helper function to retrieve the hour periods from the hourly forecast specific for today. I had originally intended to use the OpenWeatherMapAPI for the high and low temp, but found that I had to subscribe to their paid service for that. Unfortunately, the NOAA API for current conditions is also unreliable because some weather stations do not regularly update. So to resolve this I used the (reliable) NOAA hourly forecast endpoint, and just parsed the data to determine the expected high and low of the day.
Sending the actual Message
So now that I've retrieved my weather forecast, I needed to send it as a text message.
I should note that I spent a fair amount of time playing with AWS SNS service. My original idea was to leverage an SNS topic that could send a message to a phone number (and email). The issue I found was that AWS has a limit on the number of SNS Topic messages that could be published to phone numbers. This is for security and to avoid people abusing the text message functionality. Since there was a limit, I wanted something that I could have (potentially) infinite abilities to use. So I looked at Twilio.
Twilio provides a great service that enables both voice and text service through their APIs. To get started with them is super easy. You just create a free trial account, and they give you a number to start using. With this number you can send both text and voice messages. Twilio has great documentation and I recommend checking out the site here.
When you first get setup with Twilio, you have a trial account. If you upgrade your account, you can have a "pay as you go" access. You also can reserve the number that you were given in the trial period. Both of these options are relatively inexpensive and easy to setup.
For the purpose of this project, I just needed to use the text message functionality. So I ended up just doing the following:
let response = "lambda completed with ";
await client.messages
.create({
body: message,
from: process.env.TWILIO_FROM,
to: process.env.TWILIO_TO
})
.then(success => {
console.log(success.sid);
response = response + "success";
})
.catch(error => {
console.log(error);
response = response + " error";
});
return response;
I included the response
object just to be able to capture something to return when I tested my lambda.
Calling the Lambda
There are many ways you can trigger a Lambda in AWS. I chose to just create a CloudWatch rule that triggers once a day. This was very easy to do with the AWS console. There are other ways to do this, but I chose just to do this here as I had a very basic usecase. To setup a cron job with AWS CloudWatch check out the site here.
Closing Thoughts
So my end product turned out really well and enabled me to send my weather forecast as a text message. In less than 200 lines of code I was able to connect several technologies to provide my weather forecast.
I highly recommend checking out AWS Lambdas and the Twilio service. I learned some in this project, and hope this blog post (and my project) helped you.
Feel free to comment and follow me on Twitter at @AndrewEvans0102!
(cover image source is pixabay)
Top comments (2)
Nice tutorial, thanks for writing it!
This is pretty cool, I was using SNS for a similar personal project and ran into that nasty text limit. Twilio sounds like a great alternative.