RaspberryPi 5, Debian 12 (bookworm), as DLNA renderer with output to a Teufel Cinebar One+ via USB, configured with Ansible

Table of Contents
Raspberry Pi 5 in a passively cooled KKSB case on top (for size comparison) of a Teufel Cinebar One

When I listen to music, the RasPi case is not resting on top of the speaker. It’s in a shelf above my main screen and the Cinebar is attached to the wall about halfway between the top of the screen and the bottom of the shelf.

Introduction

These are my notes on setting up:

Overview

Debian 11 will be EOL this coming July, so it was time to finally modernise my existing setup to Debian 12.

The aim is to use a RasPi, a small µSD card for booting plus a USB attached sound bar to play music using DLNA.

The music files themselves are hosted on one (or more) DLNA servers.

Controlling the music happens via one (or more) DLNA controllers.

This post is a pruned and edited copy of my older, Debian 11, post on this topic.

Pruned because I decided to do this round the KISS way, parts (HW and SW) I do not use will be disabled (HDMI audio disabled, Bluetoth disabled, WiFi disabled), this has the nice side effect of no longer needing to mess with alsa device indices because then USB audio is the sole audio output available and thus at the default index 0 that alsa.conf uses for defaults.ctl.card and defaults.pcm.card.

Additionally, because I upgraded to a Raspberry Pi 5, I no longer need proprietary scripts of questionable quality from Argon40 which in turn download more scripts, just to have a functioning power button.

The USB audio at index 1 fiddlings were dropped. A task was added to switch off Bluetooth and WiFi via dtoverlay using a model filter. A packet and a task was added to used the powersave cpufreq governor.

Oh and the Ansible tasks now use FQCN.

tl;dr

  1. optional: house RasPi 5 in a case
  2. connect Cinebar via USB to RasPi 5
  3. write ųSD card using rpi-imager with custom config set as needed (hostname, user, ssh key, …)
  4. disable HDMi audio
  5. optional: disable services that use Bluetooth
  6. optional: disable Bluetooth hardware via dtoverlay
  7. optional: disable WiFi hardware via dtoverlay
  8. optional: use powersaving CPU frequency scaling governor
  9. install gmrender-resurrect
  10. ensure gmrender-resurrect runs at boot
  11. use a DLAN control point for song selection, play, stop, etc
  12. press power button to trigger a clean shutdown
  13. optional: automatic clean shutdown N hours after powerup

Hardware Details

Chosen Hardware

The RasPi 5 and case were purchased new. My existing 15W PSU is sufficient for my planned power draw. The Cinebar has been serving me well for a while now, I reused the 32 GiB µSD card since I am decommisionning the RasPi 4B and the Argon case. The 27W PSU is overkill for my use case (no fan, Cinebar has own PSU).

While my RasPi 5 has 8 GiB RAM, the 4 GiB model and even only 2 GiB would be more than sufficient for this one trick pony use case.

The following CPU and memory usage was collected while it was playing a song;

pcfe@raspi5:~ $ uptime
 22:49:43 up  2:51,  1 user,  load average: 0.03, 0.05, 0.05
pcfe@raspi5:~ $ free -h
               total        used        free      shared  buff/cache   available
Mem:           7.9Gi       279Mi       7.1Gi       5.4Mi       557Mi       7.6Gi
Swap:           99Mi          0B        99Mi

RasPi to Cinebar Connection

I connected the Cinebar to the RasPi via USB.

The RasPi 5 no longer comes with the low quality 3.5mm audio jack previous models had (good riddance). Plus I disable audio over HDMI because I do not plan to use audio over HDMI on this RasPi and I want the HDMI input on the Cinebar available for other uses.

This leaves only usb-audio, at index 0, and I can use the shipped defaults of alsa.conf etc.

RasPi Enclosure

While I could use the naked RasPi, I wanted all of

  • a heatsink
  • no noisy fan
  • some dust protection

I looked at the options available from the place I decided to by my RasPi5 from and chose this passively cooled KKSB case.

closeup of Raspberry Pi 5 in a passively cooled KKSB case

