Guten Abend zusammen,
in letzter Zeit gab es von mir mehrere Themen die alle von Problem zu meinem aktuellen Projekt handelten. Ich bin damit noch nicht fertig, aber es so weit das man es benutzen kann.
Es handelt sich bei meinem Projekt um einen Ersatz für unsere altes Radio. Die MusicBox besteht aus einem Raspberry Pi 4 (2GB) mit einem Hifiberry MiniAmp und einem WaveShare 4,3" Touch-Display.
Programmiert wurde die MusicBox mit Python. Für die Touchbedienung habe ich GUI mit Kivy erstellt.
Die MusicBox hat ein Hauptmenü mit vier Funktionen:
1. Funktion: Internetradio
Für diese Funktion muss an in einem definierten Pfad eine *csv - Datei gespeichert sein, die die gewünschten Radiostreams mit Sendernamen enthält. Diese Datei wird von Python eingelesen und die beinhalteten Sender kann man in einem DropDown-Menü auswählen. Die Wiedergabe kann mit einem Button pausiert werden und über einen Slider kann die Lautstärke eingestellt werden.
2. Funktion: Playlist-Player
Für diese Funktion wird in einem definierten Pfad unterschiedliche Ordner erstellt und mit Musik-Dateien befüllt. Zum Beispiel ein Ordner "Party" mit Partymusik und ein Ordner "Rock" mit Rockmusik und so weiter, was man eben gern hören will. Diese Ordner kann man ebenfalls in einem DropDown-Menü auswählen. Und der Inhalt der sich in dem ausgewählten Ordner befindet wird abgespielt. Über Buttons kann man die Wiedergabe stoppen und pausieren sowie den nächsten oder den vorherigen Titel auswählen. Mit einem Slider wird die Lautstärke eingestellt.
3. Funktion Bluetooth
Diese Funktion baiserit auf diesem Rpi Audio Receiver. Damit ist es möglich die MusicBox als Bluetooth-Speaker zu verwenden. Es wird einfach an dem gewünschten Abspielgerät Bluetooth aktivert und "MusicBox" ausgewählt. Die Verbindung wird automatisch hergestellt und die Musik wird über die MusicBox ausgegeben. Die Lautstärke wird dabei über das Abspielgerät gesteuert. Genaueres findet ihr in dem Link. Mein Programm macht in diesem Fall nicht viel, es wird lediglich Bluetooth aktiviert und beim verlassen dieser Funktion wird das Bluetooth wieder deaktiviert.
4. Funktion Shutdown
Die MusicBox wird ausgeschaltet bzw. der Pi wird herunter gefahren.
Was ich noch machen will:
-Beim Abspielen vom Internetradio hätte ich gerne ein Label, dass mir den aktuellen Titel anzeigt. Ein passende Funktion für python-vlc habe ich leider noch nicht finden können. Wenn hier jemand etwas passendes weis, wäre ich euch dankbar.
-Was passiert wenn beispielsweise die Internetverbindung nicht da ist oder auf den gewünschten Dateienpfad nicht zugegriffen werden kann. Ich habe bis jetzt 'try' und 'except' Blöcke in meinem Code, allerdings steht nach 'except' noch 'pass', da ich noch nicht weis, wie ich am besten damit umgehen soll. Da fehlt es mir noch etwas an Ideen/Erfahrung was eine sinnvolle Ausnahmebedingung wäre. Auch hier bin ich für Tipps dankbar.
-Der Pi braucht echt lange bis er hochgefahren ist, bis jetzt ist Raspberry-OS with Desktop installiert und die MusicBox startet automatisch in den Vollbildmodus. Den Start muss ich noch beschleunigen.
-Ausgeschaltet wird die Box über die Shutdown-Funktion, es ist aber nicht so toll den Stecker aus und wieder einzustecken, wenn man sie anschalten will. Dazu habe ich schon einen kleinen Taster bestellt, mit dem ich die Box dann starten will.
- Zum Schluss soll noch ein 3D-Druck Gehäuse kommen.
Ich habe euch hier noch ein paar Screenshots:
Hauptmenü/Startbildschirm:
Internetradio:
Playlistplayer:
Bluetooth:
Shutdown:
Die benötigte *csv-Datei muss so aufgebaut sein:
Sendername,URL
RadioBob,http://streams.radiobob.de/bob-live/mp3-192/mediaplayer
RockAntenne,http://mp3channels.webradio.rockantenne.de/alternative
Und zum Schluss noch der aktuelle Code:
#!/usr/bin/env python3
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.dropdown import DropDown
from kivy.uix.slider import Slider
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.core.window import Window
from InternetRadio import Radio
from MusicPlayer import PlaylistPlayer
from pathlib import Path
import subprocess
RADIOSENDER_PATH = Path('/media/hier/"gehts zur"/Senderliste.csv')
PLAYLIST_PATH = Path('/media/hier/"gehts zur"/Playlist/')
class MainScreen(Screen):
pass
class RadioScreen(Screen):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.radiostation = Radio()
def create_radioplayer(self):
try:
for sender in self.radiostation.read_station(RADIOSENDER_PATH):
sender_button = Button(text=sender, size_hint_y=None, height=44, background_color=(0,0,0,1))
sender_button.bind(on_release=lambda sender_button: self.ids.DropDownMenu.select(sender_button.text))
#
# Check to add only once the radiostation to DropDownMenu.
#
if self.ids.sender_button.text not in self.radiostation.read_station(RADIOSENDER_PATH):
self.ids.DropDownMenu.add_widget(sender_button)
else:
break
except:
pass
def set_sender(self, sendername):
self.radiostation.switch_station(sendername)
def radio_play(self):
self.radiostation.play_music()
def radio_stop(self):
self.radiostation.radio_stop()
def set_volume(self, volume):
self.radiostation.set_volume(volume)
class PlaylistScreen(Screen):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.player = PlaylistPlayer()
def load_playlist(self):
#
# Check to add only once the playlistobjects to DropDownMenu.
#
try:
check_new_folder = Path(f"{PLAYLIST_PATH}/{self.ids.playlist_button.text}")
if check_new_folder.is_dir():
pass
else:
for playlist in PLAYLIST_PATH.iterdir():
playlist_button = Button(text=playlist.name, size_hint_y=None, height=44, background_color=(0,0,0,1))
playlist_button.bind(
on_release=lambda playlist_button:
self.ids.DropDownPlaylist.select(playlist_button.text)
)
self.ids.DropDownPlaylist.add_widget(playlist_button)
except:
pass
def playlist_player(self, music_folder):
try:
music_path = Path(f"{PLAYLIST_PATH}/{music_folder}/")
self.player.addPlaylist(music_path)
except:
pass
def player_play(self):
self.player.play()
def player_next(self):
self.player.next()
def player_pause(self):
self.player.pause()
def player_previous(self):
self.player.previous()
def player_stop(self):
self.player.stop()
def player_volume(self, volume):
self.player.set_volume(volume)
class BluetoothScreen(Screen):
def bluetooth_on(self):
try:
subprocess.run(["rfkill unblock bluetooth"], shell=True, check=True)
except:
pass
def bluetooth_off(self):
try:
subprocess.run(["rfkill block bluetooth"], shell=True, check=True)
except:
pass
class ShutdownScreen(Screen):
def musicbox_off(self):
subprocess.run(["sudo shutdown -h 0"], shell=True, check=True)
class ImageButton(ButtonBehavior, Image):
pass
class VolumeSlider(Slider):
pass
class DropDownMenu(DropDown):
pass
class DropDownPlaylist(DropDown):
pass
kv = Builder.load_file("musicbox.kv")
class MusicBox(App):
def build(self):
screenmanager = ScreenManager()
screenmanager.add_widget(MainScreen(name='main'))
screenmanager.add_widget(RadioScreen(name='internetradio'))
screenmanager.add_widget(PlaylistScreen(name='playlist'))
screenmanager.add_widget(BluetoothScreen(name='bluetooth'))
screenmanager.add_widget(ShutdownScreen(name='shutdown'))
return screenmanager
if __name__ == '__main__':
subprocess.run(["rfkill block bluetooth"], shell=True, check=True)
# Automount won't work on test-system
try:
subprocess.run(["sudo mount -a"], shell=True, check=True)
except:
pass
Window.fullscreen = 'auto'
MusicBox().run()
Alles anzeigen
#!usr/bin/env python3
from vlc import Instance
START_VOLUME = 0
class PlaylistPlayer:
def __init__(self):
self.Player = Instance('--loop')
self.player = self.Player.media_list_player_new()
def addPlaylist(self, music_path):
self.playlist = []
for song in music_path.iterdir():
self.playlist.append(song)
self.mediaList = self.Player.media_list_new()
for song in self.playlist:
self.mediaList.add_media(self.Player.media_new(song))
self.player.set_media_list(self.mediaList)
self.player.get_media_player().audio_set_volume(START_VOLUME)
def play(self):
self.player.play()
def next(self):
self.player.next()
def pause(self):
self.player.pause()
def previous(self):
self.player.previous()
def stop(self):
self.player.stop()
def set_volume(self, volume):
self.player.get_media_player().audio_set_volume(volume)
Alles anzeigen
#!/usr/bin/env python3
import vlc
import csv
START_VOLUME = 0
class Radio():
def __init__(self):
self.create_player()
def read_station(self, radiosender_path):
self.radiostation = dict([])
with open(radiosender_path, newline='') as csvfile:
reader = csv.DictReader(csvfile, delimiter=',')
for row in reader:
self.radiostation[row['Sendername']] = row['URL']
return self.radiostation
def create_player(self):
self.instance = vlc.Instance('--input-repeat=-1', '--fullscreen')
self.player = self.instance.media_player_new()
self.player.audio_set_volume(START_VOLUME)
def switch_station(self, sendername):
url = self.radiostation[sendername]
media = self.instance.media_new(url)
media.get_mrl()
self.player.set_media(media)
def play_music(self):
self.player.play()
def radio_stop(self):
self.player.pause()
def set_volume(self, volume):
self.player.audio_set_volume(volume)
Alles anzeigen
# Filename: musicbox.kv
<MainScreen>:
name: "main"
GridLayout:
cols: 2
ImageButton:
id: button_radio
source: 'icon/radio.png'
pos_hint: {"x":0, "top":1}
on_press:
app.root.current = "internetradio"
root.manager.transition.direction = "up"
ImageButton:
id: button_playlist
source: 'icon/playlist.png'
pos_hint: {"x":0, "top":1}
on_press:
app.root.current = "playlist"
root.manager.transition.direction = "up"
ImageButton:
id: button_bluetooth
source: 'icon/bluetooth.png'
pos_hint: {"x":0, "top":1}
on_press:
app.root.current = "bluetooth"
root.manager.transition.direction = "up"
ImageButton:
id: button_shutdown
source: 'icon/shutdown.png'
pos_hint: {"x":0, "top":1}
on_press:
app.root.current = "shutdown"
root.manager.transition.direction = "up"
<RadioScreen>:
name: "internetradio"
GridLayout:
rows: 2
GridLayout:
cols: 2
Image:
size_hint_x: 0.1
source: 'icon/volume+.png'
allow_stretch: True
Slider:
id: slider
value_track: True
value_track_color: (1,0.2,0.7,1)
min:0
max:100
step: 1
on_value:
root.set_volume(int(self.value))
GridLayout:
cols: 4
ImageButton:
id: button_back_main
size_hint_x: 1
allow_stretch: True
source: 'icon/home_radio.png'
on_press:
app.root.current = "main"
root.radio_stop()
root.manager.transition.direction = "down"
ImageButton:
id: button_play
source: 'icon/play_radio.png'
on_press:
root.radio_play()
ImageButton:
id: button_pause
source: 'icon/break_radio.png'
on_press:
root.radio_stop()
ImageButton:
id: sender_button
text: ''
source: 'icon/liste_radio.png'
on_parent: DropDownMenu.dismiss()
on_release:
root.create_radioplayer()
DropDownMenu.open(self)
DropDownMenu:
id: DropDownMenu
on_select:
sender_button.text = '{}'.format(args[1])
root.set_sender(sender_button.text)
<PlaylistScreen>:
name: "playlist"
GridLayout:
rows: 3
GridLayout:
cols: 2
Image:
size_hint_x: 0.1
source: 'icon/volume+.png'
allow_stretch: True
Slider:
id: volume_slider
value_track: True
value_track_color: (0,245,255,1)
min:0
max:100
step: 1
on_value: root.player_volume(int(self.value))
GridLayout:
cols: 5
ImageButton:
id: button_previous
source: 'icon/backward.png'
on_press:
root.player_previous()
ImageButton:
id: button_play
source: 'icon/play_playlist.png'
on_press:
root.player_play()
ImageButton:
id: button_pause
source: 'icon/break_playlist.png'
on_press:
root.player_pause()
ImageButton:
id: button_stop
source: 'icon/stop_playlist.png'
on_press:
root.player_stop()
ImageButton:
id: button_next
source: 'icon/forward.png'
on_press:
root.player_next()
GridLayout:
cols: 3
ImageButton:
id: button_back_main
source: 'icon/home_playlist.png'
on_press:
root.player_stop()
app.root.current = "main"
root.manager.transition.direction = "down"
ImageButton:
id: playlist_button
text: ' '
source: 'icon/liste_playlist.png'
on_parent: DropDownPlaylist.dismiss()
on_release:
root.load_playlist()
DropDownPlaylist.open(self)
DropDownPlaylist:
id: DropDownPlaylist
on_select:
playlist_button.text = '{}'.format(args[1])
root.playlist_player(playlist_button.text)
root.player_stop()
<BluetoothScreen>:
name: "bluetooth"
GridLayout:
cols: 2
ImageButton:
id: button_on
source: 'icon/bluetooth.png'
on_press:
root.bluetooth_on()
ImageButton:
id: button_back_main
source: 'icon/home_bluetooth.png'
on_press:
app.root.current = "main"
root.manager.transition.direction = "down"
root.bluetooth_off()
<ShutdownScreen>:
name: "shutdown"
GridLayout:
cols: 2
ImageButton:
id: button_back_main
source: 'icon/home_shutdown.png'
on_press:
app.root.current = "main"
root.manager.transition.direction = "down"
ImageButton:
id: button_shutdown
source: 'icon/shutdown.png'
on_press: root.musicbox_off()
Alles anzeigen
So das war (ich hoffe) vorerst mal alles
Grüße
Dennis