Add playbook and roles for Audio Pi

This commit is contained in:
yuri 2022-12-18 04:36:53 +01:00
parent 7a869f6330
commit 96ac8de142
No known key found for this signature in database
GPG key ID: E646779AC54AEC64
21 changed files with 516 additions and 2 deletions

View file

@ -0,0 +1,8 @@
---
- name: Configure audio.z9 as a bluetooth audio sink and AirPlay server
become: true
hosts: audio
roles:
- hifiberry
- bluetooth_audio_sink
- shairport_sync

View file

@ -0,0 +1,12 @@
general =
{
name = "Audio Pi";
port = 5000;
udp_port_base = 6001;
udp_port_range = 10;
interpolation = "soxr";
output_backend = "alsa";
mdns_backend = "avahi";
volume_range_db = 40;
volume_max_db = -18.0;
};

View file

@ -9,11 +9,11 @@ argument_specs:
type: bool
add_apt_repository__keyring_url:
description: URL to the repository's keyring
required: true
required: false
type: str
add_apt_repository__keyring_path:
description: Path where to store the keyring
required: true
required: false
type: str
add_apt_repository__repo:
description: The apt source line

View file

@ -13,6 +13,7 @@
name: apt-transport-https
when: add_apt_repository__https_repo
- name: Add repository signing key to keychain
when: add_apt_repository__keyring_url is defined and add_apt_repository__keyring_path is defined
ansible.builtin.apt_key:
url: "{{ add_apt_repository__keyring_url }}"
keyring: "{{ add_apt_repository__keyring_path }}"
@ -21,3 +22,4 @@
ansible.builtin.apt_repository:
repo: "{{ add_apt_repository__repo }}"
filename: "{{ add_apt_repository__filename }}"
update_cache: true

View file

@ -0,0 +1,3 @@
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin audiosink --noclear %I 38400 linux

View file

@ -0,0 +1,8 @@
[Unit]
Description=Bluetooth speaker agent
[Service]
ExecStart=python3 /opt/speaker-agent/speaker-agent.py
[Install]
WantedBy=default.target

View file

@ -0,0 +1,14 @@
---
- name: Restart bluetooth service
ansible.builtin.systemd:
service: bluetooth.service
state: restarted
- name: Restart speaker-agent service
become_user: audiosink
ansible.builtin.systemd:
service: speaker-agent.service
state: restarted
scope: user
daemon_reload: true
- name: Reboot host
ansible.builtin.reboot:

View file

@ -0,0 +1,8 @@
---
argument_specs:
main:
options:
bluetooth_audio_sink__name:
description: Name the bluetooth device should have
type: str
required: true

View file

@ -0,0 +1,8 @@
---
dependencies:
- role: distribution_check
vars:
distribution_check__supported_distributions:
- name: Debian
versions:
- "11"

View file