Software Used

gmrender-resurrect is available on Raspberry Pi OS (64-bit) (Debian 12) with a simple apt install gmediarender.

Operating System Used

I did a fresh install of Raspberry Pi OS (64-bit), Debian version: 12 (bookworm).

Created the µSD card with the Raspberry Pi Imaging Utility (aka rpi-imager). It is well documentated.

With these selections:

  • Device: RaspberryPi 5
  • Operating System: RaspberryPi OS Lite (64-bit)
  • set hostname
  • set user and password
  • set locale
  • enable ssh
  • allow public-key authentication only
  • eject media when finished

NOTE: Lite simply because on this headless RasPi there is no point in wasting resources on running a desktop environment, so there is no point either in having it on the µSD card. If I want a package I can easily apt install or ansible.builtin.package:.

Not being in an active desktop session has the welcome side-effect of the Pi shutting down cleanly when I press the power button without me having to acknowledge in a window [that would] appear asking whether you want to shutdown, reboot, or logout.

Once the tool had finished writing to the SD card, I plugged it into the RaspberryPi 5 and booted from it.

Once it had booted up, I upgraded using APT

sudo apt update && sudo apt full-upgrade # click to see details
pcfe@raspi5:~ $ sudo apt update
Hit:1 http://deb.debian.org/debian bookworm InRelease
Get:2 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]
Get:3 http://deb.debian.org/debian bookworm-updates InRelease [55.4 kB]
Get:4 http://archive.raspberrypi.com/debian bookworm InRelease [23.6 kB]
Get:5 http://deb.debian.org/debian-security bookworm-security/main armhf Packages [150 kB]
Get:6 http://deb.debian.org/debian-security bookworm-security/main arm64 Packages [153 kB]
Get:7 http://deb.debian.org/debian-security bookworm-security/main Translation-en [92.9 kB]
Get:8 http://deb.debian.org/debian bookworm-updates/main armhf Packages [13.2 kB]
Get:9 http://deb.debian.org/debian bookworm-updates/main arm64 Packages [13.7 kB]
Get:10 http://deb.debian.org/debian bookworm-updates/main Translation-en [16.0 kB]
Get:11 http://archive.raspberrypi.com/debian bookworm/main armhf Packages [422 kB]
Get:12 http://archive.raspberrypi.com/debian bookworm/main arm64 Packages [413 kB]
Fetched 1,401 kB in 1s (1,652 kB/s)                        
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
42 packages can be upgraded. Run 'apt list --upgradable' to see them.
pcfe@raspi5:~ $ sudo apt full-upgrade
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Calculating upgrade... Done
The following NEW packages will be installed:
  linux-headers-6.6.28+rpt-common-rpi linux-headers-6.6.28+rpt-rpi-2712 linux-headers-6.6.28+rpt-rpi-v8
  linux-image-6.6.28+rpt-rpi-2712 linux-image-6.6.28+rpt-rpi-v8 linux-kbuild-6.6.28+rpt pastebinit
  python3-pycryptodome
The following packages will be upgraded:
  bsdextrautils bsdutils dpkg dpkg-dev eject fdisk less libblkid1 libc-bin libc-dev-bin libc-devtools
  libc-l10n libc6 libc6-dbg libc6-dev libcamera-ipa libcamera0.2 libdav1d6 libdpkg-perl libfdisk1
  libglib2.0-0 libmount1 libpisp-common libpisp1 libsmartcols1 libuuid1 linux-headers-rpi-2712
  linux-headers-rpi-v8 linux-image-rpi-2712 linux-image-rpi-v8 linux-libc-dev locales mount pi-bluetooth
  raspi-config raspi-firmware raspi-utils rfkill rpi-eeprom rpicam-apps-lite util-linux util-linux-extra
42 upgraded, 8 newly installed, 0 to remove and 0 not upgraded.
Need to get 109 MB of archives.
After this operation, 141 MB of additional disk space will be used.
Do you want to continue? [Y/n] 
[]
pcfe@raspi5:~ $ sudo reboot

