From 99bc13c8bb389c30601a33f3f2ad73e9433d5657 Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Mon, 18 Mar 2024 16:50:50 +0100 Subject: [PATCH] Use SIGINT to end script, systemd service. Use static/dynamic UI --- mpdlisten.py | 85 ++++++++++++++++++++++++++++++++++++--------- mpdlistenpy.service | 12 +++++++ 2 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 mpdlistenpy.service diff --git a/mpdlisten.py b/mpdlisten.py index 8f0f0ae..4575233 100644 --- a/mpdlisten.py +++ b/mpdlisten.py @@ -6,6 +6,7 @@ import musicpd from math import floor from os import environ +import signal import sys from time import sleep # Relay @@ -14,11 +15,12 @@ import RPi.GPIO as GPIO from luma.core.interface.serial import i2c from luma.core.render import canvas from luma.oled.device import ssd1306 +from PIL import Image, ImageDraw # MPD config off_delay = 3 -mpd_host='localhost' -mpd_port=6600 +mpd_host=None +mpd_port=None mpd_passwd = None mpd_states = ['play', 'pause', 'stop', 'unknown'] @@ -31,6 +33,9 @@ GPIO.setup(RELAIS_1_GPIO, GPIO.OUT) # GPIO Assign mode serial = i2c(port=1, address=0x3C) device = ssd1306(serial) +# GUI config +number_of_btn = 4 + # TODO: get ctrlc state ctrlc_pressed = False @@ -80,24 +85,59 @@ if 'MPD_HOST' in environ: if 'MPD_PORT' in environ: mpd_port = environ['MPD_PORT'] +static_ui = None +menu_mode = "playback" + + def sectomin(sec:str): minute = 0 minute = floor(float(sec)/60) second = round(float(sec)-minute*60) return "{}:{}".format(str(minute), str(second)) + +def generate_static_ui(mode:str): + # ~ if mode == "playback": + btn_width = floor((device.width-3) / 4) + btn_height = 8 + menu_line_width = 1 + menu_bar_y = device.height - btn_height - menu_line_width + + im = Image.new(mode='1', size=(device.width, device.height)) + draw = ImageDraw.Draw(im) + # TODO: draw static UI only once as a PIL image + draw.rectangle(device.bounding_box, outline="white", fill="black") + draw.line([(0, menu_bar_y),(device.width, menu_bar_y)], width=menu_line_width) + # TODO : use loop + i = 1 + while i < number_of_btn: + draw.line([((btn_width+1)*i, menu_bar_y),((btn_width+1)*i, device.height)], width=menu_line_width, fill="white") + i+=1 + # Draw icons + draw.regular_polygon(bounding_circle=(((btn_width+1)*0.5), (menu_bar_y + btn_height / 2), 3), n_sides=3, rotation=90, fill="white") + draw.regular_polygon(bounding_circle=(((btn_width+1)*1.5), (menu_bar_y + btn_height / 2), 3), n_sides=3, rotation=270, fill="white") + draw.regular_polygon(bounding_circle=(((btn_width+1)*2.5), (menu_bar_y + btn_height / 2), 3), n_sides=4, rotation=0, fill="white") + draw.regular_polygon(bounding_circle=(((btn_width+1)*3.5), (menu_bar_y + btn_height / 2), 3), n_sides=3, rotation=180, fill="white") + + return im + + def update_display(device, currentsong, status): - # ~ print(currentsong['artist']) - # ~ print(currentsong['title']) - # ~ print(status['time']) - # ~ print("{}/{}".format(status['time'], sectomin(status['duration']))) - with canvas(device) as draw: - draw.rectangle(device.bounding_box, outline="white", fill="black") - draw.text((10, 5), currentsong['artist'], fill="white") - draw.text((10, 18), currentsong['title'], fill="white") - draw.text((10, 31), currentsong['album'], fill="white") - draw.text((10, 44), "{}/{}".format(status['time'], sectomin(status['duration'])), fill="white") - draw.regular_polygon(bounding_circle=(14, 55, 8), n_sides=3, rotate=90, fill="white") + # We want to display 4 buttons at the bottom of the screen + btn_width = floor((device.width-3) / 4) + btn_height = 8 + menu_line_width = 1 + menu_bar_y = device.height - btn_height - menu_line_width + # Draw dynamic UI + ui = static_ui + draw = ImageDraw.Draw(ui) + draw.text((10, 2), currentsong['artist'], fill="white") + draw.text((10, 14), currentsong['title'], fill="white") + draw.text((10, 26), currentsong['album'], fill="white") + if 'elapsed' in status: + draw.text((10, 38), "{}/{}".format(sectomin(status['elapsed']), sectomin(status['duration'])), fill="white") + device.display(ui) + def main(args): previous_sond_id = None @@ -107,8 +147,10 @@ def main(args): client = musicpd.MPDClient() if mpd_passwd is not None: client.pwd = mpd_passwd - client.host = mpd_host - client.port = mpd_port + if mpd_host is not None: + client.host = mpd_host + if mpd_port is not None: + client.port = mpd_port client.mpd_timeout = 5 try: client.connect() @@ -117,6 +159,8 @@ def main(args): print("Check host and port are correct.") # ~ print(client.status()) # duration, elapsed, volume, repeat, random, single # ~ print(client.currentsong()) # artist, title, album + global static_ui + static_ui = generate_static_ui(menu_mode) while ctrlc_pressed is False: mpd_status = client.status() if len(mpd_status): @@ -149,9 +193,18 @@ def main(args): previous_state = play_state sleep(1) update_display(device, mpd_client_currentsong, mpd_client_status) + device.cleanup() client.disconnect() return 0 +def signal_handler(sig, frame): + global ctrlc_pressed + print('You pressed Ctrl+C!') + ctrlc_pressed = True + # ~ sys.exit(0) + + if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) \ No newline at end of file + signal.signal(signal.SIGINT, signal_handler) + sys.exit(main(sys.argv[1:])) diff --git a/mpdlistenpy.service b/mpdlistenpy.service new file mode 100644 index 0000000..3e0dcb9 --- /dev/null +++ b/mpdlistenpy.service @@ -0,0 +1,12 @@ +[Unit] +Description=Mpd listen python script +After=network.target + +[Service] +WorkingDirectory=/home/music/mpdlisten +ExecStart=/home/music/mpdlisten/mpdlisten.py +KillSignal=SIGINT +Restart=always + +[Install] +WantedBy=default.target \ No newline at end of file