@ -0,0 +1,142 @@
---
- name: Ensure acl is installed
ansible.builtin.apt:
name: acl
- name: Ensure machine-info file is deployed
ansible.builtin.template:
src: machine-info.j2
dest: /etc/machine-info
mode: 0644
owner: root
group: root
notify: Restart bluetooth service
- name: Ensure bluetooth config is deployed
ansible.builtin.template:
src: main.conf.j2
dest: /etc/bluetooth/main.conf
mode: 0644
owner: root
group: root
notify: Restart bluetooth service
- name: Ensure bluetooth service is enabled and started
ansible.builtin.systemd:
service: bluetooth.service
state: started
enabled: true
- name: Ensure audiosink user exists
ansible.builtin.user:
name: audiosink
groups:
- audio
- name: Ensure user services are started without needing to login
ansible.builtin.command:
cmd: loginctl enable-linger audiosink
changed_when: false
- name: Detect if on Raspberry Pi # noqa ignore-errors
ansible.builtin.include_role:
name: raspberry_pi_check
ignore_errors: true
- name: Ensure debian archive keyring is installed # noqa no-handler
when: raspberry_pi_check__cpuinfo.found
ansible.builtin.apt:
deb: https://ftp.debian.org/debian/pool/main/d/debian-archive-keyring/debian-archive-keyring_2021.1.1_all.deb
- name: Ensure debian backports repository is enabled
ansible.builtin.include_role:
name: add_apt_repository
vars:
add_apt_repository__https_repo: false
add_apt_repository__repo: "deb http://deb.debian.org/debian {{ ansible_facts.lsb.codename }}-backports main contrib non-free"
add_apt_repository__filename: "{{ ansible_facts.lsb.codename }}-backports.list"
- name: Ensure pipewire, wireplumber and libspa-0.2-bluetooth are installed
ansible.builtin.apt:
name:
- pipewire
- wireplumber
- libspa-0.2-bluetooth
default_release: "{{ ansible_facts.lsb.codename }}-backports"
- name: Ensure pipewire user service is enabled and started
become_user: audiosink
ansible.builtin.systemd:
name: pipewire.service
state: started
enabled: true
scope: user
- name: Ensure pipewire-pulse user service is enabled and started
become_user: audiosink
ansible.builtin.systemd:
name: pipewire-pulse.service
state: started
enabled: true
scope: user
- name: Ensure wireplumber user service is enabled and started
become_user: audiosink
ansible.builtin.systemd:
name: wireplumber.service
state: started
enabled: true
scope: user
- name: Ensure speaker-agent dependencies are installed
ansible.builtin.apt:
name:
- git
- python3-dbus
- name: Ensure speaker-agent repository is cloned
ansible.builtin.git:
repo: https://github.com/fdanis-oss/pw_wp_bluetooth_rpi_speaker.git
dest: /opt/speaker-agent
version: 9a939a23865ea020816017e06fd2290f301c35f9
- name: Ensure .config directory exists
become_user: audiosink
ansible.builtin.file:
path: /home/audiosink/.config
state: directory
mode: 0700
owner: audiosink
group: audiosink
- name: Ensure .config/systemd directory exists
become_user: audiosink
ansible.builtin.file:
path: /home/audiosink/.config/systemd
state: directory
mode: 0755
owner: audiosink
group: audiosink
- name: Ensure .config/systemd/user directory exists
become_user: audiosink
ansible.builtin.file:
path: /home/audiosink/.config/systemd/user
state: directory
mode: 0755
owner: audiosink
group: audiosink
- name: Ensure speaker-agent service file is deployed
become_user: audiosink
ansible.builtin.copy:
src: speaker-agent.service
dest: /home/audiosink/.config/systemd/user/speaker-agent.service
mode: 0644
owner: audiosink
group: audiosink
notify: Restart speaker-agent service
- name: Ensure speaker-agent service is enabled and started
become_user: audiosink
ansible.builtin.systemd:
name: speaker-agent.service
state: started
enabled: true
scope: user
# This part is janky, but I don't know how to make the bluetooth service, wireplumber and pipewire
# work with each other without being logged in.
- name: Ensure getty@tty1 override file is deployed
ansible.builtin.copy:
src: override.conf
dest: /etc/systemd/system/getty@tty1.service.d/override.conf
mode: 0644
owner: root
group: root
notify: Reboot host
- name: Ensure autologin for audiosink user is enabled
ansible.builtin.systemd:
service: getty@tty1.service
enabled: true
notify: Reboot host

View file

@ -0,0 +1 @@
PRETTY_HOSTNAME=Audio Pi

View file

