#!/usr/bin/env python import time from colorsys import hsv_to_rgb from PIL import ImageFont, Image, ImageDraw, ImageStat import os import os.path from os import path import ST7789 as ST7789 from socketIO_client import SocketIO, LoggingNamespace import requests from io import BytesIO from numpy import mean import sys import logging # logging.getLogger('socketIO-client').setLevel(logging.DEBUG) # logging.basicConfig() import signal import RPi.GPIO as GPIO # get the path of the script # script_path = os.path.dirname(os.path.abspath( __file__ )) script_path = os.path.dirname(os.path.abspath(__file__)) # set script path as current directory os.chdir(script_path) def on_connect(): print("connect") def on_disconnect(): print("disconnect") img = Image.new("RGBA", (240, 240), color=(0, 0, 0, 25)) img = Image.open("/volumio/app/plugins/miscellanea/albumart/default.jpg") img = img.resize((WIDTH, HEIGHT)) draw = ImageDraw.Draw(img, "RGBA") draw.text((10, 200), "shuting down ...", font=font_m, fill=(255, 255, 255)) disp.display(img) def on_push_state(*args): img = Image.new("RGBA", (240, 240), color=(0, 0, 0, 25)) global status global service status = args[0]["status"] service = args[0]["service"] # Load albumcover or radio cover albumart = args[0]["albumart"] if len(albumart) == 0: # to catch a empty field on start albumart = "http://localhost:3000/albumart" if "http:" not in albumart: albumart = "http://localhost:3000" + args[0]["albumart"] print(albumart) response = requests.get(albumart) img = Image.open(BytesIO(response.content)) img = img.resize((WIDTH, HEIGHT)) # Light / Dark Symbols an bars, depending on background im_stat = ImageStat.Stat(img) im_mean = im_stat.mean mn = mean(im_mean) txt_col = (255, 255, 255) # bar_col = (255, 255, 255, 255) bar_bgcol = (200, 200, 200) bar_col = (255, 255, 255) dark = False if mn > 175: txt_col = (55, 55, 55) dark = True # bar_col = (100, 100, 100, 225) bar_bgcol = (255, 255, 255) bar_col = (100, 100, 100) if mn < 80: txt_col = (200, 200, 200) # paste button symbol overlay in light/dark mode if status == "play": if dark is False: img.paste(pause_icons, (0, 0), pause_icons) else: img.paste(pause_icons_dark, (0, 0), pause_icons_dark) else: if dark is False: img.paste(play_icons, (0, 0), play_icons) else: img.paste(play_icons_dark, (0, 0), play_icons_dark) draw = ImageDraw.Draw(img, "RGBA") top = 7 if "artist" in args[0]: x1 = 20 w1, y1 = draw.textsize(args[0]["artist"], font_m) x1 = x1 - 20 if x1 < (WIDTH - w1 - 20): x1 = 0 if w1 <= WIDTH: x1 = (WIDTH - w1) // 2 draw.text((x1, top), args[0]["artist"], font=font_m, fill=txt_col) top = 35 if "album" in args[0]: if args[0]["album"] is not None: x2 = 20 w2, y2 = draw.textsize(args[0]["album"], font_s) x2 = x2 - 20 if x2 < (WIDTH - w2 - 20): x2 = 0 if w2 <= WIDTH: x2 = (WIDTH - w2) // 2 draw.text((x2, top), args[0]["album"], font=font_s, fill=txt_col) if "title" in args[0]: x3 = 20 w3, y3 = draw.textsize(args[0]["title"], font_l) x3 = x3 - 20 if x3 < (WIDTH - w3 - 20): x3 = 0 if w3 <= WIDTH: x3 = (WIDTH - w3) // 2 # thin border # shadowcolor = "black" # draw.text((x3-1, 105), args[0]['title'], font=font_l, # fill=shadowcolor) # draw.text((x3+1, 105), args[0]['title'], font=font_l, # fill=shadowcolor) # draw.text((x3, 105-1), args[0]['title'], font=font_l, # fill=shadowcolor) # draw.text((x3, 105+1), args[0]['title'], font=font_l, # fill=shadowcolor) draw.text( (x3, 105), args[0]["title"], font=font_l, fill=txt_col ) # fill by mean # volume bar vol_x = int((float(args[0]["volume"]) / 100) * (WIDTH - 33)) # draw.rectangle((5, 184, WIDTH-34, 184+8), (255, 255, 255, 145)) draw.rectangle((5, 184, WIDTH - 34, 184 + 8), bar_bgcol) # background draw.rectangle((5, 184, vol_x, 184 + 8), bar_col) # time bar if "duration" in args[0]: duration = args[0]["duration"] # seconds if duration != 0: if "seek" in args[0]: seek = args[0]["seek"] # time elapsed seconds print("seek: ", seek) if seek != 0 and seek is not None: el_time = int(float(seek) / 1000) du_time = int(float(args[0]["duration"])) dur_x = int((float(el_time) / float(du_time)) * (WIDTH - 10)) # draw.rectangle((5, 222, WIDTH-5, 222 + 12), (255, 255, 255, 145)) draw.rectangle( (5, 222, WIDTH - 5, 222 + 12), bar_bgcol ) # background draw.rectangle((5, 222, dur_x, 222 + 12), bar_col) # Draw the image on the display hardware disp.display(img) socketIO = SocketIO("localhost", 3000) socketIO.on("connect", on_connect) # Create ST7789 LCD display class. disp = ST7789.ST7789( rotation=90, # Needed to display the right way up on Pirate Audio port=0, # SPI port cs=1, # SPI port Chip-select channel dc=9, # BCM pin used for data/command backlight=13, spi_speed_hz=80 * 1000 * 1000, ) # Initialize display. disp.begin() WIDTH = 240 HEIGHT = 240 font_s = ImageFont.truetype(script_path + "/fonts/Roboto-Medium.ttf", 20) font_m = ImageFont.truetype(script_path + "/fonts/Roboto-Medium.ttf", 24) font_l = ImageFont.truetype(script_path + "/fonts/Roboto-Medium.ttf", 30) play_icons = Image.open("images/controls-play.png").resize((240, 240)) play_icons_dark = Image.open("images/controls-play-dark.png").resize((240, 240)) pause_icons = Image.open("images/controls-pause.png").resize((240, 240)) pause_icons_dark = Image.open("images/controls-pause-dark.png").resize((240, 240)) # vol_icons = Image.open(script_path + '/images/controls-vol.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") # vol_icons_dark = Image.open(script_path + '/images/controls-vol-dark.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") # bt_back = Image.open(script_path + '/images/bta.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") # ap_back = Image.open(script_path + '/images/airplay.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") # jp_back = Image.open(script_path + '/images/jack.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") # sp_back = Image.open(script_path + '/images/spotify.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") # sq_back = Image.open(script_path + '/images/squeeze.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") BUTTONS = [5, 6, 16, 20] # BUTTONS = [16] LABELS = ["A", "B", "X", "Y"] # Set up RPi.GPIO with the "BCM" numbering scheme GPIO.setmode(GPIO.BCM) # Buttons connect to ground when pressed, so we should set them up # with a "PULL UP", which weakly pulls the input signal to 3.3V. GPIO.setup(BUTTONS, GPIO.IN, pull_up_down=GPIO.PUD_UP) def handle_button(pin): label = LABELS[BUTTONS.index(pin)] print("Button press detected on pin: {} label: {}".format(pin, label)) if pin == 5: # print('Play/pause') print(status, service) if (status == "play") and (service == "webradio"): print("stop - webradio") socketIO.emit("stop") elif status == "play": print("pause") socketIO.emit("pause") else: print("play") socketIO.emit("play") if pin == 6: print("volDown") socketIO.emit("volume", "-") if pin == 16: print("Next") socketIO.emit("next") if pin == 20: print("volUp") socketIO.emit("volume", "+") def main(): while True: print("In main") for pin in BUTTONS: GPIO.add_event_detect(pin, GPIO.FALLING, handle_button, bouncetime=100) disp.set_backlight(True) # mit socket verbinden socketIO.on("pushState", on_push_state) # get initial state socketIO.emit("getState", "", on_push_state) socketIO.wait() # socketIO.wait_for_callbacks(seconds=1) # time.sleep(1) time.sleep(0.01) if __name__ == "__main__": try: main() except KeyboardInterrupt: img = Image.new("RGB", (240, 240), color=(0, 0, 0)) draw = ImageDraw.Draw(img) draw.rectangle((0, 0, 240, 240), (0, 0, 0)) disp.display(img) disp.set_backlight(False) pass