It’s C# Advent time and I’ve been looking for a project to explore the .NET fringe. After at least five minutes of deliberation, I decided it would be a great idea to try to concoct a Raspberry Pi Magic Mirror guide, by building a Magic Mirror using Blazor Web Assembly and a Raspberry Pi running Raspbian. Commence project BlazorMirror and here is a sneak peek at what we are building:

Raspberry pi magic mirror guide
BlazorMirror complete!

First of all, if you’ve never heard of a Magic Mirror, it’s an internet phenomenon where techies and the DIY-inclined take a two-way mirror, build a case for it, and throw a monitor behind it to project relevant information, like the weather and time, with a fancy look. There is even a highly configurable Raspberry Pi Magic Mirror platform built with node.js called MagicMirror2. However, I wanted a bit more learning and adventure in my life so we’re going to build our own version in Blazor WebAssembly.

Blazor WebAssembly and .NET on Linux are a couple of the newest features available in the Microsoft ecosystem. Having no experience with either of them, I decided to take on a fun project to gain a little experience with both, see how easy they are to work with, and take you, readers, along for the ride.

Why Blazor WASM?

So, what makes me interested in Blazor WebAssembly? Microsoft touts Blazor WebAssembly (Blazor WASM, for short) as an opportunity to replace JavaScript on the front end. If that’s the case, it could provide opportunities to more tightly integrate your front-end and back-end validation, simplify communication to the back end, and maintain a common language for all of the code. In a typical heavy front-end application, this can’t be done because JavaScript is required to perform complex front-end operations. It also would allow you to reference non-C# WASM libraries, eventually, because other languages will also compile to WebAssembly, and WebAssembly is WebAssembly no matter where it started. I haven’t heard anything yet about easy cross-language support, but it will be possible.

Now, why would I want to run .NET on Linux? Well Raspberry Pi runs flavors of Linux by default, which means I can write code in .NET for my Raspberry Pi! I always want to tinker with my Raspberry Pi and the efforts would be much more fruitful in a language I use regularly. You could set up Windows IoT on Raspberry Pi but it’s a bit more limiting and there isn’t a lot of support out there for it. So if you, like me, think building a magic mirror in Raspberry Pi sounds like a fun project, here’s a Raspberry Pi Magic Mirror guide that follows my work. But before you get started, there are a few supplies you will need:

BlazorMirror supply list

  • Visual Studio (VS) or VS Code, updated to the latest version  (I used Visual Studio)
  • .NET 5
  • A monitor that supports HDMI
  • HDMI cable
  • RaspberryPi  (I used a RaspberryPi 3B.)
  • SD card for RaspberryPi
  • USB keyboard and mouse
  • Two-way mirror (You can use acrylic or glass. Glass will be more expensive but will prevent any odd reflections.)
  • Your BlazorMirror frame. (There are a lot of tutorials for this on YouTube.)
  • Multiport extension cord (This is what you will plug your monitor and Raspberry Pi into.)

The BlazorMirror development process

Let’s get started. First, we will address the creation of the basic Blazor WASM app. We accomplish this by first installing .NET 5. Blazor WASM is available in .NET Core 3.2+, but with .NET 5 you get long-term support, completely bypass .NET standard, and get the latest performance improvements.

To create a basic application open VS, choose “Blazor App” and click Next.

raspberry pi magic mirror guide process

Now, fill in the naming and location information like you would for any other less exciting application. Now choose your Blazor-specific settings. We won’t need much since it’s a locally hosted application on an internal network, but don’t forget to switch to .NET 5.0!

raspberry pi magic mirror guide process

Now we want to throw it into a git repository to track our progress. I used the default VS .gitignore on github and it is working great.

Fantastic! The basis of our future BlazorMirror empire is established, so let’s move onto some functionality.

We have three goals for our monitor display:

  • Pull and display weather data from an API
  • Show the current date and time
  • View your calendar events