@ -0,0 +1,211 @@
[General]
# Defaults to 'BlueZ X.YZ', if Name is not set here and plugin 'hostname' is not loaded.
# The plugin 'hostname' is loaded by default and overides the Name set here so
# consider modifying /etc/machine-info with variable PRETTY_HOSTNAME=<NewName> instead.
Name = {{ bluetooth_audio_sink__name }}
# Default device class. Only the major and minor device class bits are
# considered. Defaults to '0x000000'.
Class = 0x200414 # Audio (Major Service Class), Audio/Video (Major Device Class), Loudspeaker (Minor Device Class)
# How long to stay in discoverable mode before going back to non-discoverable
# The value is in seconds. Default is 180, i.e. 3 minutes.
# 0 = disable timer, i.e. stay discoverable forever
#DiscoverableTimeout = 0
DiscoverableTimeout = 0
# Always allow pairing even if there are no agent registered
# Possible values: true, false
# Default: false
#AlwaysPairable = false
AlwaysPairable = true
# How long to stay in pairable mode before going back to non-discoverable
# The value is in seconds. Default is 0.
# 0 = disable timer, i.e. stay pairable forever
#PairableTimeout = 0
PairableTimeout = 0
# Use vendor id source (assigner), vendor, product and version information for
# DID profile support. The values are separated by ":" and assigner, VID, PID
# and version.
# Possible vendor id source values: bluetooth, usb (defaults to usb)
#DeviceID = bluetooth:1234:5678:abcd
# Do reverse service discovery for previously unknown devices that connect to
# us. For BR/EDR this option is really only needed for qualification since the
# BITE tester doesn't like us doing reverse SDP for some test cases, for LE
# this disables the GATT client functionally so it can be used in system which
# can only operate as peripheral.
# Defaults to 'true'.
#ReverseServiceDiscovery = true
# Enable name resolving after inquiry. Set it to 'false' if you don't need
# remote devices name and want shorter discovery cycle. Defaults to 'true'.
#NameResolving = true
# Enable runtime persistency of debug link keys. Default is false which
# makes debug link keys valid only for the duration of the connection
# that they were created for.
#DebugKeys = false
# Restricts all controllers to the specified transport. Default value
# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
# Possible values: "dual", "bredr", "le"
#ControllerMode = dual
# Enables Multi Profile Specification support. This allows to specify if
# system supports only Multiple Profiles Single Device (MPSD) configuration
# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
# Devices (MPMD) configurations.
# Possible values: "off", "single", "multiple"
#MultiProfile = off
# Permanently enables the Fast Connectable setting for adapters that
# support it. When enabled other devices can connect faster to us,
# however the tradeoff is increased power consumptions. This feature
# will fully work only on kernel version 4.1 and newer. Defaults to
# 'false'.
#FastConnectable = false
# Default privacy setting.
# Enables use of private address.
# Possible values: "off", "device", "network"
# "network" option not supported currently
# Defaults to "off"
# Privacy = off
# Specify the policy to the JUST-WORKS repairing initiated by peer
# Possible values: "never", "confirm", "always"
# Defaults to "never"
#JustWorksRepairing = never
JustWorksRepairing = always
# How long to keep temporary devices around
# The value is in seconds. Default is 30.
# 0 = disable timer, i.e. never keep temporary devices
#TemporaryTimeout = 30
# Enables the device to issue an SDP request to update known services when
# profile is connected. Defaults to true.
#RefreshDiscovery = true
Enable=Source,Sink,Media
[Controller]
# The following values are used to load default adapter parameters. BlueZ loads
# the values into the kernel before the adapter is powered if the kernel
# supports the MGMT_LOAD_DEFAULT_PARAMETERS command. If a value isn't provided,
# the kernel will be initialized to it's default value. The actual value will
# vary based on the kernel version and thus aren't provided here.
# The Bluetooth Core Specification should be consulted for the meaning and valid
# domain of each of these values.
# BR/EDR Page scan activity configuration
#BRPageScanType=
#BRPageScanInterval=
#BRPageScanWindow=
# BR/EDR Inquiry scan activity configuration
#BRInquiryScanType=
#BRInquiryScanInterval=
#BRInquiryScanWindow=
# BR/EDR Link supervision timeout
#BRLinkSupervisionTimeout=
# BR/EDR Page Timeout
#BRPageTimeout=
# BR/EDR Sniff Intervals
#BRMinSniffInterval=
#BRMaxSniffInterval=
# LE advertisement interval (used for legacy advertisement interface only)
#LEMinAdvertisementInterval=
#LEMaxAdvertisementInterval=
#LEMultiAdvertisementRotationInterval=
# LE scanning parameters used for passive scanning supporting auto connect
# scenarios
#LEScanIntervalAutoConnect=
#LEScanWindowAutoConnect=
# LE scanning parameters used for passive scanning supporting wake from suspend
# scenarios
#LEScanIntervalSuspend=
#LEScanWindowSuspend=
# LE scanning parameters used for active scanning supporting discovery
# proceedure
#LEScanIntervalDiscovery=
#LEScanWindowDiscovery=
# LE scanning parameters used for passive scanning supporting the advertisement
# monitor Apis
#LEScanIntervalAdvMonitor=
#LEScanWindowAdvMonitor=
# LE scanning parameters used for connection establishment.
#LEScanIntervalConnect=
#LEScanWindowConnect=
# LE default connection parameters. These values are superceeded by any
# specific values provided via the Load Connection Parameters interface
#LEMinConnectionInterval=
#LEMaxConnectionInterval=
#LEConnectionLatency=
#LEConnectionSupervisionTimeout=
#LEAutoconnecttimeout=
[GATT]
# GATT attribute cache.
# Possible values:
# always: Always cache attributes even for devices not paired, this is
# recommended as it is best for interoperability, with more consistent
# reconnection times and enables proper tracking of notifications for all
# devices.
# yes: Only cache attributes of paired devices.
# no: Never cache attributes
# Default: always
#Cache = always
# Minimum required Encryption Key Size for accessing secured characteristics.
# Possible values: 0 and 7-16. 0 means don't care.
# Defaults to 0
#KeySize = 0
# Exchange MTU size.
# Possible values: 23-517
# Defaults to 517
#ExchangeMTU = 517
# Number of ATT channels
# Possible values: 1-5 (1 disables EATT)
# Default to 3
#Channels = 3
[Policy]
#
# The ReconnectUUIDs defines the set of remote services that should try
# to be reconnected to in case of a link loss (link supervision
# timeout). The policy plugin should contain a sane set of values by
# default, but this list can be overridden here. By setting the list to
# empty the reconnection feature gets disabled.
#ReconnectUUIDs=00001112-0000-1000-8000-00805f9b34fb,0000111f-0000-1000-8000-00805f9b34fb,0000110a-0000-1000-8000-00805f9b34fb
# ReconnectAttempts define the number of attempts to reconnect after a link
# lost. Setting the value to 0 disables reconnecting feature.
#ReconnectAttempts=7
# ReconnectIntervals define the set of intervals in seconds to use in between
# attempts.
# If the number of attempts defined in ReconnectAttempts is bigger than the
# set of intervals the last interval is repeated until the last attempt.
#ReconnectIntervals=1,2,4,8,16,32,64
# AutoEnable defines option to enable all controllers when they are found.
# This includes adapters present on start as well as adapters that are plugged
# in later on. Defaults to 'false'.
AutoEnable=true

