Ermir Beqiraj
Backend architect. Systems, agents, infrastructure — from inside the work.
all writing

Nowadays we hear a lot about microcomputers and IoT, they are everywhere. You see smart home projects, smart TV, smart fridge, smart calendar? This is what I will discuss in this article. How to build a smart calendar with Raspberry Pi and .NET Core. The product will be a web app, which starts in full screen and shows a monthly calendar, Google Calendar events, holidays and birthdays (you can’t miss those), weather and images from a Google Drive folder. We will start with the hardware components and the source code necessary for this implementation which you can find in GitHub.

What I’ve used for this project

  • Raspberry Pi 3 (model B+)
  • 2 Heatsinks
  • CPU fan
  • Case
  • 16 GB (8GB is fine) micro SD memory card
  • Monitor with HDMI input and HDMI cable
  • SSH & SFTP client (e.g. PuTTY & Bitvise SSH)
  • Web app and console apps to run on the Raspberry Pi
  • Google developer account, Drive and Google Photos

Follow github.com/ermirbeqiraj/digital-calendar to download the source code. Here I’ve composed 2 console apps that will download photos from Google Drive and Google Calendar respectively on a schedule, and a web app that will run fullscreen once the Raspberry Pi boots up.

Prepare the OS

Open raspberrypi.org/downloads and click Noobs, scroll to the downloads section, pick “NOOBS Lite — Network install only”. This is the lightest version at this stage.

Access your SD card from your main PC. From the Raspberry Pi docs, it is suggested to use SD Card Formatter to prepare your SD card.

Extract the NOOBS archive and paste the files in the newly formatted SD card.

Assemble physical parts

Remove the small cover under heat sinks and stick it to the CPU and Ethernet card.

  • Insert the micro SD card
  • Assemble the case (optional)
  • Assemble the fan (if applies)
  • Connect your keyboard, mouse, HDMI & Ethernet cable
  • Connect the power and wait for the wizard window to select your network

The installation process is straightforward — follow all the steps. I chose the full Raspbian OS; the light version will also work. Once finished, your Pi is ready.

Software components

The solution for this topic consists of 3 projects: a web app and two console apps — one to retrieve photos, and the other to get calendar events.

For displaying pictures, I use Google Drive: save photos to a specific folder in Drive and the console app downloads them daily.

For calendar events, another console app reads the latest events and holidays on a cron schedule.

  • Visit Google Developers Console and create a project
  • Click Enable APIs and services and enable Google Drive API and Google Calendar API
  • Head to the credentials section
  • Click Create credentialsOAuth client ID
  • Download the credentials file and save it in both GetImages and GetCalendarEvents projects, right-click each and choose “Copy to output directory”
  • I named it dd-client-id.json (demo drive client). If you choose a different name, update the CLIENT_ID_FILE_NAME constant.

Open your selected directory in Google Drive and note the folder ID from the URL. Update GetImages to use your folder ID:

const string DRIVE_FOLDER_ID = "1mF9vGUoGpaHLG_jnP_c2HKyL6o_8HqhZ";

Then open GetCalendarEvents and update the calendars array:

static readonly string[] CALENDARS = new string[] {
    "primary",
    "en.al#holiday@group.v.calendar.google.com",
    "#contacts@group.v.calendar.google.com"
};

Prepare Raspbian to execute .NET Core apps

According to the Raspberry Pi .NET Core instructions, some prerequisite packages are needed first. Packages may vary by Raspbian version. To check your version:

sudo cat /etc/os-release
sudo apt-get install curl libunwind8 gettext apt-transport-https

Create the following directories:

  • /var/netcore/web
  • /var/netcore/console/calendar
  • /var/netcore/console/images

Publish all 3 projects for ARM architecture:

dotnet publish -c Release -r linux-arm

Move the contents of each publish directory to their respective folders on the Raspberry Pi, then mark each main file as executable:

cd /var/netcore/web
chmod 775 ./WebApp

cd /var/netcore/console/calendar
chmod 775 ./GetCalendarEvents

cd /var/netcore/console/images
chmod 775 ./GetImages

Calendar and Images first run

When opening the calendar and images apps for the first time, make sure you are connected to the Pi with a desktop environment or VNC Viewer. Run each as an executable: ./GetCalendarEvents and ./GetImages. Log in to your Google account and approve permissions when asked. This is a one-time action — each app will create a token.json folder in its base directory. They also need write permissions inside /var/netcore/web/wwwroot/calendar and /var/netcore/web/wwwroot/images.

Web app daemon

First, test the web app in a terminal: run ./WebApp in the web app directory and check the output. Next, create a systemd service:

cd /etc/systemd/system
sudo nano webapp.service

Paste the following:

[Unit]
Description=webapp
After=nginx.service

[Service]
Type=simple
User=pi
WorkingDirectory=/var/netcore/web
ExecStart=/var/netcore/web/WebApp
Restart=always

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl enable webapp.service
sudo systemctl start webapp.service

Installing Nginx

Download the signing key and add it to apt:

sudo wget https://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key

Edit /etc/apt/sources.list:

sudo nano /etc/apt/sources.list

Add these lines (replace stretch with your version from /etc/os-release):

deb https://nginx.org/packages/mainline/debian/ stretch nginx
deb-src https://nginx.org/packages/mainline/debian/ stretch nginx

Install nginx:

sudo apt-get update
sudo apt-get install nginx

Verify it’s running: sudo systemctl status nginx. You should see Active: active (running).

Edit the default site configuration to proxy to the web app:

sudo nano /etc/nginx/sites-available/default

Replace the contents with:

server {
    listen        80 default_server;
    server_name _;
    location / {
        proxy_pass         http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $http_host;
        proxy_cache_bypass $http_upgrade;
    }
}

Restart nginx: sudo systemctl restart nginx.service

Schedule image and calendar retrieval

Run crontab -e and register 2 jobs:

0 4 * * * /var/netcore/console/calendar/GetCalendarEvents
0 */6 * * * /var/netcore/console/images/GetImages

These will run on schedule without further interaction, since you authenticated manually on first run.

Startup in kiosk mode

To launch Chromium in fullscreen when the Pi boots:

sudo nano ~/.config/lxsession/LXDE-pi/autostart

Add:

@xset s off
@xset -dpms
@xset s noblank
@chromium-browser --noerrdialogs --incognito --kiosk http://<YOUR-RASPBERRY-IP>

What’s missing

  • Ability to authenticate with service account type in Google Calendar and Google Drive. Currently using OAuth2InstalledApp which is the closest but still requires a manual first-run step.
  • Temperature and humidity sensor support.

You can find the source code for the web app and 2 console apps at github.com/ermirbeqiraj/digital-calendar

Ermir Beqiraj is a backend architect building AI-integrated infrastructure. This is his personal writing.