Broadcast message from root@raspi5 on pts/1 (Sat 2024-05-18 14:52:53 CEST):

The system will reboot now!

pcfe@raspi5:~ $ Connection to raspi5.internal.pcfe.net closed by remote host.
Connection to raspi5.internal.pcfe.net closed.

Wait a bit for it to finish booting, and log in again.

user@workstation ~ $ ssh raspi5.internal.pcfe.net 
Linux raspi5 6.6.28+rpt-rpi-2712 #1 SMP PREEMPT Debian 1:6.6.28-1+rpt1 (2024-04-22) aarch64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat May 18 14:45:25 2024 from 192.168.50.59
pcfe@raspi5:~ $ uptime
 14:55:40 up 2 min,  1 user,  load average: 0.24, 0.19, 0.08
pcfe@raspi5:~ $ uname -r
6.6.28+rpt-rpi-2712
pcfe@raspi5:~ $ cat /etc/os-release 
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
pcfe@raspi5:~ $ 

And I ensured I was running the latest firmware with

sudo rpi-update # click to see details
pcfe@raspi5:~ $ sudo rpi-update 
 *** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom
 *** Performing self-update
 *** Relaunching after update
 *** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom
FW_REV:3b768c3f4d2b9a275fafdb53978f126d7ad72a1a
BOOTLOADER_REV:61fb89536fc94a57c1e0afd42617849b6d0cac37
 *** We're running for the first time
 *** Backing up files (this will take a few minutes)
 *** Backing up firmware
 *** Backing up modules 6.6.28+rpt-rpi-2712
WANT_32BIT:0 WANT_64BIT:1 WANT_PI4:1 WANT_PI5:1
##############################################################
WARNING: This update bumps to rpi-6.6.y linux tree
See: https://forums.raspberrypi.com/viewtopic.php?p=2191175

'rpi-update' should only be used if there is a specific
reason to do so - for example, a request by a Raspberry Pi
engineer or if you want to help the testing effort
and are comfortable with restoring if there are regressions.

DO NOT use 'rpi-update' as part of a regular update process.
##############################################################
Would you like to proceed? (y/N)
Downloading bootloader tools
Downloading bootloader images
 *** Downloading specific firmware revision (this will take a few minutes)
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  142M    0  142M    0     0  20.3M      0 --:--:--  0:00:07 --:--:-- 23.4M
*** PREPARING EEPROM UPDATES ***

BOOTLOADER: update available
   CURRENT: Mon 13 May 15:15:01 UTC 2024 (1715613301)
    LATEST: Fri 17 May 11:29:43 UTC 2024 (1715945383)
   RELEASE: latest (/lib/firmware/raspberrypi/bootloader-2712/latest)
            Use raspi-config to change the release.
   CURRENT: Mon 13 May 15:15:01 UTC 2024 (1715613301)
    UPDATE: Fri 17 May 11:29:43 UTC 2024 (1715945383)
    BOOTFS: /boot/firmware
'/tmp/tmp.tWeQTpPTrQ' -> '/boot/firmware/pieeprom.upd'

UPDATING bootloader. This could take up to a minute. Please wait

*** Do not disconnect the power until the update is complete ***

If a problem occurs then the Raspberry Pi Imager may be used to create
a bootloader rescue SD card image which restores the default bootloader image.

flashrom -p linux_spi:dev=/dev/spidev10.0,spispeed=16000 -w /boot/firmware/pieeprom.upd
UPDATE SUCCESSFUL
 *** Updating firmware
 *** Updating kernel modules
 *** depmod 6.6.30-v8+
 *** depmod 6.6.30-v8-16k+
 *** Updating VideoCore libraries
 *** Running ldconfig
 *** Storing current firmware revision
 *** Deleting downloaded files
 *** Syncing changes to disk
 *** If no errors appeared, your firmware was successfully updated to 3b768c3f4d2b9a275fafdb53978f126d7ad72a1a
 *** A reboot is needed to activate the new firmware
 pcfe@raspi5:~ $ sudo reboot

