Skip to content

Blog

The python3-discogs Library

In June 2020 Discogs announced they were going to discontinue support for the Python library they maintained. Shortly after, a few members of the community picked up where Discogs left off and announced a community version of the library.

I remember Discogs’ announcement but I forgot about the community. I had starred their repository at some point but randomly came across it again late last week.

I started to refactor my project on Friday to use the API to see if it would be easier than using requests to parse all the JSON. And it was. In one method I think I went from about 40 lines of code to 5. Yay APIs!

I didn’t understand the API thoroughly and got stuck on a few parts. The documentation is pretty good and has examples using the Python REPL but I still had a couple problems trying to put it together.

It turns out that the community members are very responsive and helpful to questions. I quickly received some pointers and help on the right way to do things and by the end of the weekend I not only had re-written the code to get a random album, but it was trivial to use the same code to get a random EP or single. In fact, I bet my code is way more Pythonic now as I’m only using one method for both.

This is another example of the beauty of open source. One group discontinues support but another is able to pick it up and keep it going.

With all the help I received, I wrote up a lot of the functions to use the API that would appear in a Python program and started a discussion and submitted a pull requestto update the documentation. The community has been welcoming and it’s the least I can do to give back.

Silver Saucer Discogs Integration

The progress continues in moving Silver Saucer to FastAPI. I've spent almost all of my free time learning FastAPI from the Talk Python course. It would probably have gone faster if I was directly following the training course, but of course I'm doing my own thing.

I'm trying to do two things at the same time. First, build this in FastAPI, but use my own content, including CSS, page structure, and integrating the Discogs API instead of using a database. Second, I'm re-using some code (mostly the services that call the Discogs API) from the old Silver Saucer Pyramid site. I haven't touched that code in almost a year (for shame!) so I have to review that as I go.

Yesterday was a good day as I was able to wire up the Discogs integration using 90% of that old services code. It turns out when I create the class to manage the call, it's pretty specific as the first time I got it to work, I thought I was pulling in the record title, but instead it was the image URL, which I quickly converted to an image since I had something working:

Discogs returning an album image of Liz Phair's Exile in Guyville

From there, I was quickly able to call the rest of the data I wanted from the API and display it on the page. My memory of how the release date works must be off or requires more research. I thought I could pull both the release date of the specific album. For example, if it's re-issued like Garbage's BeautifulGarbage was last month, it would show a November 2021 date. But there was a main release date that should show 2001, but I can't seem to find it in the API. Oh well.

I was also able to update the HTML for the Genres and display that better than I did on the last site, parsing the list and showing it as sub-bullets. Another obligatory screenshot of everything working when clicking a random album:

Discogs returning an album image of Japandroids last album

I need to start coding the 7" / EP random function and then start on manually choosing a record. I have a couple questions on the training and thankfully Talk Python offers office hours, which are today!

Lots to do and I'm having fun doing it.

SilverSaucer and FastAPI Progress Update

I’ve made significant progress this weekend in taking the Building Full Web Apps with Fast API training from Talk Python.

I’m enjoying the course and it’s cool to learn how async works in a web application. My site will be far less complex than the training and I’ll probably skip the database part, though I might want to wire that up later depending on how fast the Discogs API really is.

One of my favorite things about taking courses from Michael Kennedy and Talk Python is that the design patterns are consistent across courses. While my knowledge of Python is sill limited, I was able to pick this up quickly as I move from a pattern of controllers in Pyramid to doing most of the same work, just now in the viewmodel itself in FlasAPI. At one point I was pretty confused, because I had the knowledge of where we were going in the training, it just wasn’t there yet. As I stuck with it, it made more and more sense. (Nice work, Mr. Kennedy!)

Obligatory work in progress screenshot, with a new logo coming soon:

Silver Saucer homepage running on FastAPI

After I finish up coding the login piece, I’m going to start work on the Discogs integration. Hopefully it won’t be too hard to move that over as I did have it working in Pyramid. I spent way too much time yesterday working on Bootstrap and CSS to tweak the site. It was a bit frustrating as I really don’t know Bootstrap (or CSS for that matter). In the end it’s close to what I want, but I never was able to get horizontal lines working. I would never make it as a frontend developer.

