No project description provided
Project description
plapperkasten
Manage a headless media device.
What?!
Turn a Raspberry Pi with only buttons and speakers attached into a jukebox playing local music.
In a typical setup you might have a couple of buttons wired to a raspberry pi's GPIO pins, some speakers attached to the audio jack and a RFID reader plugged into one of the USB ports.
This is based on the idea of the PhonieBox ("original" software, more information on awesomeopensource.com) but written from scratch and not as feature-rich - yet!
plapperkasten has started as a small script to learn the workings of a Raspberry Pi box and to avoid the great but intimidatingly complex RPi-Jukebox-RFID. As the project has grown more complex and mature I've decided to release it into the wild - so here it is.
Features
- Easy setup: install the python package from PyPi (with
pip install plapperkasten) or configure the whole machine using plapperkasten-setup. This will guide you from the download of the OS all the way to a functioning device includingSSH, setting up an isolated python environment, configuring sound, users, etc. - Nearly everything is a plugin: Theres a plugin for controlling the sound using
ALSAand one forPipeWire. Need to output toJACKorPulseAudio? Write your own plugin by lending from the existing plugins. - Completely written in Python 3:
- Does not run as a super user:
plapperkastenis geared towards interacting with hardware buttons, sound and the like without needing special privileges. This needs some configuration of your OS but plapperkasten-setup is there to help you - or if you want it your way to give you some hints.
Setup
Please see https://github.com/randomchars42/plapperkasten-setup for detailed instructions and an easy installer.
Development
Style
Docstrings are formatted according to the Google Style Guide.
All other formatting tries to adhere to PEP8 and is enforced by YAPF.
Log entries use lazy evaluation, i.e., logger.debug('start %s', name), start with a lower-case letter and do not end with a full stop.
Raised errors on the other hand use f-strings (if necessary) and contain whole sentences, i.e. ValueError(f'{variable} did not match XXXX.').
Linting / Checking
Code should be checked by pylint and mypy.
Paths
All path representations should be pathlib.Path-objects instead of strings.
Logging
Logging uses a wrapper (plapperkasten.plklogging) around the logging module to cover logging from multiple processes.
Import a logger using:
from plapperkasten.plklogging import plklogging
logger: plklogging.PlkLogger = plklogging.get_logger(__name__)
# your code here
Setup for development
Requirements
Python >= 3.10as it uses typehinting features only available beginning with 3.10.
Semi-optional
libgpiodwithpython3-libgpiodandgpiodmonitor>=1.1.3if you plan to use theinputgpiod-plugin. Alternatively you may implement the functionality usingRPi.GPIOor any library of your choice - but beware: you're gooing to need super user privileges!
Recommendations
-
pyenv to setup a python version withput messing up your system.
-
pipenv to create a virtual environment with a defined python version.
Example setup using pipenv
git clone git@github.com:randomchars42/plapperkasten.git
cd plapperkasten
# consider adding this to your .bashrc / equivalent for your shell:
# `export PIPENV_VENV_IN_PROJECT=1`
# this leads to a folder `.venv` being created at the project root
# otherwise you might need to tweak the `[tool.mypy]`` path in
# `pyproject.toml` (depending on your editor setup).
# setup a virtual environment with set python version
# set the version to >= 3.10
pipenv --python 3.10
# install development dependencies
pipenv install --dev
# activate venv
pipenv shell
Plapperkasten logic in a nutshell
plapperkasten is a simple platform for plugins that can talk to each other to control a jukebox. There is a plugin recieving input from buttons (src/plapperkasten/plugins/inputgpiod), one recieving input from RFID readers (src/plapperkasten/plugins/inputdevinputevent), one for controlling an MPD client (src/plapperkasten/plugins/mpdclient) and so forth.
A good place to get to know the overall logic is src/plapperkasten/plapperkasten.py. This is the main entry point into the programme.
It will in turn:
- start a process for logging
- load the configuration files
- gather all plugins from
src/plapperkasten/pluginsand~/.config/plapperkasten/plugins(or wherever~/.config/plapperkasten/config.yamlpoints it to) - start a process for each plugin (that is not blacklisted in
~/.config/plapperkasten/config.yaml) - the plugins register for events they might process
- trigger
on_before_run()for each pluggin - wait for the plugins to send events to process, translate or re-emit so that other plugins can react to them
- or wait for all plugins to exit (either by themselves or because of a
terminateevent) - if a
shutdownevent has been emitted,plapperkastenwill try to shutdown the host (here's how to stop it during development)
Examplary flow of events
In a typical setup you might have a couple of buttons wired to a raspberry pi's GPIO pins, some speakers attached to the audio jack and a RFID reader plugged into one of the USB ports.
An RFID token has been recognised
inputdevinputeventlistens to the attached RFID reader and reads a token.inputdevinputeventsends anrawevent with the payload0123456789(the value read from the token) toplapperkastenvia its pipeplapperkastenrecieves the event, looks it up in~/.config/plapperkasten/events.mapand sees it maps to aload_sourceevent with the payloaduse=Mpdclient,key=Music/Folder/Band/Album.plapperkastenemitsload_sourceto those who listenmpdclientlistens toload_sourceand makes mpd or mopidy (whichever you run on the system) add the folderMusic/Folder/Band/Albumto a new playlist and start playing it
A button is pressed
inputgpiodlistens to the gpio pins and recieves a signal from pin 12.inputgpiodsends an event12_shorttoplapperkastenvia its pipeplapperkastenrecieves the event, looks it up in~/.config/plapperkasten/events.mapand sees it maps to avolume_increaseeventplapperkastenemitsvolume_increaseto those who listenpwwp(short for PipeWire / Wireplumber) listens tovolume_increaseand callswpctl set-volume @DEFAULT_AUDIO_SINK@ 1%+pwwpsends an eventbeeptoplapperkastenplapperkastenknows from its config that it should pass those events through, so it re-emits the event to whom it may concernsoundeffectslistens to thebeepand produces a beep
Core files
src/plapperkasten/settings/config.yamlcontains default settings - user changes should not go here~/.config/plapperkasten/config.yamlmay contain any of the settings fromsrc/plapperkasten/settings/config.yamland overwrite themsrc/plapperkasten/settings/events.mapcould contain default event mappings but is empty - user changes should not go here~/.config/plapperkasten/events.mapmay contain events defined by the user (seesrc/plapperkasten/keymap.pyfor a description of the form)
Creating a plugin
A good place to start is to copy the "example" plugin and to take off from there. You will find it in src/plugins/example.
Each plugin lives in its own process. This has some severe implications:
- You need to use the logging functionality provided by
plapperkasten.plkloggingsee logging above. - The class is initialised in the main process, this is where it might access data from the configuration or other parts of the programme. It may only store immutable information or fresh copies (as returned by
plapperkasten.config) or else the multiprocessing hell will be upon you. - If you need to do something (like fetching configuration values or registering for events) on initialisation before the plugin's process was started use
on_init. - If you need to do something as soon as the new process has started use
on_before_run. - If you need to do something every X seconds use
on_tickand setself._tick_intervalto X inon_init. - If you need to respond to an event, register for it and write an
on_EVENTmethod, e.g., placeregister_for('my_event')inon_initand writeon_my_eventto handle the event. - If you need total controll - you usually don't - overwrite
run, as insrc/plapperkasten/plugins/inputgpiod/inputgpiod.py. - If you need to tidy up after you put the code in
on_after_run. - If you want to prevent
plapperkastenfrom shutting down after a period of idleness usesend_busy. - If you want
plapperkastento think it might restart its "idle" countdown usesend_idle(but the countdown will only commence if all plugins are idle). - If you want to send an event to
plapperkastenusesend_to_main.
Preventing shutdown during development
To avoid shutdown during development set debug in ~/.config/plapperkasten/config.yaml to true.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file plapperkasten-0.7.0.tar.gz.
File metadata
- Download URL: plapperkasten-0.7.0.tar.gz
- Upload date:
- Size: 3.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.8.0 pkginfo/1.9.6 readme-renderer/41.0 requests/2.31.0 requests-toolbelt/1.0.0 urllib3/2.0.4 tqdm/4.66.1 importlib-metadata/6.8.0 keyring/24.2.0 rfc3986/2.0.0 colorama/0.4.6 CPython/3.10.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
163e5565876ef823a2ad8d8ce00d734b586a4e172a19f41eb4f421a6596d29a0
|
|
| MD5 |
fc21fe7a691e002aad4aaac32316d79f
|
|
| BLAKE2b-256 |
1bfc935e84929aaead7d4d4a744af3013e180990098f40750c99280b402dce46
|
File details
Details for the file plapperkasten-0.7.0-py3-none-any.whl.
File metadata
- Download URL: plapperkasten-0.7.0-py3-none-any.whl
- Upload date:
- Size: 3.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.8.0 pkginfo/1.9.6 readme-renderer/41.0 requests/2.31.0 requests-toolbelt/1.0.0 urllib3/2.0.4 tqdm/4.66.1 importlib-metadata/6.8.0 keyring/24.2.0 rfc3986/2.0.0 colorama/0.4.6 CPython/3.10.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e16ec180253b2c7b20f44ae2cfb6ed15e3c2aceb8e8bdfd23661dbc0837cae0a
|
|
| MD5 |
461e994f4805c3adc2f24c9cf6c657d5
|
|
| BLAKE2b-256 |
1ab57594b0b680648f4c8a01f77f677b5879bc595a53439cfeef2ecac93e7b2c
|