Broadcast message from root@raspi5 on pts/1 (Sat 2024-05-18 14:59:19 CEST):

The system will reboot now!

pcfe@raspi5:~ $ Connection to raspi5.internal.pcfe.net closed by remote host.
Connection to raspi5.internal.pcfe.net closed.

Configured Using Ansible

Back in 2021, I had cobbled together what I learned from

into an Ansible playbook. You will find an updated version further down below in this 2024 post.

Follow the above links if you are not comfortable with Ansible, it shows you how do the setup via bash. The reason I use Ansible for my setup is because a RasPi, especially the µSD card is cattle, not pets to me. So if something breaks, I want to be able to plonk a new one in, run a playbook that configures Raspbian and listen again to music in the home office room.

Inventory Entries

A couple inventory entries are needed for this playbook. Adjust to your username and RasPi hostname.

  1. My …/ansible-inventory-housenet/hosts is in INI format and has a line
raspi5.internal.pcfe.net
  1. Since this version of Raspberry Pi OS was installed without creating a user pi, but with creating a user pcfe. This user, created via OS Customization in rpi-imager, can sudo without password. My …/ansible-inventory-housenet/host_vars/raspi5.internal.pcfe.net/ansible_user.yml reads;
---
ansible_user: pcfe
  1. My …/ansible-inventory-housenet/host_vars/raspi5.internal.pcfe.net/gmediarenderer.yml reads;
---
gmediarender_logging: false

If I feel the need to debug, then I set gmediarender_logging: true and read /var/log/gmediarender/gmediarender.log. Because the log is chatty, once a problem is resolved, I set the boolean back to false and re-run the play to both disable logging and delete the log directory.

Template for Poweroff Timer, Systemd

My templates/timers/shutdown_12_hours_after_boot.timer.j2 reads;

[Unit]
Description=Poweroff 12 hours after boot

[Timer]
OnBootSec=12hours
Unit=systemd-poweroff.service

[Install]
WantedBy=timers.target

This powers down unconditionally 12 hours after powerup, adjust the number of OnBootSec=… to your needs.

The Ansible Playbook

This is an adjusted version of my earlier playbook, it’s shorter now as I got rid of the Argon case and no longer fiddle with /lib/modprobe.d/aliases.conf to force USB audio to a specific index (which was then followed by configuring alsa to use that chosen index).

The Playbook, called type__entertainment_raspi_dlna__prepare.yml, is meant to be self explanatory. Hence the many comment lines and the long task names. Both are intentional. code as docs and all that jazz.

Read it, adjust to your needs. Name it any name you want, I chose this rather long name because it is in a repository containing many other ansible plays.

For the steps marked optional: in the tl;dr section; what would need doing in a shell is hopefully obvious after reading both my playbook and the other sources (e,g, Edit some file and change foo to bar in bash is ansible.builtin.lineinfile: with regexp: foo and line: bar in ansible. Add a section [pi5] with these two lines … and before the section [all] in bash is ansible.builtin.blockinfile: with insertbefore: ^\[all\] in ansible. After you edited the file, reboot with sudo shutdown -r now in bash is notify: Handle rebooting and ansible.builtin.reboot: in ansible. You get the idea)

####################################################################################
#  author: Patrick C. F. Ernzer <pcfe@pcfe.net>
#  description: Configures a RasPi 5
#               that is running RaspberryPi OS Lite (Debian 12)
#               connected to my Teufel Cinebar One+
#               for DLNA music player usage
#  license: Creative Commons Attribution-ShareAlike 4.0 International License
#  min_ansible_version: 2.14
####################################################################################
#
# used hardware:
# - Teufel Cinebar One+
#   https://teufel.de/cinebar-one-105396000
# - Raspberry Pi 5
#   https://www.raspberrypi.com/products/raspberry-pi-5/
# - KKSB Raspberry Pi 5 Case with Aluminium Heatsink for Silent Passive Cooling
#   https://kksb-cases.com/products/kksb-raspberry-pi-5-case-with-aluminium-heatsink-for-silent-passive-cooling