I also had an aha moment for phase 2. In phase 2, when I select a record to play (or from random), I want it to display the album art on my LED matrix. I wasn’t sure how that was going to work if I did it on a public website. I was playing around with the idea of hosting it in the house on a Raspberry Pi for local access only, but doing the login chapter gave me an idea.

I’m going to disable the ability to register logins on the site and not link to the login page. I’ll be the only one who can login, and with the stored cookie, it will know it’s me. If it’s me, the site will send a MQTT message back to the MatrixPortal controlling the LED to display the art. I just finished setting up my MQTT Mosquitto broker, so hopefully I can get this to work. MQTT is new to me, but I know CircuitPython already integrates with it. But that’s why that is phase two. Let’s not put the cart before the house because I already have so many projects….

Adafruit Macropad Awesome List

Inspired by the CircuitPython Awesome List, I've created an Awesome List for the Adafruit Macropad.

I finally found some time to set mine up after I received mine this past summer in the Adafruit AdaBox subscription. I've 3D printed a couple of cases for it already and also programmed mine to work with the Onshape CAD software.

It's pretty cool and I was curious to see what others had programmed for macros for their applications and I figured why not create a list for everyone!

If you have any suggestions for what to add, please let me know or feel free to fork and send a pull request. Contributing guidelines are in the list and if you have any questions, you can find me on Twitter or on the Adafruit Discord (link in the list).

December 2021 Project Updates

Since my last blog post just over a month ago, I've been extremely productive. I have a few blog posts planned over the next week (no, really, I swear) sharing some updates on a number of projects I've been working on.

The 3D Printer I bought back in September has been keeping me very busy. Not just learning how to use it, but also constant tweaking and maintenance it needs. I bought a Creality Ender 3 v2 and as I like to joke about it, I have more time than money to spend on a 3D Printer. The Ender series is inexpensive, but it does take a lot of work to maintain it. I read once that you will learn as much about the hardware as you will about 3D printing if you buy an Ender series, and it's definitely true.

Learning CAD software has also been a time consuming process. Sure, there are thousands of pre-made plans you can print on your printer, but sometimes you need to customize something or build something just for you. In my case it's a new speaker stand for my AudioEngine P4s that integrates an Adafruit rp2040 Feather with Neopixel FeatherWing. Trying to figure out how to get the USB-C cable to work inside the stand was more complex than I thought it would be. As we speak, I'm printing - what I hope - is the final version.

The rp2040 Feather and NeoPixel FeatherWing is another project that I've spend considerable time on the last few months and more on that soon.

I also wrapped up version one of the Pi-Dial project last weekend. I made some tweaks to the code and it's working exactly as I want. If and when I get more time, I'm already thinking about version 2 which use Python's asyncio library. I could also pick up a Raspberry Pi Zero and make a version that uses that in a much smaller enclsoure. It may even be possible to embed that in a speaker stand as well, but the LCD would take up most of the front and I'd have to mount the rotary encoders on the side. I have enough projects though right now...

Last but not least, I've started working on Silver Saucer again. Looking at the blog, it's almost exactly a year since I spent significant time on it, which amused me. I just finished merging the change from Pyramid to FastAPI in and now I need to re-write the Discogs integration. I'll write more on that this week as well. I also have some cool things planned that will use an Adafruit MatrixPortal and LED matrices to display album art of what I'm listening to, but that's phase two.

More to come. Really!

Introducing pi-dial - Part 7 - Fixing the LCD

(Catch up on the pi-dial series of blog posts.)

In my last post, I teased about 3D printing the enclosure. That will come in the next blog post, I promise.

First, I wanted to talk about an issue I ran into with how text was displayed on the 16x2 LCD screen. Using the RPi_GPIO_i2c_LCD library when I changed the input, if the input had less characters than the new input, the extra characters were staying on the screen.

For example, if I started on the Xbox One input, I would see:

Input: XBoxOne

If I changed to the Vinyl input, I would see:

Input: Vinylne

