Taking the weather with you with JS


Taking the weather with you with JS

A brief about how I built a small web app to read data from my weather station and animate it using CSS and SVG! You can check the app out on in my portfolio.

My dad had a mighty extensive music collection when I grew up. There were all kinds of vinyl, tapes, and CDs arranged across shelves and storage boxes, including the Beatles, The Troggs, The Kinks, REM, and so many more.

One of the CDs sitting amongst the hundreds was the album Woodface, from the Australian and New Zealand rock band Crowded House. A particular track on this album has followed me throughout my life titled "Weather with You". It's a catchy song you have probably heard as a backing track to a TV show at some point and most of the verses don't make a great deal of sense. But the chorus boldly states

Everywhere you go, always take the weather with you!

I remember recording this track onto a cassette to make a mix tape for my walkman on long car drives - I would certainly have found it hard to imagine that one day I would be thinking of this as inspiration for a Javascript project but here we are.

The back of a CD album for Crowded House, Woodface
Crowded House - Woodface.

Fast forward a couple of decades from my childhood and here I am - a web developer with an itch to build cool web apps that act as a learning exercise to improve my front-end skills. Like any artist though - I need an idea, some inspiration to focus my efforts in the world of CSS and JS.

Netatmo Weather Station

I was lucky enough to receive a Netatmo Weather StationIcon to represent this opens in a new tab as a bonus from work a year or two back. It is a collection of devices that one sets up in their garden to read live weather information including a thermometer, barometer, anemometer and rain gauge. Netatmo also provides an iOS app and a web app that renders the information onto your phones and other devices in a rich data format with charts and numbers all over the screen.

A set of Netatmo weather station modules mounted on a post
My Netatmo weather station.

Additionally, there is also a Netatmo API that allows you to query your weather station to retrieve live data from your devices. So I began setting up a way to query this following their documentation and setting up OAuth2 integration. At a basic level, this worked but it required the site to manage tokens and even then it only granted access to some weather data. For instance, I also wanted access to cloud cover, lux censors and sun angles for my location and that was not available through Netatmo. But they were available through other smart products already running in my home.

Home Assistant

Over the years I have become a bit of a smart home fanatic with my poison preference being Apple's Homekit. Whilst Matter is still in its infancy one of the major drawbacks with all smart home providers is that not all devices work with all ecosystems. As such there are solutions to this problem such as Home AssistantIcon to represent this opens in a new tab that act as a bridge between all devices and your smart home controller.

A home assistant web dashboard containing settings for various smart home devices
My Home Assistant dashboard.

A colleague showed me how to set up a tunnel to assign your Home Assistant bridge to a public subdomain. This would make it accessible outside of your home network without needing to cumbersomely open firewall ports. After setting up some basic WAF rules and auth headers, I made my Home Assistant API queryable. The system requires auth and is geo-limited to minimise the risk of malicious actors.

Querying the data

So now it was time to query the data from Home Assistant and make my end-point that can safely be queried client side. This new endpoint would accept no parameters and cherrypicks only the data needed to render UI ignoring everything else. Next App router makes this very easy serving a public end-point that on the server side calls the HA end-point with auth making the HA address invisible to the end-user.

The data is then formatted nicely in JSON to make it easily consumable by the app.

Enter Storybook.js

To build a visual display of the current weather data I needed a decent way to be able to test the various weather conditions the data from the station could portray. StorybookIcon to represent this opens in a new tab has been my home for all front-end design and construction for the past few years. It is a powerful tool used to render and test front-end components in a project. It is exceptionally useful for allowing you to set props on components inside its GUI so you can test in real-time without making code changes.

My website already had a Storybook design systemIcon to represent this opens in a new tab so adding another component to it was a trivial exercise.

A green cloudy sky with my AM logo and the title Alasdair Macrae Design
My Storybook design system.

I created a type for the props the component would take and began setting up the DOM via styled-components.

To make the interface exciting but easy to adjust I decided to draw my own house using an SVG. After some toying around with the viewBox and coordinates I settled on a nice isometric viewpoint and began drawing walls, roofs and gardens in flat colours. The output was a basic-looking house resembling some of my childhood MS Paint drawings.

So obviously I needed to add texture to my surfaces and other elements that would change based on weather conditions. So first up I googled CSS brick gradients and saw that a repeating linear gradient could do that. The problem? It was not so easy to use in SVG elements. I hacked away for a while but to no avail. So I did what any great developer does and threw a question up onto Stack Overflow. Almost immediately I had a perfect answer to use an SVG pattern rather than gradient. And just like that my house looked a little more real.

<pattern id="brickPattern" width="54" height="48" patternUnits="userSpaceOnUse">
   <rect width="100%" height="100" class="mortar" />
   <rect width="50" height="20" x="0" y="0" fill="#b00" />
   <rect width="25" height="20" x="0" y="24" fill="#b00" />
   <rect width="25" height="20" x="29" y="24" fill="#b00" />
</pattern>

An SVG pattern used to render bricks

Next up I set about drawing some trees in CSS that were wrapped in a component and accepted props to adjust the way they leaned based on wind speed and direction. It then placed a few around the house and put a sun and moon in the sky. Finally, I took some of my CSS clouds and added them to the sky and there I had it a basic scene.

I naturally wanted to make the sky a little more interesting. So I retrieved the sunrise and sunset times from Home Assistant, and then I needed cloud cover. Netatmo does not provide this very well, but Tado does (I am not entirely sure why a smart heating system would do this, but it does). I updated my API to provide this and then I needed a cloud. I could use an image but I had already built a flight over cloud pov animation in CSSIcon to represent this opens in a new tab so there was no way I was going to resort to a PNG file in this SVG graphic masterpiece.

A sky and horizon with CSS rendered clouds
The POV Flight component.

As the design system follows atomic design principlesIcon to represent this opens in a new tab it was easy to import the cloud used in the flight component into my weather station component. With that in mind, I could also use my rain component to animate the sky when rainfall was detected, and thus I imported that too.

A red house rendered using an SVG and displayed inside Storybook.js
Weather Station component rendered inside Storybook.js.

Now the component was rendered in StorybookIcon to represent this opens in a new tab I could start testing the visual display without having to mock data, The Storybook controls allow me to set values for testing components and I could quickly see what impact changing various props would have.

Bringing it all together

The only part left to do was create a page in the Next App router site and hook the APIs up to the component. I created a page quickly in the Next App Router and set up a container component responsible for handling the state and calling the new end-points. This container then passes the returned data into the weather station display component, and it all comes to life. You can see it now at /portfolio/weather.

A web component rendering a house inside a picture frame that updates based on local weather conditions
The station in action.

This whole exercise was a learning curve on so many fronts. It was immensely beneficial to get exposure to Cloudflare configuration, Home Assistant setup and Next API handling. Additionally, learning how to animate SVGs and clouds with CSS was difficult but rewarding.

Everywhere I go - I will always take the weather with me.