I kept seeing people say that bluetooth speakers/mic wouldn’t work directly without gutting them and putting an ESP in them.
Thought it would be an interesting challenge to figure it out as it’s just a basic speak/mic attached to Linux. I didn’t see why it wouldn’t work with the wyoming-satellite etc.
I don’t own any bluetooth speaker/mic combo devices but I had this Bluetooth Transmitter/Receiver for my vehicle which has a mic built in. It also allows you to plug powered speakers into the side and get audio out. I also liked that it showed the name of the device connected on the LCD screen. The mic quality is pretty bad but it worked (I don’t recommend it). Maybe they have made better ones now since this one. They can be found on Aliexpress from any place around $10-$20 and there is quite a gambit of them.
You may have to play with some more Linux/Pipewire settings to get it to autoload reliably but it indeed does work ok.
You need to use Debian 12 “Bookworm” as Pipewire is now default in it. You can use backports with Debian 11 “Bullseye” but why bother.
The bluetooth mic works using Profile: Headset Head Unit (HSP/HFP, codec CVSD) ie you will have lower sound quality than A2DP. A2DP does not allow for microphone use currently.
In researching this topic though I saw where people were adding scripts to profile swap for other purposes but would switch to A2DP while listening to music.
Personally, I could care less about quality and just that it is working for this demo as it sounded ok to me. I did notice the difference though. I saw some interesting things about codecs you could use to make HSP sound better but you had to compile some stuff for flac usage. I didn’t look further into it or maybe I misunderstood what they were talking about.
Installation
Some of these things may or may not be required but they helped in troubleshooting things with some visual representation. They are not needed for a CLI based setup. On Rasbian/Debian 12 most of this stuff is already there so maybe just try it out and hope for the best with no need to install any of this. I’m just tacking it here from a bare-bones look…
sudo apt install pipewire \
libspa-0.2-bluetooth \
pipewire-audio-client-libraries \
pipewire-media-session- \
wireplumber \
bluez \
blueman \
pavucontrol
Note: Most documentation states old locations /etc/pipewire
or /etc/pulse
for configurations. Currently, all the settings for pipewire are located here: /usr/share/pipewire/
unless you are playing with local user stuff.
Uncomment this extra command module-switch-on-connect
should tell pipewire-pulse to autoswitch to the bluetooth audio device when connected. If the device is not an always-on thing not sure if that is 100% thought out. I don’t think there is a wake on bluetooth with most power saving devices .
nano /usr/share/pipewire/pipewire-pulse.conf
# Extra commands can be executed here.
# load-module : loads a module with args and flags
# args = "<module-name> <module-args>"
# flags = [ "no-fail" ]
pulse.cmd = [
{ cmd = "load-module" args = "module-always-sink" flags = [ ] }
{ cmd = "load-module" args = "module-switch-on-connect" }
#{ cmd = "load-module" args = "module-gsettings" flags = [ "nofail" ] }
]
This is not required but to clear up some vcp, mcp, bap bluetooth service errors uncomment Experimental=true in:
sudo nano /etc/bluetooth/main.conf
# Enables D-Bus experimental interfaces
# Possible values: true or false
Experimental = true
Make sure this is uncommented as well:
# 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 'true'.
AutoEnable=true
sudo systemctl restart bluetooth
sudo usermod -G bluetooth -a pi
su - $USER
id
rfkill list
0: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
1: hci0: Bluetooth
Soft blocked: yes
Hard blocked: no
rfkill unblock all
rfkill list
0: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
1: hci0: Bluetooth
Soft blocked: no
Hard blocked: no
bluetoothctl
Agent registered
power on
Changing power on succeeded
agent on
Agent is already registered
default-agent
Default agent request successful
scan on
Discovery started
[CHG] Controller DE:AD:BE:EF:CA:FE Discovering: yes
[NEW] Device DE:AD:BE:EF:FA:CE AudioHeadSet
pair de:ad:be:ef:fa:ce
Attempting to pair with DE:AD:BE:EF:FA:CE
[CHG] Device DE:AD:BE:EF:FA:CE Connected: yes
[CHG] Device DE:AD:BE:EF:FA:CE UUIDs: #########-####-####-####-###############
[CHG] Device DE:AD:BE:EF:FA:CE UUIDs: #########-####-####-####-###############
[CHG] Device DE:AD:BE:EF:FA:CE ServicesResolved: yes
[CHG] Device DE:AD:BE:EF:FA:CE Paired: yes
Pairing successful
[CHG] Device DE:AD:BE:EF:FA:CE ServicesResolved: no
[CHG] Device DE:AD:BE:EF:FA:CE Connected: no
trust de:ad:be:ef:fa:ce
[CHG] Device DE:AD:BE:EF:FA:CE Trusted: yes
Changing DE:AD:BE:EF:FA:CE trust succeeded
connect de:ad:be:ef:fa:ce
Attempting to connect to DE:AD:BE:EF:FA:CE
[CHG] Device DE:AD:BE:EF:FA:CE Connected: yes
Connection successful
[CHG] Device DE:AD:BE:EF:FA:CE ServicesResolved: yes
scan off
quit
Note: If HSP is not showing up do not do this for your first time setting up! Unless you think you broke something during setup. It will delete all bluetooth devices that were trusted:
sudo cp -r /var/lib/bluetooth /var/lib/bluetooth_BACKUP
sudo systemctl stop bluetooth
sudo rm -rf /var/lib/bluetooth/*
sudo systemctl start bluetooth
sudo reboot
Add back the device with the directions above.
sudo apt install gstreamer1.0-pipewire
gst-launch-1.0 audiotestsrc ! audioresample ! autoaudiosink
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Redistribute latency...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
Redistribute latency...
New clock: GstPulseSinkClock
0:00:00.1 / 99:99:99.
You should hear a beep.
pw-cli list-objects | grep node.name
node.name = "Dummy-Driver"
node.name = "Freewheel-Driver"
node.name = "Midi-Bridge"
node.name = "v4l2_input.platform-bcm2835-isp.2"
node.name = "v4l2_input.platform-bcm2835-isp.3"
node.name = "v4l2_input.platform-bcm2835-isp.6"
node.name = "v4l2_input.platform-bcm2835-isp.7"
node.name = "alsa_output.platform-bcm2835_audio.stereo-fallback"
node.name = "bluez_input.DE_AD_BE_EF_FA_CE.0"
node.name = "bluez_output.DE_AD_BE_EF_FA_CE.1"
You can switch between the AD2P and HSP (Head-Set Profile) like this:
pactl list cards short
54 alsa_card.platform-bcm2835_audio alsa
55 alsa_card.platform-3f902000.hdmi alsa
73 bluez_card.DE_AD_BE_EF_FA_CE module-bluez5-device.c
pactl set-card-profile bluez_card.DE_AD_BE_EF_FA_CE a2dp-sink-sbc
pactl set-card-profile bluez_card.DE_AD_BE_EF_FA_CE headset-head-unit-cvsd
pactl list | grep "Active Profile"
Active Profile: output:stereo-fallback
Active Profile: off
Active Profile: headset-head-unit-cvsd <=== Yay!
General Satellite Setup
Wyoming OpenWakeWord Setup
git clone https://github.com/rhasspy/wyoming-openwakeword.git
cd wyoming-openwakeword
script/setup
script/run --uri 'tcp://0.0.0.0:10400' --debug --preload-model 'hey_jarvis'
ctrl + c to quit
Wyoming-OpenWakeWord Service
sudo nano /etc/systemd/user/wyoming-openwakeword.service
[Unit]
Description=Home Assistant Wyoming OpenWakeWord
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
ExecStart=%h/code/wyoming-openwakeword/script/run \
--uri 'tcp://0.0.0.0:10400' \
--debug \
--preload-model 'hey_jarvis'
WorkingDirectory=%h/code/wyoming-openwakeword
Restart=always
RestartSec=1
[Install]
WantedBy=default.target
systemctl --user enable wyoming-openwakeword
systemctl --user start wyoming-openwakeword
Wyoming Satellite Setup
sudo apt-get install python3 python3-pip python3-venv alsa-utils git
sudo apt-get install --no-install-recommends ffmpeg
git clone https://github.com/rhasspy/wyoming-satellite.git
cd wyoming-satellite
scripts/setup
script/run --name 'bluetooth satellite' --uri 'tcp://0.0.0.0:10700' --mic-command 'parecord --device bluez_input.DE_AD_BE_EF_FA_CE.0 --rate=16000 --channels=1 --format=s16le --raw' --snd-command 'paplay --device bluez_output.DE_AD_BE_EF_FA_CE.1 --rate=22000 --channels=1 --format=s16le --raw' --debug --wake-uri 'tcp://127.0.0.1:10400' --wake-word-name 'hey_jarvis' --done-wav sounds/done.wav --awake-wav sounds/awake.wav
ctrl + c to quit
Wyoming-Satellite service
sudo nano /etc/systemd/user/wyoming-satellite.service
[Unit]
Description=Home Assistant Wyoming-Satellite
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/code/wyoming-satellite
ExecStartPre=/usr/bin/timeout 30 sh -c 'while ! /usr/bin/ss -H -t -l -n sport = :10400 | /usr/bin/grep -q "^LISTEN.*:10400"; do /usr/bin/sleep 1; done'
ExecStar=%h/code/wyoming-satellite/script/run \
--name 'bluetooth satellite' \
--uri 'tcp://0.0.0.0:10700' \
--mic-command \
'parecord --device bluez_input.DE_AD_BE_EF_FA_CE.0 --rate=16000 --channels=1 --format=s16le --raw' \
--snd-command 'paplay --device bluez_output.DE_AD_BE_EF_FA_CE.1 --rate=22000 --channels=1 --format=s16le --raw' \
--debug \
--wake-uri 'tcp://127.0.0.1:10400' \
--wake-word-name 'hey_jarvis' \
--done-wav '/home/pi/code/wyoming-satellite/sounds/done.wav' \
--awake-wav '/home/pi/code/wyoming-satellite/sounds/awake.wav'
Restart=always
RestartSec=1
[Install]
WantedBy=default.target
systemctl --user enable --now wyoming-satellite
INFO:root:Ready
DEBUG:root:Detected IP: 10.0.42.100
DEBUG:root:Zeroconf discovery enabled (name=############, host=None)
DEBUG:root:Connecting to mic service: ['parecord', '--device', 'bluez_input.DE_AD_BE_EF_FA_CE.0', '--rate=16000', '--channels=1', '--format=s16le', '--raw']
DEBUG:root:Connecting to snd service: ['paplay', '--device', 'bluez_output.DE_AD_BE_EF_FA_CE.1', '--rate=16000', '--channels=1', '--format=s16le', '--raw']
DEBUG:root:Connecting to wake service: tcp://127.0.0.1:10400
INFO:root:Connected to services
DEBUG:root:Connected to mic service
DEBUG:root:Connected to wake service
DEBUG:root:Server set: 3667213960213
INFO:root:Connected to server
INFO:root:Waiting for wake word
DEBUG:root:Ping enabled
DEBUG:root:Detection(name='hey_jarvis_v0.1', timestamp=3674454417437)
DEBUG:root:Streaming audio
DEBUG:root:Event(type='run-pipeline', data={'start_stage': 'asr', 'end_stage': 'tts', 'restart_on_end': False, 'snd_format': {'rate': 22050, 'width': 2, 'channels': 1}}, payload=None)
DEBUG:root:Muting microphone for 0.8995918367346939 second(s)
DEBUG:root:Connected to snd service
DEBUG:root:Unmuted microphone
DEBUG:root:Connected to snd service
DEBUG:root:Unmuted microphone
DEBUG:root:Event(type='transcript', data={'text': ' Turn off the family room light.'}, payload=None)
INFO:root:Waiting for wake word
DEBUG:root:Connected to snd service
DEBUG:root:Event(type='synthesize', data={'text': 'Turned off the lights', 'voice': {'name': 'en_US-lessac-medium'}}, payload=None)
DEBUG:root:Connected to snd service
DEBUG:root:Detection(name='hey_jarvis_v0.1', timestamp=3784489216895)
DEBUG:root:Streaming audio
Random Fun
ncmpcpp Install
sudo apt install ncmpcpp
sudo nano $HOME/.config/ncmpcpp/config
mpd_host = "127.0.0.1"
mpd_port = 6600
mpd_music_dir = ~/Music
Mopidy Install
Add the archive’s GPG key:
sudo mkdir -p /etc/apt/keyrings
sudo wget -q -O /etc/apt/keyrings/mopidy-archive-keyring.gpg \
https://apt.mopidy.com/mopidy.gpg
Add the APT repo to your package sources:
sudo wget -q -O /etc/apt/sources.list.d/mopidy.list https://apt.mopidy.com/bookworm.list
Install Mopidy and all dependencies:
sudo apt update
sudo apt install mopidy
To list all the extensions available from apt.mopidy.com, you can run:
apt search mopidy
sudo apt install mopidy-mpd mopidy-somafm mopidy-local mopidy-spotify mopidy-tunein
sudo nano /etc/mopidy/mopidy.conf
[audio]
output = pulsesink server=127.0.0.1:4713 stream-properties="props,media.role=music"
[http]
enabled = true
hostname = 0.0.0.0
port = 6680
zeroconf = Mopidy HTTP server on $hostname
allowed_origins =
csrf_protection = true
default_app = mopidy
HACS
-
Install HACS
-
Go to any of the sections (integrations, frontend, automation).
-
Click on the 3 dots in the top right corner.
-
Select “Custom repositories”
-
Add the URL to the repository: hass-integrations/custom_components/mopidy at main · bushvin/hass-integrations · GitHub
-
Select the correct category.
-
Click the “ADD” button.
-
Go to Home Assistant settings → Integrations and add Mopidy
-
Restart HA
-
Go to the Integrations page and click + ADD INTEGRATION
-
Select Mopidy in the list of integrations
-
Fill out the requested information. Make sure to enter your correct FQDN or IP address. Using
localhost
,127.0.0.1
,::1
or any other loopback address will disable Mopidy-Local artwork.
Name: Mopidy001
IP: 10.0.42.1
Port: 6680 -
Click Submit.
Edit pipewire-pulse.conf
/usr/share/piperwire/pipewire-pulse.conf
# Extra commands can be executed here.
# load-module : loads a module with args and flags
# args = "<module-name> <module-args>"
# flags = [ "no-fail" ]
pulse.cmd = [
{ cmd = "load-module" args = "module-always-sink" flags = [ ] }
{ cmd = "load-module" args = "module-switch-on-connect" }
{ cmd = "load-module" args = "module-native-protocol-tcp auth-ip-acl=127.0.0.1" }
{ cmd = "load-module" args = "module-role-ducking trigger_roles=annouce,phone ducking_roles=music volume=75%" }
#{ cmd = "load-module" args = "module-gsettings" flags = [ "nofail" ] }
]
stream.properties = {
#node.latency = 1024/48000
#node.autoconnect = true
#resample.quality = 4
#channelmix.normalize = false
#channelmix.mix-lfe = true
#channelmix.upmix = true
#channelmix.upmix-method = psd # none, simple
#channelmix.lfe-cutoff = 150
#channelmix.fc-cutoff = 12000
#channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0
#channelmix.hilbert-taps = 0
#dither.noise = 0
}
pulse.properties = {
# the addresses this server listens on
server.address = [
"unix:native"
#"unix:/tmp/something" # absolute paths may be used
"tcp:4713" # IPv4 and IPv6 on all addresses
#"tcp:[::]:9999" # IPv6 on all addresses
#"tcp:127.0.0.1:8888" # IPv4 on a single address
#
#{ address = "tcp:4713" # address
# max-clients = 64 # maximum number of clients
# listen-backlog = 32 # backlog in the server listen queue
# client.access = "restricted" # permissions for clients
#}
]
#pulse.min.req = 256/48000 # 5ms
#pulse.default.req = 960/48000 # 20 milliseconds
#pulse.min.frag = 256/48000 # 5ms
#pulse.default.frag = 96000/48000 # 2 seconds
#pulse.default.tlength = 96000/48000 # 2 seconds
#pulse.min.quantum = 256/48000 # 5ms
#pulse.idle.timeout = 0 # don't pause after underruns
#pulse.default.format = F32
#pulse.default.position = [ FL FR ]
# These overrides are only applied when running in a vm.
vm.overrides = {
pulse.min.quantum = 1024/48000 # 22ms
}
}
systemctl --user restart pipewire-pulse
Helvum Install
sudo apt install flatpak
sudo apt install apt install gnome-software-plugin-flatpak
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install flathub org.pipewire.Helvum
Run
flatpak run org.pipewire.Helvum
Run ncmpcpp player to test mopidy server
ncmpcpp
Press 2 to see the plugins like SomaFM etc press Q to quit.
If you can’t hear the bluetooth audio from mopidy play with the patch in Helvum, pavucontrol or use the cli:
Show Inputs
pw-link -iI
39 Midi-Bridge:Midi Through:(playback_0) Midi Through Port-0
33 alsa_output.platform-bcm2835_audio.stereo-fallback:playback_FL
65 alsa_output.platform-bcm2835_audio.stereo-fallback:playback_FR
87 PulseAudio Volume Control:input_FL
89 PulseAudio Volume Control:input_FR
93 bluez_output.DE_AD_BE_EF_FA_CE.1:playback_FL
92 bluez_output.DE_AD_BE_EF_FA_CE.1:playback_FR
100 PulseAudio Volume Control:input_FL
98 PulseAudio Volume Control:input_FR
81 PulseAudio Volume Control:input_FL
85 PulseAudio Volume Control:input_FR
Show Outputs
pw-link -oI
40 Midi-Bridge:Midi Through:(capture_0) Midi Through Port-0
57 v4l2_input.platform-bcm2835-isp.2:out_0
59 v4l2_input.platform-bcm2835-isp.3:out_0
61 v4l2_input.platform-bcm2835-isp.6:out_0
63 v4l2_input.platform-bcm2835-isp.7:out_0
67 alsa_output.platform-bcm2835_audio.stereo-fallback:monitor_FL
66 alsa_output.platform-bcm2835_audio.stereo-fallback:monitor_FR
88 PulseAudio Volume Control:monitor_FL
90 PulseAudio Volume Control:monitor_FR
84 bluez_output.DE_AD_BE_EF_FA_CE.1:monitor_FL
91 bluez_output.DE_AD_BE_EF_FA_CE.1:monitor_FR
99 PulseAudio Volume Control:monitor_FL
72 PulseAudio Volume Control:monitor_FR
78 [email protected]:4713:output_FL
86 [email protected]:4713:output_FR
80 PulseAudio Volume Control:monitor_FL
79 PulseAudio Volume Control:monitor_FR
Manually Link Outputs
pw-link bluez_output.DE_AD_BE_EF_FA_CE.1:playback_FL [email protected]:4713:output_FL
pw-link bluez_output.DE_AD_BE_EF_FA_CE.1:playback_FR [email protected]:4713:output_FR
Mopidy in HA