Nothing overly complicated, but something that wouldn’t work as basic Razor Pages without JavaScript. First, though, we need to clean up the random stuff MS decided to add to the template.

Inside the Shared folder remove NavMenu.razor, NavMenu.razor.css, SurveyPrompt.razor, and replace the contents of MainLayout.razor with:

@inherits LayoutComponentBase

<div class="page">
    <div class="content px-4">
        @Body
    </div>
</div>

In the Page folder delete Counter.razor, FetchData.razor, and replace the contents of Index.razor with:

@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

Last, go into wwwroot and delete the sample-data folder. Your commit should look something like this:

raspberry pi magic mirror guide process

Here’s the commit: https://github.com/m4tt1mus/BlazorMirror/commit/5b04eefa15d7eda5844c6c473cf7ea805dd391af

Now that we’ve cleaned up, let’s dive into the set up. I’m going to add a Feature folder and an appsettings file (that won’t be committed). I like feature folders because I can keep all of my logic together in one place and the appsettings will be used to hold keys and other private information. This isn’t a high-security application where I feel something like KeyVault is necessary.

Conjuring up the date and time

For the programming portion, let’s start with something easy: the time and date. First, create a TemporalInfo folder in the features folder. This will house our new Blazor component, which you can add now. I named it the same as the folder. Once your Blazor component is added, in_Imports.razor, add @using BlazorMirror.Feature.TemporalInfo to the end of it. Once imported, you can reference your component using the component’s name inside <>s. For us, this means adding <TemporalInfo /> into Index.razor. Here’s the code:

@page "/"

<div>
    <TemporalInfo />
</div>

Great, the basic empty component is now set up but now it’s reality check time for me. I rarely write Razor. I usually work in front-end heavy apps with platforms like Angular or React to accomplish frequent data refreshes and leave my C#-ing to the back-end. Figuring out how to display a clock seems simple, but then you run into issues like the best way to continuously refresh the clock. To do this, I created a code tag, added a property to store the time, and used a Timer that triggers a function continuously after an elapsed time period. You’ll see after updating the time variable, I call StateHasChanged() to notify the component its state has changed.

@using System.Timers
@implements IDisposable


<div>
    <div class="time">@DateTimeNow.ToString("t")</div>
    <div class="date">@DateTimeNow.ToString("D")</div>
</div>

@code {
    private Timer _timer;
    public DateTime DateTimeNow = DateTime.Now;


    protected override async Task OnInitializedAsync()
    {
        _timer = new Timer();
        _timer.Interval = 500;
        _timer.Elapsed += UpdateTimeDisplay;
        _timer.AutoReset = true;
        _timer.Enabled = true;
    }

    private void UpdateTimeDisplay(Object source, ElapsedEventArgs e)
    {
        DateTimeNow = DateTime.Now;
        StateHasChanged();
    }

    public void Dispose()
    {
        _timer.Dispose();
    }

}

The commit is HERE

I switched to using IDisposable for the timers later. You have to implement it for the whole component, which is a bit different than normal. You can find that commit HERE

Casting the weather readout

Let’s move onto the weather. Our first consideration is where we get the weather data. I decided to go with AccuWeather. They have a free tier that will allow for hourly updates, which is adequate for me! The goal of this feature is to show the current weather as well as the five-day forecast. The fastest way to follow along with this one is to take a look at my commit. Some new things were introduced:

  1. Pulling data from an AccuWeather API. This is done in the AccuWeatherApi class. I added the ability to fake a data response during development to prevent us from exceeding our API usage. Pulling can be turned on and off via the appsettings.json
  2. I added a private key for the AccuWeather API in the appsettings.json. Since this isn’t committed to source control, we added an example of it to the README. Go sign up for the AccuWeather API for yours!
  3. I used the AccuWeather API website to query my Location Key with the geoposition search and put that in the appsettings.json also.
  4. @inject AccuWeatherApi AccuWeatherApi: This is how you can use DI inside a Blazor component! First you must register it in Program.cs though.
  5. I added Current Weather and Five Day weather and supporting classes. I use the same method as before to pull new data, but with a one-hour interval.
  6. After setting everything up, I realized I didn’t include a StateHasChanged() in the weather update methods and fixed that with this commit. I also added optional console logging of the responses.