Since XboxOne had 7 characters and Vinyl only had 5, the extra 2 characters were staying on the screen. If I used the clear() method, it caused the line to blink. When I stopped to think about it, it makes sense, as the method clears the space and redraws it. Since I’m constantly polling the receiver via the API to check the volume and input, it would redraw the screen based on the sleep call I make in the while loop at the end of the pidial-lcd.py file.

I really don’t want it to blink. It’s distracting when sitting on my desk.

A quick search later I came across the RPLCD library. They also have their docs on ReadtheDocs and after quickly scanning that, I used it as a replacement. I had to change just a few lines of code and voila! It’s clear command doesn’t blink and it actually clears the LCD before writing to it again and the extra characters were gone.

I’ve said it before and I’ll say it again: I love open source.

And make sure you thank an open source maintainer today.

Next up: 3D printing and assembly

pi-dial - Part 6 - When is a list not a list?

(Catch up on the pi-dial series of blog posts.)

In addition to controlling the volume of my receiver’s Zone 2, I want to be able to use a second rotary encoder to change the input. For example, I may want to change it from the Phono input that I use to listen to records to the Tuner input to listen to the radio.

The Denon AVR-3806 that I own has twelve total inputs, of which I’ve customized a few of the names. If I print out the list using the denonavr library, I see:

All inputs: ['8K', 'AUX', 'AppleTV', 'Bluetooth', 'HEOS Music', 'Phono', 'Retropie', 'SOURCE', 'TV Audio', 'Tuner', 'UBP-X700', 'Vinyl', 'Xbox One']

I’m using Python, so iterating through the list and / or slicing it to get to a specific position in the list shouldn’t be hard. Shouldn’t being the key word.

Spoiler: It was anything but.

If I attempted to iterate through the list, whenever the list position was equal to 8K, Bluetooth, or Source, the list would stop. It didn’t matter if I tried to move up or down the list. Now shame on me for not blogging about this earlier as it’s been almost two months since I worked on this particular piece, because I think there was another position in the list that did something similar. But of course I don’t have great notes or even a lot of commits to go back and look through.

I’m still very much a novice at coding, so I did what most newbies do. I wrote multiple if / else statements to try and figure out how to get around this limitation. I was 100% sure it was my code. Nothing I did worked, even as I wrote very procedural code to step through the list. I was on vacation the week I worked on this and spent literally the entire week trying to get this to work.

I finally broke down and asked my wife for help. In about five minutes she ripped up my if / else statements and wrote a methodthat passed a variable on whether the rotary encoder was turning up or down. And then she ran into the problem where the list would get stuck on positions 0, 3, and 7 (8k, Bluetooth, and Source) as you can see in the code’s comments. A couple hours later she was able to work around it. This was a fun way to spend a Friday night together!

Neither of us have any idea why those inputs caused the issue, but she was able to write a method to work around it.

The good news is that it wasn’t necessarily my code, the bad news is that I still have so much to learn about the best way to write my code.

The better news? The code is done! I have one program that monitors the current input and volume and displays it on the LCD and I have another program that can change the inputs and the volume.

Next up: 3D printing the enclosure.

Introducing pi-dial - Part 5 - A detour into async

(Catch up on the pi-dial series of blog posts.)

With the hardware prototyping done, it’s time to write the code that is at the hart of pi-dial.

I need the hardware to do four things, all in Zone 2 of the receiver: 1. Change the inputs (CD to Phono to Tuner, etc.) 2. Change the volume up or down 3. Mute and un-mute the receiver

I mentioned briefly in my first post that the denonavr open source project has the methods I’ll need for this. It is possible to directly interact with the receiver as it has all of the functionality built using custom HTTP calls. But it’s definitely not optimal, leading to projects like denonavr.

Late last year, the denonavr project was re-written to use Python’s async library. I know of async, but I don’t know how to use async.

I found the documentation for using async with denonavr kind of sparse. I opened an issue in the project’s Github repo and received some pointers to get started.

After that, it was time to dive into async itself. I purchased the Everything Bundle from Talk Python Training earlier this year which included the Async Techniques and Examples course.

I kind of understood it? But not enough that I am going to use it (right now). I’m still very much a novice at coding in Python and I’m just trying to build something between a prototype and minimum viable product. This was not a rabbit hole I wanted to go down right now, but I might in the future.

