loading...
Cover image for Show Dev: RaspberryPi Weather ⛈ fuelled by Netlify functions and Preact

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

moigonz profile image moi gonzalez (he/him) Updated on ・4 min read

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/

Posted on Mar 31 by:

moigonz profile

moi gonzalez (he/him)

@moigonz

Frontend Developer based in Berlin

Discussion

markdown guide
 

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!

 

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

 

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

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

 

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

 

Thanks for the tip! Will add it now :D