Time went a little quickly when creating this, so I’ll be finishing the calendar. I would also like to refactor the HttpClient creation to use the IHttpClientControllerFactory. If you would like to follow my progress, watch my repo: https://github.com/m4tt1mus/BlazorMirror.

I didn’t cover any CSS styling options, but you can style to your own choosing. Obviously, my styling is available on the repo. I used the new CSS Isolation feature when doing so. One caveat is that you can only apply styles to elements specifically inside that component, so things like changing the body background-color have to go in the base app.css.

Raspberry Pi Magic Mirror setup

Setting up Raspbian on your RaspberryPi is something a ton of blogs cover, so I won’t be going over that here. This is a great resource to walk you through the steps: https://magpi.raspberrypi.org/articles/pi-sd-etcher.

This is where you’ll need your USB keyboard and mouse. Don’t forget to set up WiFi and VNC.

Setting up .NET on Raspbian was easier than I imagined. Here are the steps:

  1. VNC to your newly set up RaspberryPi
  2. Open a command prompt
  3. To pull down .NET 5 run the following:
    curl https://download.visualstudio.microsoft.com/download/pr/567a64a8-810b-4c3f-85e3-bc9f9e06311b/02664afe4f3992a4d558ed066d906745/dotnet-sdk-5.0.101-linux-arm.tar.gz -O NOTE: If you want a different version of dotnet-sdk, take a look at https://dotnet.microsoft.com/download/dotnet/5.0. Find the ARM32 version you want, click on it, then it should show you the direct download link. Replace the URL above with the new one and in the later commands use the updated version number.
  4. Make a home for dotnet:
    `mkdir -p $HOME/dotnet && tar zxf dotnet-sdk-5.0.101-linux-arm.tar.gz -C $HOME/dotnet`
  5. Add .NET to your path for easy use:
    `sudo nano ~/.bashrc`
    • Scroll to bottom of the file
    • Add
      • export DOTNET_ROOT=$HOME/dotnet
      • export PATH=$PATH:$HOME/dotnet
    • Save and close the file.
    • Close the terminal
    • Open the terminal and run dotnet
  1. Run dotnet --version. If it recognizes the command, it is installed correctly!

 

Raspberry Pi Magic Mirror guide setup

Well, that was easy. We’re almost ready to run BlazorMirror, but we need the code. If you click the Raspberry Pi icon-> Preferences => Add / Remove programs and search for git. Check git and install it. Once I had that setup, I issued the command: `git clone https://github.com/m4tt1mus/BlazorMirror.git to retrieve my code.`

Now, move into the new folder and create your appsettings.json file in wwwroot. In the root of the repo, run dotnet build and dotnet run. This will pull all your dependencies and then start up the application.

The last step is to open your browser and navigate to http://localhost:5000. The app is up and running! To finish it out, hit F11 and it will take up the whole screen.

Reflecting on the final product

Well, that was quite the journey. Following the advice of YouTubers Evan and Katelyn, I committed to the “Make. Fail. Make. Fail. Make.” motto (you can find it on their T-shirts) and succeeded in the end.

Blazor WASM is a bit challenging to get a handle on, but it’s mainly because you’re doing things in C# that you previously did in JavaScript. I also had a bit of trouble getting debugging to work consistently. I feel like Blazor WASM is at an ‘almost there’ point. Not quite ready for production, but would be a great asset in the future. The other major objective, running .NET and Web Assembly on Linux, was surprisingly easy. A few commands combined with knowing where to pull the SDK from and we were ready to go! I look forward to making some improvements to the existing code. If you would like to stick around and follow my progress feel free to watch the repo.

Let's Talk

Have a tech-oriented question? We'd love to talk.