---
- name: Ensure the Raspberry Pi is ready for DLNA renderer duties
  hosts: raspi5.internal.pcfe.net
  become: true

  handlers:
    - name: Handle restarting cpufrequtils.service
      ansible.builtin.service:
        name: cpufrequtils
        state: restarted
      changed_when: true

    # please note that the service takes some 30 seconds to shutdown or restart
    - name: Handle restarting gmediarenderer
      ansible.builtin.service:
        name: gmediarender
        state: restarted
      changed_when: true

    - name: Handle systemd daemon reload
      ansible.builtin.systemd:
        daemon_reload: true

    - name: Handle rebooting
      ansible.builtin.reboot:

  tasks:
    # ensure the needed packages are installed
    # I use ALSA simply because the author of gmediarender-resurrect uses ALSA
    # https://github.com/hzeller/gmrender-resurrect/blob/master/INSTALL.md
    # and gmrender-resurrect is the only thing I use this RasPi for.
    # cpufrequtils because I want the ability to reduce idle power consumption
    - name: Ensure wanted packages are present
      ansible.builtin.package:
        name:
          - libupnp-dev
          - libgstreamer1.0-dev
          - gstreamer1.0-plugins-base
          - gstreamer1.0-plugins-good
          - gstreamer1.0-plugins-bad
          - gstreamer1.0-plugins-ugly
          - gstreamer1.0-libav
          - gstreamer1.0-alsa
          - cpufrequtils
        state: present

    - name: Ensure HDMI audio is disabled
      ansible.builtin.lineinfile:
        group: root
        mode: u=rwx,g=rx,o=rx
        owner: root
        path: /boot/firmware/config.txt
        regexp: '^dtoverlay=vc4-kms-v3d'
        line: 'dtoverlay=vc4-kms-v3d,noaudio'

    - name: Ensure all updates are applied
      ansible.builtin.package:
        update_cache: true
        name: '*'
        state: latest  # noqa package-latest
      tags: apply_errata

    - name: Ensure gmrender-resurrect is installed, package name is gmediarender
      ansible.builtin.package:
        name:
          - gmediarender
        state: present

    - name: Ensure gmediarender has no ALSA_DEVICE set, it should use the gstreamer default
      ansible.builtin.lineinfile:
        path: /etc/default/gmediarender
        regexp: '^ALSA_DEVICE'
        state: absent
      notify: Handle restarting gmediarenderer

    # I find the default volume too high for comfort, so lower it
    - name: Ensure gmediarender has a more reasonable start volume of 40/100 than the default 75/100
      ansible.builtin.lineinfile:
        path: /etc/default/gmediarender
        regexp: '^INITIAL_VOLUME_DB='
        line: 'INITIAL_VOLUME_DB=-28'
      notify: Handle restarting gmediarenderer

    # For logging on/off, use the variable gmediarender_logging (bool)
    - name: If logging is enabled, then ensure directory /var/log/gmediarender exists
      ansible.builtin.file:
        path: /var/log/gmediarender
        state: directory
        owner: root
        group: audio
        mode: u=rwx,g=rwx,o=rx
      when: gmediarender_logging

    - name: Ensure gmediarender DOES have logging enabled
      ansible.builtin.lineinfile:
        path: /etc/default/gmediarender
        regexp: '^DAEMON_EXTRA_ARGS='
        insertafter: '^# DAEMON_EXTRA_ARGS='
        line: 'DAEMON_EXTRA_ARGS="--logfile /var/log/gmediarender/gmediarender.log"'
      notify: Handle restarting gmediarenderer
      when: gmediarender_logging

    - name: If logging is disabled, then ensure /var/log/gmediarender does not exists
      ansible.builtin.file:
        path: /var/log/gmediarender
        state: absent
      when: not gmediarender_logging

    - name: Ensure gmediarender does NOT have logging enabled
      ansible.builtin.lineinfile:
        path: /etc/default/gmediarender
        regexp: '^DAEMON_EXTRA_ARGS='
        insertafter: '^# DAEMON_EXTRA_ARGS='
        line: 'DAEMON_EXTRA_ARGS=""'
      notify: Handle restarting gmediarenderer
      when: not gmediarender_logging

    # as of 2024-05-18 I still get a SystemV init file from the
    # Debian 12 gmediarender package. Since Debian 12 has wrappers to allow
    # `systemctl […] gmediarender.service` commands to work just fine,
    # for now just use that /etc/init.d/gmediarender
    # even though stopping takes 30 secs,that's fine as systemd gives it
    # 5 mins to get sorted, if it takes longer it is killed.
    - name: Ensure gmediarender service is enabled and started
      ansible.builtin.service:
        name: gmediarender
        enabled: true
        state: started

    # Currently I have no use for Bluetooth on this DLNA renderer,
    # disable services that use it.
    - name: Ensure Bluetooth using services I do no intend use are disabled and stopped
      ansible.builtin.service:
        name: '{{ item }}'
        enabled: false
        state: stopped
      loop:
        - hciuart
        - bluetooth

    # disable onboard Bluetooth and WiFi hardware
    # using a model filter of [rp5]
    # c.f. https://www.raspberrypi.com/documentation/computers/config_txt.html#model-filters
    - name: Ensure WiFi and Bluetooth are disabled on RasPi 5
      ansible.builtin.blockinfile:
        dest: /boot/firmware/config.txt
        block: |
          [pi5]
          dtoverlay=disable-bt-pi5
          dtoverlay=disable-wifi-pi5          
        marker: "# {mark} Ansible Managed Block to disable BT and WiFi on RasPi5"
        insertbefore: ^\[all\]
        owner: root
        group: root
        mode: u=rwx,g=rx,o=rx
      notify: Handle rebooting

    - name: Check to see if we need a reboot, Debian style
      ansible.builtin.stat:
        path: /var/run/reboot-required
      register: reboot_required_file
      when: ansible_pkg_mgr == "apt"

    - name: Notify the handler for rebooting, if Debian style hint file was found
      ansible.builtin.debug:
        msg: Found reboot hint file
      when:
        - reboot_required_file is defined
        - reboot_required_file.stat.exists is defined
        - reboot_required_file.stat.exists
      changed_when: reboot_required_file.stat.exists
      notify: Handle rebooting

    # When I forget to shut down the RasPi manually,
    # it should power itself down 12 hours after bootup.
    - name: Ensure systemd-poweroff.timer for systemd is present
      ansible.builtin.template:
        src: timers/shutdown_12_hours_after_boot.timer.j2
        dest: /etc/systemd/system/systemd-poweroff.timer
        owner: root
        group: root
        mode: u=rw,g=r,o=r
      notify: Handle systemd daemon reload

    - name: Ensure timer to power off after 12 hours is both started and enabled
      ansible.builtin.systemd:
        name: systemd-poweroff.timer
        state: started
        enabled: true

    # c.f. https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#using-dvfs
    - name: Ensure cpufrequtils service is enabled and running
      ansible.builtin.service:
        name: cpufrequtils
        enabled: true
        state: started

    # on 2024-05-17 I still got an old style SysV init file with cpufrequtils, :sigh:
    # actively setting this task to fail if the file does not exist, so that I can notice
    # when cpufrequtils on Debian gets modernised to a modern unit file
    - name: Ensure old style /etc/init.d/cpufrequtils uses the powersave governor
      ansible.builtin.lineinfile:
        path: /etc/init.d/cpufrequtils
        regexp: '^GOVERNOR='
        line: 'GOVERNOR="powersave"'
        create: false
      notify: Handle restarting cpufrequtils.service

