DEV Community

Cover image for Show Dev: RaspberryPi Weather ⛈ fuelled by Netlify functions and Preact
moi gonzalez (he/him)
moi gonzalez (he/him)

Posted on • Updated on

Show Dev: RaspberryPi Weather ⛈ fuelled by Netlify functions and Preact

What do you get when you mix a frontend developer, a raspberry pi gathering dust for months and a quarantine?

I live in a city where there are days when you could have all 4 seasons within a single day. That's why I decided to build an app with daily weather reporting using an old raspberry pi 🔥

✨ The Result

What I totally love about web technologies is that you can build anything from websites to mobile apps. With this little project I hope to show that you can also power Internet of Things with some modifications and optimisations and have awesome results.

📸 Swag

Light mode 🌝

Alt Text

Dark mode 🌚

Alt Text

Sunset time 🌘

Alt Text

🐙 Source Code

GitHub logo moigonzalez / weather-preactpi

A tiny UI for daily weather forcasts ⛈

🚀 Netlify Functions

This was the most interesting part of the project. Even though I just scratched the surface of using Netlify functions I'm already loving it 😍. I could make a lambda function that gets the user location based on IP and then get the weather for this location. It looks like this:

get-weather.js

const fetch = require('node-fetch');
const IPinfo = require("node-ipinfo");
const ipinfo = new IPinfo(process.env.IP_INFO_TOKEN);

exports.handler = async function(event) {
  // The IP comes for free
  const ip = event.headers['client-ip'];

  // IPinfo gives us the location data
  const { _city, _countryCode } = await ipinfo.lookupIp(ip);

  // And finally the weather ⛈
  const weather = await 
  fetch(`http://api.openweathermap.org/data/2.5/forecast/? q=${_city},${_countryCode}&APPID=${process.env.OPEN_WEATHER_TOKEN}&units=metric`);

  return {
    statusCode: 200,
    body: JSON.stringify(weather)
  }
}

then on the client I could just make one http call to get the final weather response:

App.js

const getWeather = () => {
  fetch('/.netlify/functions/get-weather')
    .then(x => x.json())
    .then(res => setWeather(res));
}

The best part is that the endpoint call is the same in your development environment, staging and production. 🎊

⚙️ Setting up timers with web workers

Since Open Weather Map API gives the weather in spans of 3 hours, I wanted the UI to update whenever new data becomes available. By using these 3 hour spans I could set up JavaScript timeouts to handle these updates.

Also, I added a dark mode that appears whenever is sunset time. Since these two timers make two parallel timeouts running on the client, I wanted to have each timer running in a separate Web Worker to ease the load on the browser's thread.

For this, I set up a worker that calculates the amount of milliseconds until the next weather report and sends a message to the app to update the UI. The second one handles the times related to sunrise and sunset.

App.js

  useEffect(() => {
    if (weather.cod === '200') {
      const timeWorker = new Worker('../../static/update-time-worker.js');
      timeWorker.postMessage(`${weather.list[0].dt}`);

      timeWorker.onmessage = ({ data }) => {
        getWeather();
      }
    }
  }, [weather]);
update-time-worker.js

onmessage = ({ data }) => {
  const end = new Date(data * 1000);
  const start = new Date();

  const timeDiff = Math.floor((end.getTime() - start.getTime()));

  setTimeout(() => {
    postMessage('Update weather');
  }, timeDiff);
};

🎨 The CSS

To make sure fonts would scale from the size and resolution of my laptop screen and the size of the (tiny) raspberry touchscreen, I used font sizes varying depending on the screen height:

styles.css

:root {
  --font-size-l: 6vh;
  --font-size-m: 5vh;
  --font-size-sm: 3vh;
}

Changing the screen size on the browser gives this result:

Alt Text

🍒 The Raspberry part

Alt Text

Before anything, I installed Raspbian because it includes a pre-installed Chromium version. You can boot this browser in full screen and without the address bar, making it look like a native app. In March the Raspberry Pi Imager was released and it makes it super easy to install: https://www.raspberrypi.org/downloads/

Alt Text

Now, I want to use my little touchscreen and for this I need a special script to change the boot screen:

git clone https://github.com/goodtft/LCD-show
cd LCD-show
sudo ./LCD35-show

Alt Text

All set for the last step! We will just show Chromium without the navigation bar and on fullscreen:

/usr/bin/chromium-browser --incognito --start-maximized --kiosk https://weather-preactpi.netlify.com/

🔥💥 Boom 💥🔥

Alt Text

📚 Resources

https://ipinfo.io/
https://openweathermap.org/
https://docs.netlify.com/functions/build-with-javascript/#format
http://frenetic.be/tricks/simple-timer.php
https://www.youtube.com/watch?v=Fj3wq98pd20
https://blog.gordonturner.com/2017/07/22/raspberry-pi-full-screen-browser-raspbian-july-2017/

Latest comments (5)

Collapse
 
thomasbnt profile image
Thomas Bnt ☕

Very great post! I'll also see him in #Raspberry tag !

Collapse
 
moigonz profile image
moi gonzalez (he/him)

Thanks for the tip! Will add it now :D

Collapse
 
graciegregory profile image
Gracie Gregory (she/her)

This is awesome. I really need to start tinkering around with Pis already! Side-note: I'm based out of Portland so I definitely need to try my hand at this project! Portland and Berlin must be antipodes of one another or close to it — totally feel you on the "every season in one day" thing!

Collapse
 
dennisk profile image
Dennis Keirsgieter

Nice! What's the screen and or are the specs of the screen?

Collapse
 
moigonz profile image
moi gonzalez (he/him)

Thanks! The screen is this one: amazon.de/gp/product/B06X191RX7/re...

Got mine years ago together with the Pi! Works really well :)