Spotify Desk Widget with a Raspberry Pi and e-Paper Display
Tags: raspberrypi, epaper, python, spotify
Published: 2023-05-06 15:16:37

I’ve been looking for a fun coding project to work on my free time so I remembered I had a 2.7 Inch e-Paper display laying around and decided to finally put it to use. As someone who listens to music on Spotify all day, I found myself constantly opening the app to check the current title and artist of the songs I liked. This is when I had the [very original idea](https://www.google.com/search?q=spotify+epaper+display&sxsrf=APwXEdfKv98UvMXWZL3mYAW3dFRgeGNFLQ:1683385295381&source=lnms&tbm=isch&sa=X&ved=2ahUKEwiJlLT0-uD-AhWGkYkEHU9hBg0Q_AUoAnoECAEQBA&biw=1440&bih=739&dpr=2) of making a Spotify e-Paper desk widget thing.

To build this project I used a local Home Assistant that I have running in another Pi. The automation gets triggered whenever a new song is played, which then fires a webhook to my Raspberry Pi 4. The webhook executes a Python script that retrieves the current song information from Home Assistant's API then using Python PIL library we can create the template with the album cover and finally we render it on the e-Paper display.

Here’s a overall general guide on how you can build this:

- Set up a Raspberry Pi with the Waveshare e-Paper display.
- Install and configure a local [Home Assistant](https://www.home-assistant.io/) instance.
- Create a Spotify developer account and register a new app to obtain the necessary API keys.
- Configure the Spotify integration in your Home Assistant.
- Create a Home Assistant automation script that triggers a RESTful command whenever a new song is played on Spotify.

Register RESTful command as a service in Home Assistant's configuration file:

```yaml
rest_command:
 spotify_epaper:
 url: "http://YOURLOCAL_RASPBERRYPI/hooks/spotify-e-paper-webhook"
 verify_ssl: false
 method: GET
```

The Home Assistant automation script is very simple:

```yaml
alias: Spotify ePaper Automation
description: ""
trigger:
 — platform: state
 entity_id:
 — media_player.spotify
 attribute: media_title
condition: []
action:
 — service: rest_command.spotify_epaper
 data: {}
mode: single
```

- I created the webhook endpoint on the Raspberry Pi using https://github.com/adnanh/webhook

- When the webhook is triggered a Python script is executed and this is where the magic happens.

This Python script initializes the e-paper display with `epd2in7.EPD()` ([epd2in7 is provided by Waveshare](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT_Manual#Introduction)). The fonts are loaded and a new image is created using `Image.new()`. The `ImageDraw.Draw()` method is used to create a rectangle and text.

The script then sends requests to the local Home Assistant instance using the requests library to retrieve weather and Spotify information. The data is then processed and pasted onto the image using `draw.text()` and `draw.multiline_text()`.

Finally, the image is displayed on the e-Paper with `epd.display()` and the display is put to sleep to save power with `epd.sleep()`.

```python
#!/usr/bin/python
# -*- coding:utf-8 -*-
import sys
import os
picdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'pic')
libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib')
if os.path.exists(libdir):
    sys.path.append(libdir)

import logging
from waveshare_epd import epd2in7
import time
from PIL import Image, ImageDraw, ImageFont
import traceback
import requests, json
from datetime import date

today = date.today()
logging.basicConfig(level=logging.DEBUG)

try:
    logging.info("Start...")
    
    epd = epd2in7.EPD()
    logging.info("Init and Clear")
    epd.init()
    epd.Clear(0xFF)

    font15 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 15)
    font20 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 20)
    font25 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 25)
    
    # Create the image
    logging.info("Create the image...")
    image = Image.new('1', (epd.height, epd.width), 255)
    draw = ImageDraw.Draw(image)
    draw.rectangle([(121,0),(270,119)],fill = 0)

    weather_url = "http://HOME_ASSISTANT_LOCAL:8123/api/states/weather.forecast_home"
    weather_headers = {
        "Authorization": "Bearer TOKEN",
        "content-type": "application/json",
    }

    weather_response = requests.get(weather_url, headers=weather_headers)

    if weather_response.status_code == 200:
        print('Connection to Weather successful.')
        # get data in json format
        data = weather_response.json()
        
        attributes = data['attributes']
        state = data['state']

        temperature = attributes['temperature']
        temperature_unit = attributes['temperature_unit']

        # Set strings to be printed to screen
        date = today.strftime("%A %d")
        draw.multiline_text((130, 60), str(temperature) + temperature_unit + "\n" + date, font=font25, fill= 1)
    
    spotify_url = "http://HOME_ASSISTANT_LOCAL:8123/api/states/media_player.spotify"
    spotify_headers = {
        "Authorization": "Bearer TOKEN",
        "content-type": "application/json",
    }

    spotify_response = requests.get(spotify_url, headers=spotify_headers)

    if spotify_response.status_code == 200:
        print('Connection to Spotify successful.')
        # get data in json format
        data = spotify_response.json()
        
        attributes = data['attributes']
        media_title = attributes['media_title']
        media_artist = attributes['media_artist']
        entity_picture = attributes['entity_picture']

        # get album cover and save it as png file
        img_url = 'http://HOME_ASSISTANT_LOCAL:8123' + entity_picture
        path = os.path.join(picdir, 'album_cover.png')
        response = requests.get(img_url)
        if response.status_code == 200:
            with open(path, 'wb') as f:
                f.write(response.content)

        # Open album image, resize and paste 
        album_cover = Image.open(os.path.join(picdir, 'album_cover.png'))
        album_cover.thumbnail((120, 120), reducing_gap=2.0)
        image.paste(album_cover, (0,0))  

        # Set strings to be printed to screen
        draw.text((5, 120), media_title, font = font25, fill = 0)
        draw.text((5, 150), media_artist, font = font15, fill = 0)


    epd.display(epd.getbuffer(image))
    
    logging.info("Go to sleep and save power...")
    epd.sleep()
        
except IOError as e:
    logging.info(e)
    
except KeyboardInterrupt:    
    logging.info("ctrl + c:")
    epd2in7.epdconfig.module_exit()
    exit()
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47owulc8a0pjg6xelk2t.png)

This project was a lot of fun and super easy to achieve. I love how I made it work with my already running Home Assistant setup, this will let me integrate the e-Paper display even further with other automations.

With just a few simple steps, I was able to create a cool geeky gadget to display on my desk. The combination of Home Assistant’s powerful integrations, the Raspberry Pi’s versatility, and Waveshare's e-Paper display made this project a breeze to build.

My next iteration will include:

- Send all the necessary data in the webhook payload itself to avoid all those requests in the Python script
- Resize and filter the album cover with better algorithms for black and white display
- Add upcoming work calendar events
- Utilize those fours button keys in the display
- Upload code to Github (I need to clean the code first and setup the appropriate env vars)

If you’re looking for a fun coding project to work on, I highly recommend giving this one a try!