How to Run the Playbook

Option 1: with ansible-navigator

ansible-navigator run type__entertainment_raspi_dlna__prepare.yml

When having the relevant ssh private key loaded into the ssh-agent(1) of the session you run navighator from, then navigator will use that key to authenticate as ansible_user: … when connecting to the RasPi.

Please note, I have an ansible-navigator.yml pointing to the used inventory and setting a few other options I like to use.

Click to see my Ansible Navigator configuration file.

ansible-navigator.yml in the directory I run the the playbook from.

Do not copy blindly, edit to your needs, especially location of inventory. And maybe also the location of log files, making the host’s CA trust available to the execution environment, set how navigator will open a file when you type :o, etc.

I use AAP 2.4’s ee-minimal-rhel8 in this config file. You can use any Execution Environment (EE) Image you want (for example ghcr.io/ansible/creator-ee).

options: O in volume-mounts: alows the EE both to have your hosts’s ca-trust available and allows the EE to adjust it’s ca-trust but those changes in the EE will not end up on your host.

Create the directory navigator-output and add it to your .gitignore

---
# RTFM at https://ansible.readthedocs.io/projects/navigator/settings/
ansible-navigator:
  ansible:
    inventory:
      entries:
        - ../../../HouseNet/ansible-inventory-housenet/hosts
        - ../../../HouseNet/ansible-vault-housenet/empty-hosts-file-to-make-ansible-happy.ini