View file

@ -0,0 +1,3 @@
---
- name: Restart system
ansible.builtin.reboot:

View file

@ -0,0 +1,17 @@
---
argument_specs:
main:
options:
hifiberry__device:
description: Which HiFiBerry board to enable
type: str
choices:
- hifiberry-dac
- hifiberry-dacplus
- hifiberry-dacplushd
- hifiberry-dacplusadc
- hifiberry-dacplusadcpro
- hifiberry-digi
- hifiberry-digi-pro
- hifiberry-amp
required: true

View file

@ -0,0 +1,9 @@
---
dependencies:
- role: distribution_check
vars:
distribution_check__supported_distributions:
- name: Debian
versions:
- "11"
- role: raspberry_pi_check

View file

@ -0,0 +1,18 @@
---
- name: Remove dtparam=audio=on
ansible.builtin.lineinfile:
line: dtparam=audio=on
dest: /boot/config.txt
state: absent
notify: Restart system
- name: Set audio=off for dtoverlay=vc4-kms-v3d
ansible.builtin.lineinfile:
regexp: ^dtoverlay=vc4-kms-v3d
line: dtoverlay=vc4-kms-v3d,audio=off
dest: /boot/config.txt
notify: Restart system
- name: Add hifiberry dtoverlay
ansible.builtin.lineinfile:
line: "dtoverlay={{ hifiberry__device }}"
dest: /boot/config.txt
notify: Restart system

View file

@ -0,0 +1,12 @@
- name: Detect if on Raspberry Pi
ansible.builtin.lineinfile:
path: /proc/cpuinfo
regexp: ".*Raspberry Pi.*"
state: absent
check_mode: true
register: raspberry_pi_check__cpuinfo
changed_when: false
- name: Fail when not on Raspberry Pi # noqa no-handler
when: not raspberry_pi_check__cpuinfo.found
ansible.builtin.fail:
msg: You are not running on Raspberry Pi hardware!

View file

@ -0,0 +1,5 @@
---
- name: Restart shairport-sync service
ansible.builtin.systemd:
service: shairport-sync
state: restarted

View file

@ -0,0 +1,8 @@
---
argument_specs:
main:
options:
shairport_sync__config:
description: shairport-sync config content
type: str
required: true

View file

@ -0,0 +1,8 @@
---
dependencies:
- role: distribution_check
vars:
distribution_check__supported_distributions:
- name: Debian
versions:
- "11"

View file

@ -0,0 +1,17 @@
---
- name: Ensure shairport-sync is installed
ansible.builtin.apt:
name: shairport-sync
- name: Ensure shairport-sync config is deployed
ansible.builtin.copy:
content: "{{ shairport_sync__config }}"
dest: /etc/shairport-sync.conf
mode: 0644
owner: root
group: root
notify: Restart shairport-sync service
- name: Ensure shairport-sync service is enabled and started
ansible.builtin.systemd:
service: shairport-sync
state: started
enabled: true