I did take some time to update the project’s documentation with more async examples to make it easier for the next person. Those changes were merged in a couple weeks ago, which is nice.

Thankfully, the denonavr project still includes legacy support, which was pretty easy to get up and running, with the exception of one thing, which I’ll cover next time.

A brief aside: I feel bad for users who ask a question and don’t get a response in open source projects. Even worse, in my opinion, is adding a bot that will automatically close those questions or issues after a period of time without a response. Having been involved with a couple of open source projects, I understand how frustrating it can be for users to ask the same questions over and over or have a repository filled with “issues” that really aren’t issues. But then write some documentation to help those users! Don’t ignore them and then automatically close the issue.

Next up: Finally writing the Python code to power pi-dial.

Progress Update

It’s been a little bit since I posted an update about the pi-dial project. Don’t worry, I have some updates to share soon.

In typical fashion, I’ve been distracted by two new projects. That, and I’ve been putting off blogging. I probably need to do a better job keeping notes of the updates I want to write about because I’ve been doing it all from memory. And the longer I go between working on the project and writing about it, the less I remember.

The new projects? I have a CircuitPython project to share soon and I bought a 3D printer last Wednesday. The printer has taken up most of my time in the last few days and I’ll share more about that, too.

Ender 3 v2 3D Printer

Introducing pi-dial - Part Four - Adding an LCD

Previously: Part 1 Part 2 Part 3

While it may sound like I’ve been on a tear creating the pi-dial, the truth is somewhere in-between. While I’ve done a lot in the last month, there have been enough times where I’ve wandered down a rabbit hole doing research and getting distracted.

One of those times was thinking about how I was going to mount this contraption. One idea I had was to build a custom wooden box / case to mount the Pi in. The rotary encoder is panel mountable, so I thought I could drill a hole in the wood. I don’t have any wood working skills to really speak of, so I looked at this as another learning opportunity. (Like I need another learning project!)

I spent a lot of time on the MakerBot Thingiverse website browsing plans for 3D printers. I found a few plans that include a case for the Raspberry Pi and a hole the rotary encoder, but the plans were almost ten years old and for the original Raspberry Pi version 1 which hasn’t been around forever and all I have is a bunch of Raspberry Pi version 2s.

But then I found this plan that reminds me of an old car stereo. It would sit nicely on my desk:

Raspberry Pi Brackets and Enclosure

If you click through, I’m not sure I’ll use the power step down converter for the back plate, but it might be a nice project as well. That’s a problem for future Paul to deal with. I sent the plans to my friend with the 3D Printer and I’ll have to ask him if it’s possible to make the knob on the right equal to the one on the left, which is the rotary encoder (so I can use two of them).

Now I need to LCD to match the design. Another few hours of reading and research and this doesn’t look as hard as I thought it might be.

I looked at the Adafruit Pi Plate, which includes a 16x 2 screen (16 characters per row, 2 rows). It also has five buttons, and I don’t need that many. I did find some interesting 3D printer cases for it, but I’d rather use a rotary encoder to control the volume than buttons.

Looking at some of the tutorials on-line, I went with a cheap two-pack from Amazon. If you look at the photo below, you can see the LCD has 16 pins across above the LCD, but it also has an I2C interface on the left side where you can see the 4 pins coming out of it.

Working 16x2 LCD on a Raspberry Pi

The great thing about it using I2C - inter-integrated circuit - is that it makes using peripherals like this LCD a snap. Out of the 40 pins on a Raspberry Pi, only 4 are I2C, so you have to plan ahead on how you’re going to connect things to the Pi when using multiple peripherals, like 2 rotary encoders and an LCD. The rotary encoders were easy to hook up just using the jumper cables, so I didn’t have a concern.

My LCD came with the default address, 0x27, and I had it working within an hour using the RPi_GPIO_i2c_LCD library.

In less than 50 lines of Python, I was able to create a while loop using the denonavr library. Every 5 seconds in my prototype code it polls the receiver asking it for the current volume and current input and displays on the screen above.

Voila! This was probably the easiest part of the project so far.

Next up: A detour with Python’s async