ansible-runner:
    artifact-dir: navigator-output/runner/
    rotate-artifacts-count: 10
  execution-environment:
    container-engine: podman
    enabled: true
    image: registry.redhat.io/ansible-automation-platform-24/ee-minimal-rhel8
    pull:
      policy: missing
    environment-variables:
      set:
        ANSIBLE_LOG_PATH: navigator-output/ansible.log
    volume-mounts:
      - src: /etc/pki/ca-trust
        dest: /etc/pki/ca-trust
        options: O
logging:
    append: false
    file: navigator-output/ansible-navigator.log
  playbook-artifact:
    enable: true
    save-as: navigator-output/{playbook_name}-artifact-{time_stamp}.json
  editor:
    command: codium -g {filename}:{line_number}
    console: false

Also note that I actually

ANSIBLE_VAULT_PASSWORD_FILE=.vault_password.sh ansible-navigator run …

simply because I have 2 inventories configured in the git repository the playbook lives in and one of them is a collection of vaulted files (none of which is needed for this one play though).

Click to see my .vault_password.sh script.

.vault_password.sh in the directory I run the the playbook from.

#!/bin/sh
#
# See section "Store the vault password in an environment variable" at
# https://ansible.readthedocs.io/projects/navigator/faq/#how-can-i-use-a-vault-password-with-ansible-navigator
#
# put your password in an env var, but not in your bash history
# obviously not only set history control, but also remember to actually use a leading space
# after that this scriptlet will echo your password and ansible tooling (-playbook, -vault,
# -navigator, etc) is happy to use this script as vault password source
# Set the environment variable to the location of the file when executing ansible-navigator
#
# use as follows
# user@host $ HISTCONTROL=ignorespace
# user@host $  export ANSIBLE_VAULT_PASSWORD=my_password
# user@host $ ANSIBLE_VAULT_PASSWORD_FILE=.vault_password.sh ansible-navigator run site.yml
echo ${ANSIBLE_VAULT_PASSWORD}

If you are not a bash user, then adjust the exclusion of the export ANSIBLE_VAULT_PASSWORD=my_password command from your shell’s history (zsh, fish or whichever your shell of choice is).

Option 2: with ansible-playbook

You can of course also run this old-school with ansible-playbook if you are not using ansible-navigator.

ansible-playbook \
  --inventory […some path…]/hosts \
  type__entertainment_raspi_dlna__prepare.yml

Playing Music via DLNA

Obvious if you used DLNA before, but in case this is your first use of DLNA;

  1. If off (LED red), boot the RasPi by shortly pressing the power button on the case, Wait a moment for the RasPi to finish booting.
  2. Ensure the Cinebar is on and set to Bluetooth input, wait for LED to be solid blue (USB and BT use the same input. When the Cinebar is still starting up, the LED pulses).
  3. Open your preferred DLNA control point on a device of your choice (on Android I use BubbleUPnP).
  4. select a media library of your choice (I use ReadyMedia (aka minidlna) on a Linux server).
  5. select your RasPi as renderer, by default it is called gmediarender on $(hostname).
  6. via your DLNA control point, you manage
    • choice of song
    • playlist (n.b. the playlist resides on your control point, not the renderer)
    • volume
    • play, pause, seek, stop, next/previous track

