piannunciator was written to solve a simple problem: provide a way to play media files for alerts and announcements from a home automation system without the expense of high-end media players or the complexity (and cloud dependencies) of getting integrations with Google or Amazon assistants to work. It simply uses a Raspberry Pi as a media player with an MQTT interface to play local (to the Pi) or remote (HTTP-fetchable from a LAN or WAN endpoint) audio files. That's it.
It came about because many of the users of my Reactor rules engine for multiple hubs run it on a Pi. For these users, there's already one Pi on the network ready to go. For users that run Reactor on a NAS or other platform, it's still easy to source a Pi that has audio support (3B, 4B, etc.) and use it with piannunciator.
Once installed and running, you simply send an MQTT message with the piannunciator/0/action/play
topic (the first two
components of the topic are configurable). The message should include a JSON payload object containing a file
key,
the value of which is a local filename or a URL to the media to be played.
{ "file": "local-audio.mp3" }
or
{ "file": "http://192.168.0.100/media/music/other/song.mp3" }
That's it.
piannunciator is written in Python, so it requires Python 3.9 or higher. It also uses VLC to play media, so you need to install it (sudo apt-get install vlc
).
It also needs the paho-mqtt Python package: pip3 install paho-mqtt
. Note that this command does not use sudo
— it should be installed as the unprivileged user (e.g. pi
or whoever you usually log in as).
The configuration file is in INI format. There is only one section, named piannunciator
. The following keys can be used in this section:
Name | Description |
---|---|
server |
The hostname or IP address of the MQTT broker. Default: 127.0.0.1 (aka localhost ) |
port |
The port number for the MQTT broker connection. Default: 1883 |
topic |
The topic prefix used by the package. The default is piannunciator/0 ; you only need to change it if you have multiple piannunciators in your network, and in that case, change the 0 (zero) to another value for each. |
playbackTimeout |
Time (in seconds) at which the playback process times out and will be terminated. Default: 60 (seconds) |
cachedir |
Location of the cache directory. Default: ./cache |
tmpdir |
Location for storage of temporary files. Default: /tmp |
In most systems, the only value you will need to set is server
. You will need to create the cache directory yourself.
You will also need to make sure that your Pi is configured to output audio to the correct device. For example, if you are connecting speakers to the 3.5mm audio jack, you need to make sure that's the default audio device. This is done using the raspi-config
utility (run with sudo
or while logged in as root
).
You can run piannunciator by simply typing python3 piannunciator.py
. This isn't the best long-term solution, though... read on...
Generally, you'll want piannunciator running all the time, so you want to install it as a daemon. The best way to do that is to put it under control of systemd
.
-
Become
root
on your Raspberry Pi. From a regular user login, typesudo su -
to becomeroot
. -
Using your favorite editor, create a text file in
/etc/systemd/system
calledpiannunciator.service
with the following contents. Make sure to change theWorkingDirectory
to the directory wherepiannunciator.py
is located.[Unit] Description=piannunciator After=network.target [Service] Type=simple User=pi WorkingDirectory=/home/pi/piannunciator ExecStart=/usr/bin/python3 piannunciator.py --config piannunciator.ini StandardOutput=journal StandardError=journal SyslogIdentifier=piannunciator Restart=on-failure [Install] WantedBy=multi-user.target
-
Poke systemd to let it know that the new service file exists (or was recently modified):
systemctl daemon-reload
-
Make systemd start the daemon at startup:
systemctl enable piannunciator.service
-
Make sure it's running:
systemctl start piannunciator.service
This configuration will cause logging to the systemd.journal facility. You can see the log by running sudo journalctl -u piannunciator.service
.
To follow logging in real time, add -f
before -u
in the foregoing command.
To get an audio file to play, use your favorite MQTT tool to publish the topic piannunciator/0/action/play
with the following JSON payload:
{ "file": "localsound.mp3", "repeat": 2 }
{ "file": "http://url-to-media-file", "cache": false }
If the file specified is just a plain filename rather than a URL, piannunciator will attempt to open and play the file from its WorkingDirectory. You can upload files to that directory on your Pi. Make sure they have read permission for all users.
The cache
value can be changed to true
if the audio file is static; that is, it doesn't change its content. When true
, piannunciator will keep a copy of the media file locally (in the cache directory configured), and future requests to play the audio file will be done using the cached media file rather than fetching the file from the given URL. Caching only works for media played from a URL; it does not apply to files played from the WorkingDirectory.
An optional repeat
key with an integer value can be added to the payload to play additional iterations of the requested audio file. For clarity, repeat: 0
means play the file once and don't repeat it, while repeat: 2
means play the file then repeat it twice.
An optional id
key and value can be included in the payload. When included, they are echoed in status topics related to playback (see below).
piannunciator sends a status message with topic piannunciator/0/status
as follows:
- When the process starts and any time it finishes handling a request, the payload will be a JSON object with a
status
key with string valueidle
(i.e.{ "status": "idle" }
). - When an audio file starts playing, the JSON object payload will have a
status
valueplaying
. The object will include afile
key with the requested media file URL or filename, and theid
key and value (see above). - When an audio file finishes playing successfully, a JSON playload similar to the above will be sent, but the
status
value will befinished
. - If an error occurs while trying to play the audio file, the
status
field of the JSON object payload will befailed
and areason
key will contain additional information. The syslog should also be checked for additional details.
This project is offered under the MIT License.