Node.js + AWS: Change Lights Color based on the Weather
Good evening ladies and gents, it is going to be SOOO SUNNY tomorrow.
When you get to that tomorrow:
Problem
Weather news are based on probabilities and those probabilities seem to get accurate only on the day itself.
But who has the time and energy to manually check the weather on the day itself, probably on the morning before leaving to work?
Well, certainly not me!
Long story short
tl;dr: with mighty programming skills, I made a bunch of lights automatically show me some weather forecast every day 💡
Let there be light
So… first step: I bought a LED strip, a band made of tiny lights that can change color.
Let there be WIFI
To talk to the LED strip through a computer program, I got myself a WIFI controller and connected it to the strip.
The WIFI controller gave my LED strip an IP address within my local network.
Here comes the code
I decided to use the JavaScript runtime Node.js as it comes with tons of practical libraries, performs async operations easily, and is well supported by cloud providers.
Getting weather forecast
So I created an account on the OpenWeatherMap API, an online service to retrieve weather forecasts.
Then using a Node.js wrapper, I wrote two functions:
- One to retrieve the list of weather forecasts for Paris.
- One to tell whether is is going to be rainy today in Paris based on the forecasts: if at least one forecast says it is going to rain, consider it rains.
import weather from 'openweather-apis'const getTodayWeatherForecast = () => {
return new Promise((resolve, reject) => {
weather.setLang('en')
weather.setCity('Paris')
weather.setUnits('metric')
weather.setAPPID(process.env.OPEN_WEATHER_MAP_API_KEY) weather.getWeatherForecast(function(_, forecast) {
if (forecast.cod !== "200") {
reject(`Error`)
} else {
resolve(forecast.list)
}
})
})
}export const todayIsGoingToBeRainy = async () => {
const forecast = await getTodayWeatherForecast() return forecast.some((el) => {
return el.weather[0].main === "Rain"
}
}
Establishing contact with the light strip
I then used a Node.js utility to communicate with my WIFI controller and wrote an other function. Its goal: establishing a connection to my LED strip and returning a controller object that I could then use to send instructions to the strip such as turning it on, turning it off, changing its color.
import { Control } from 'magic-home'export const getLedStrip = async () => {
const ledStripIp = process.env.LED_STRIP_IP_ADDRESS return new Control(ledStripIp, {
ack: {
color: false
}
})
}
The program: checking weather and setting LED strip color
After having defined all the functions I needed, I assembled them into this simple program:
export const checkWeatherAndSetLedStripColor = async () => {
const light = await getLedStrip()
await light.turnOn()
await light.setColor(0, 0, 0) // white const rainingToday = await todayIsGoingToBeRainy() if (rainingToday) {
return light.setColor(0, 0, 255) // blue
} else {
return light.setColor(255, 220, 0) // yellow
}
}
Step-by-step, what this code does is it establishes a connection with the LED strip. Then, it then checks whether it is going to rain today and if so orders the LED strip to become blue. If no rain is forecasted, it orders the LED strip to become yellow.
Executing the program
checkWeatherAndSetLedStripColor()
Running everything every morning
I wanted the program to run automatically every morning.
Hey, wait. Isn’t that a very good occasion of going serverless and using the cloud so that I do not even need to turn on a computer in my home at all?
Damn, if only my LED strip itself was in the cloud…
Exposing the LED strip to the world
Make the LED strip IP static
Before exposing my LED strip to the world, I needed to make sure that its IP address was going to stay the same forever.
Internet Service Providers (ISP) provide a web interface enabling anyone to administrate their local network. Somewhere within this interface, a Device Management tool made it possible to select the LED strip device and assign it a static IP address.
Allow internet to access the LED strip
Using a Port Forwarding tool available on the ISP administration interface, I created a new forwarding rule.
In that rule, I decided that the external port 5555 of my internet box would be redirected to my WIFI controller, meaning anyone out there on the internet sending a message to my internet box on the port 5555 was actually going to be talking directly to my LED strip.
And guess what, that anyone was going to be me.
Deploying everything in the cloud
Using my public IP address
Before deploying anything online in the cloud, I had to replace the local LED strip IP address known by the program until then with my public IP address. This enabled the program to be run from anywhere and not just from within my local network.
Configuring AWS
Prior to using Amazon Web Services (AWS), there are two things to do:
Choosing how to deploy on AWS
There are many ways to deploy services on AWS - in the cloud.
For this project, I chose to go with a simple framework called Serverless Framework. It is based on a kind of “infrastructure as code” paradigm and it alleviates the definition of some stuff we don’t really care about for a quick proof-of-concept project.
Configuring Serverless Framework
Right after installation, I added some configuration into a serverless.yml
file:
service:
name: led-strip-weatherprovider:
name: aws
runtime: nodejs12.x
region: eu-west-1
timeout: 30functions:
checkWeatherAndSetLedStripColor:
handler: handler.checkWeatherAndSetLedStripColor
events:
- schedule: cron(20 7 ? * MON-FRI *)
environment:
LED_STRIP_IP_ADDRESS: ${self:custom.environment.LED_STRIP_IP_ADDRESS}
OPEN_WEATHER_MAP_API_KEY: ${self:custom.environment.OPEN_WEATHER_MAP_API_KEY}turnLedStripOff:
handler: handler.turnLedStripOff
events:
- schedule: cron(0 8 ? * MON-FRI *)
environment:
LED_STRIP_IP_ADDRESS: ${self:custom.environment.LED_STRIP_IP_ADDRESS}custom:
environment: ${file(.env)}
The file first declares that we want AWS as our cloud provider and that the code will run through Node.js.
Then it states that our services are going to be two functions:
- One that checks the weather and sets the LED strip color accordingly.
- One that turns the LED strip off.
I provided earlier the code of the first function. Here is the code of second one, much plain simple:
export const turnLedStripOff = async () => {
const light = await getLedStrip()
return light.turnOff()
}
In addition to the code, the functions are also given environment variables and are binded with events so that they get run automatically at certain points in time.
Here I decided that the first function would run at 7.20 am once every weekday and that the second function will run at 8.00 am every weekday.
Deploying
With the configuration properly set up, deploying the service on AWS was done by executing the following command:
serverless deploy
The deployment actually created the two lambda functions on AWS and assigned them the defined schedule events. The final architecture is as follow:
Final result
Every morning I see blue lights, I take a coat with me.
Every morning I see yellow lights, I don’t.
Thanks for reading along!