Be patient when waiting for DLNA devices to appear, if your setup is suboptimal, wait easily 10 minutes or more for your music server to appear.

Usage Notes

Ideally you would shut down both the RasPi and the Cinebar when not in use, to save some energy.

The RasPi 5 comes with a power buton, no additional software or setup needed. Press it briefly to initiate shutdown.

Because I chose Raspberry Pi OS (64-bit) Lite, I have no desktop session, which means a clean shutdown is triggered, without additional dialogs to click OK in, by briefly pressing the power button. Exactly the behaviour I want.

To force a hard shutdown, press and hold the power button.

Avoid doing that, prefer the clean shutown.

The Cinebar will enter low power mode (LED is red) by itself if no audio is played for a while.

If, like me, you frequently forget to power down the RasPi, then consider setting up an automatic shutdown timer as shown in the playbook.

Why Did I Start from Scratch

Reason for New OS Install

Normally, on Debian, I upgrade from one stable release to the next, but the Raspberry Pi OS docs say

Upgrading an existing image is sometimes possible, but is not guaranteed to work in every circumstance, and we do not recommend it.

Never touch a running system is a dumb idea, security updates need applying and when an operating system version is at End of Life, then it’s time to pick a newer base OS version.

Reasons for Playbook Changes

This was really just a cleanup job of the playbook I used for my Debian 11 plus RasPi 4B based setup, With the RasPi 5 I could drop quite a few tasks I needed back then for the Argon software.

Reason for New Hardware

The idea was to simply re-use the RasPi 4B and Argon ONE, but while the Argon 40’s instructions to

curl https://download.argon40.com/argon1.sh | bash

already annoyed me in the past, under bookworm I was really not having much joy getting the install script itself andd the softeware the script installs to work reliably.

As I feared, I was not alone in having trouble with the vendor’s software under Debian 12. It seems the vendor is taking way too long to support a Debian version that was released over half a year ago for my taste.

While https://gitlab.com/DarkElvenAngel/argononed worked better that the vendor software, it always bothered me that RasPis, up to and including the 4B, do not come with power on/off control out of the box.

Because the Raspberry Pi 5 comes with a power button, if I upgraded from a 4B to a 5, then I would not need software outside of the Debian 12 repos.

Since I only use this RasPi for playing music, I decided to pick a passively cooled case.

Because I play the music over USB, not HDMI, I decided to disable HDMI audio.

I setled on a KKSB Raspberry Pi 5 Case with Aluminium Heatsink for Silent Passive Cooling, it’s silent, small and has a simple plastic bit that presses the power button on the RasBerry Pi 5 itself.

So far passive cooling keeps it cool enough for my taste.

Before switching to the powersave governor, well below 55°C when playing a song.

pcfe@raspi5:~ $ vcgencmd measure_temp
temp=52.7'C

And with the powersave governor I do not even reach 51°C on the RasPi even though ambient temperature in the room is 28.3°C.

Lowest I saw so far when playing music was

pcfe@raspi5:~ $ vcgencmd measure_temp
temp=48.3'C

and the highest

pcfe@raspi5:~ $ vcgencmd measure_temp
temp=50.5'C

TODO

  • revisit post when this has been in active use for a few weeks
  • maybe use gstout_buffer_duration: 0

Update 2024-12-10

The setup has been in use for over half a year now, the only two changes to the above were

  • to lower power use when it’s off, I have done sudo rpi-eeprom-config -e and set POWER_OFF_ON_HALT=1 that should save somewhere between one and 1,5 Watts in soft-off mode.
  • obviously I have applied package updates as they become available in the Debian repos (that RasPi is one of many hosts where I regularly use ansible.builtin.package: to apply latest and then reboot if needed).

It behaves exactly as I expect from a music playing appliance; It’ unobtrusive, does not draw attention to itself and does not demand my attention through apps, newsletters, silly vendor apps that crash or needing maintenance. It does the job of playing my music flawlessly.