disclaimer: I haven’t looked at your code, but I hope you aren’t managing queue state yourself? You can use the Volumio API’s next
and prev
functions directly?
@AxLED I’ve edited the line as you suggest, and everything now works, with correct artist, track and albumart information. Since I replied to your earlier post, I rebooted my pi, and volumio status
now gives more sensible values for title, artist albumart etc when using Spotify Connect2 (but position is still undefined, so this fix is necessary):
{
“status”: “play”,
“title”: “Ceremony”,
“artist”: “New Order”,
“album”: “Substance”,
“albumart”: “https://i.scdn.co/image/ab67616d0000b273847620cdf72cd76743159265”,
“uri”: “spotify:track:5CRPGdCSOfstyTVK9Ja5EQ”,
“trackType”: “spotify”,
“seek”: 16000,
“duration”: 264,
“samplerate”: “44.1 KHz”,
“bitdepth”: “16 bit”,
“channels”: 2,
“consume”: false,
“volume”: 100,
“dbVolume”: null,
“mute”: false,
“disableVolumeControl”: true,
“stream”: false,
“updatedb”: false,
“volatile”: true,
“service”: “volspotconnect2”
}
@ashthespy
i use the prev next
from voulumio API, but i display some information in the prev/next menu of pirate audio tft and thats not working when there is no position.
Sample (what i diplay):
10/18 (song 10 from 18 in queue)
prev / next (information which submenu is opened)
songtitle (title from queue with number from line 1)
AxLED
That makes sense, sorry for the noise!
Hi,
thank you for your beautiful plugin. I finally got it working with the fix from reply 158. Would be nice to have the skipping possible even with Spotify Connect. But I have a problem with the screen timeout. If I get it right, the screen should turn off after 15 minutes or whatever is configured. Mine stays on forever, whether playing music or not. Also using the plug-in’s shutdown function didn’t turn the screen off (actually I don’t know if the pi was shut down or not).
Do you have any clue what could be going on?
Thank you again
thanks for your response. About your questions:
Skipping Spotify: Which of the 2 available spotify plugins are you using? I developed and testet only for one of them and with a trial account of spotifiy (because it is not worth the monthly fee to me).
Screen: The screen will only turn off, if you choose sleeptimer on pirate audio menu. If you shutdown the pi via pirate audio menu, the screen turn off until shutdown. After shutdown and while connected to power supply the screen turns back on (from this state there is no way to control the screen). I am using my pi zero as portable solution with adafruit powerboost circuit, which cuts the power 10 seconds after complete shutdown.
AxLED
Thanks for getting back so quickly. I have made a PR that fixes Spotify connect including skipping.
So the screen is not turned off based on time, e.g. since Volumio last entered stop state? But what is the time setting in the plugin config then for?
nice that you opened a PR right away, unfortunately it clashes with my current ideas and development, so I will not accept this one. But I will put your changes on the ToDo list.
To your 2nd question:
The minutes settings under pirate audio plugin configuration are for the sleep timer.
When you select the sleep timer on pirate audio hat following happens:
- Display turns off
- Pi shuts down after x minutes (x=as defined in plugin config)
AxLED
Hi AxLED,
I just did a fresh setup on a PI3 with the Pirate Audio Hat connected.
Following your instructions it is working seamlessly on v3.213 out of the box after enabling the plugin and doing a restart.
BR
Josef
Hi Drew,
your case looks very nice. Could you share the model?
Hi AxLED,
I shutdown the Pi via a nightly crontab (/sbin/shutdown -H now).
Is there some python command I could use 5 minutes before the shutdown cron script to switch off the screen/ maybe switch off the plugin, and keep the screen off until next boot? - even though the power supply is still connected?
Another idea is to cut the pi power supply 30 minutes after shutdown using a cheap mains timer switch.
You can switch off the screen backlight (which basically turns it off) with:
disp.set_backlight(False)
Where disp is the display class (already set up in the Pirate Audio plugin script):
disp = ST7789.ST7789(
height=240, #v0.0.6
width=240, #v0.0.6
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,
offset_left=0, #v0.0.6
offset_top=0 #v0.0.6
)
I posted a link to a quick and dirty screensaver script earlier in this thread (post #148) which you could modify for your needs perhaps.
Thanks Darren,
I tried modifying your script and using disp.set_backlight(False), which works, but after shut-down the previous (last-used) ‘image’, reappears on the Pirate Audio screen as before
(I presume this is a ‘screen buffer’?)
As a work-around I modified your script further, to display a black square on the screen prior to shutdown/poweroff.
Adding:
from PIL import Image, ImageDraw
buffer = Image.new(“RGB”, (240, 240))
draw = ImageDraw.Draw(buffer)
draw.rectangle((0, 0, 240, 240), (0, 0, 0))
disp.display(buffer)
… replacing code after disp section in your version.
I added a crontab:
58 23 * * * /data/pirateoff.sh
59 23 * * * /sbin/shutdown -H now
[Ref: Drawing squares: https://github.com/pimoroni/st7789-python/blob/master/examples/320x240.py]
[Ref: Discussion on Pi poweroff/shutdown: What is the Difference between the "shutdown" and "halt" Commands? - Raspberry Pi Forums]
Any other suggestions for turning off the Pirate Audio backlight or power after pi shutdown?
as mentioned some posts before (see 166)
Screen: The screen will only turn off, if you choose sleeptimer on pirate audio menu. If you shutdown the pi via pirate audio menu, the screen turn off until shutdown. After shutdown and while connected to power supply the screen turns back on (from this state there is no way to control the screen). I am using my pi zero as portable solution with adafruit powerboost circuit, which cuts the power 10 seconds after complete shutdown.
AxLED
Hi,
first of all thank you AxLED and all volumio developers for your amazing work .
I really like what you have created.
My goal is to build a standalone music player.
I am using a raspizero with a pirateaudio headphone module and a UPS module.
I made some modifications to the display.py module to handle seek, next prev and backlight in player mode.
With this modifications it is possible to:
seek + or - by holding down the B or Y buttons
play next or previous track by double clicking on B or Y buttons
change the backlight by holding down the X button, in particular the backlight will be decreased by 20/100 every 0.75 seconds that the user holds down the X button.
I hope this will help someone who asked about the backlight capability.
I used the SW version of PWM so the backlight is not very stable, I will improve it using HW timers.
Greetings
#!/usr/bin/env python3
import time
from PIL import ImageFont, Image, ImageDraw, ImageStat, ImageFilter
from PIL import ImageFilter # v0.0.4
import os
import os.path
#import ST7789 as ST7789
import ST7789 #v0.0.6
from socketIO_client import SocketIO
import requests
from io import BytesIO
from numpy import mean
import sys
import signal
import RPi.GPIO as GPIO
import math
import json
from signal import *
from time import strftime, gmtime # v.0.0.4
import time # v.0.0.4
# import logging
# logging.getLogger('socketIO-client').setLevel(logging.DEBUG)
# logging.basicConfig()
# get the path of the script
script_path = os.path.dirname(os.path.abspath(__file__))
# set script path as current directory
os.chdir(script_path)
# socketIO = SocketIO('localhost', 3000)
# Create ST7789 LCD display class.
disp = ST7789.ST7789(
height=240, #v0.0.6
width=240, #v0.0.6
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=None,
spi_speed_hz=80 * 1000 * 1000,
offset_left=0, #v0.0.6
offset_top=0 #v0.0.6
)
# read json file (plugin values)
with open('/data/configuration/miscellanea/pirateaudio/config.json', 'r') as myfile:
data = myfile.read()
obj = json.loads(data) # parse file
# read json file (volumio language)
with open('/data/configuration/miscellanea/appearance/config.json', 'r') as mylangfile:
data_lang = mylangfile.read()
obj_lang = json.loads(data_lang) # parse file
langcode = obj_lang['language_code']['value']
langpath = '/data/plugins/miscellanea/pirateaudio/i18n/strings_' + langcode + '.json'
if os.path.exists(langpath) is False: # change to en as default language
langpath = '/data/plugins/miscellanea/pirateaudio/i18n/strings_en.json'
# read json file (language file for translation)
with open(langpath, 'r') as mytransfile:
data_trans = mytransfile.read()
obj_trans = json.loads(data_trans) # parse file
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)
font_fas = ImageFont.truetype(script_path + '/fonts/FontAwesome5-Free-Solid.otf', 28)
bg_default = Image.open('images/default.jpg').resize((240, 240))
albumart, artist, album, title, img_check = '', '', '', '', ''
mode = 'player'
title_queue, len_queue = [], 0 # v.0.0.4
position = '' # v.0.0.4
nav_array_name, nav_array_uri, nav_array_type, nav_array_service = [], [], [], []
marker, listmax, liststart, listresult = 0, int(obj['listmax']['value']), 0, 0
BUTTONS = [5, 6, 16, obj['gpio_ybutton']['value']]
BACKLIGHT = 13
pwm_value = 100
# LABELS = ['A', 'B', 'X', 'Y']
GPIO.setmode(GPIO.BCM) # Set up RPi.GPIO with the "BCM" numbering scheme
GPIO.setup(BACKLIGHT,GPIO.OUT)
pi_pwm = GPIO.PWM(BACKLIGHT,60)
pi_pwm.start(0)
pi_pwm.ChangeDutyCycle(pwm_value)
# exit function (even is service is stopped)
def clean(*args):
#disp.set_backlight(False)
GPIO.cleanup(BACKLIGHT)
GPIO.cleanup(BUTTONS) # v0.0.4
sys.exit(0)
for sig in (SIGABRT, SIGILL, SIGINT, SIGSEGV, SIGTERM):
signal(sig, clean)
# exit function (even is service is stopped)
def on_connect():
# print('connect')
start_time = time.time() # debug, time of code execution
socketIO.on('pushState', on_push_state)
socketIO.emit('getState', '', on_push_state)
socketIO.on('pushBrowseSources', on_push_browsesources)
socketIO.on('pushBrowseLibrary', on_push_browselibrary)
socketIO.on('pushQueue', on_push_queue)
socketIO.emit('getQueue', on_push_queue)
# print("on_connect--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def on_disconnect():
display_stuff('bg_default', obj_trans['DISPLAY']['LOSTCONNECTION'], 0, 0, 'info')
def navigation_handler():
# start_time = time.time() # debug, time of code execution
global mode, nav_array_name, nav_array_uri, nav_array_type, marker, liststart, listresult
if mode == 'player':
mode = 'menu'
emit_action = ['setSleep', {'enabled': 'true', 'time': strftime("%H:%M", gmtime(obj['sleeptimer']['value']*60))}]
nav_array_name = [obj_trans['DISPLAY']['MUSICSELECTION'], 'Sleeptimer ' + str(obj['sleeptimer']['value']) + 'M', obj_trans['DISPLAY']['SHUTDOWN'], obj_trans['DISPLAY']['REBOOT']]
nav_array_uri = ['', emit_action, 'shutdown', 'reboot']
nav_array_type = ['', 'emit', 'emit', 'emit']
listresult = 6
display_stuff('bg_default', nav_array_name, marker, liststart)
else:
print('else navigation_handler() eingetreten')
# print("navigation_handler--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def on_push_browsesources(*args):
# start_time = time.time() # debug, time of code execution
global listresult # v.0.0.4 removed some globals, as thex not needed here
if mode == 'navigation': # v.0.0.4 added, to make sure this getting not displayed on_connect
listresult = len(args[0])
i = 0
append_n = nav_array_name.append # to avoid dots in for loop
append_u = nav_array_uri.append
for i in range(listresult):
append_n(args[0][i]['name'])
append_u(args[0][i]['uri'])
display_stuff('bg_default', nav_array_name, marker, 0)
# print("on_push_browsesources--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def on_push_browselibrary(*args):
# start_time = time.time() # debug, time of code execution
global listresult # v.0.0.4 removed some globals, as thex not needed here
reset_variable('navigation')
listresult = len(args[0]['navigation']['lists'][0]['items']) # v.0.0.4 code cleaning
i = 0
if listresult > 0: # we have item entries
append_s = nav_array_service.append # to avoid dots in for loop
append_t = nav_array_type.append
append_n = nav_array_name.append
append_u = nav_array_uri.append
for i in range(listresult):
if 'service' in args[0]['navigation']['lists'][0]['items'][i]: # v.0.0.4
append_s(args[0]['navigation']['lists'][0]['items'][i]['service']) # v.0.0.4
if 'title' in args[0]['navigation']['lists'][0]['items'][i]: # v.0.0.4
append_n(args[0]['navigation']['lists'][0]['items'][i]['title'])
append_t(args[0]['navigation']['lists'][0]['items'][i]['type'])
if 'uri' in args[0]['navigation']['lists'][0]['items'][i]: # v.0.0.4 spotify check
append_u(args[0]['navigation']['lists'][0]['items'][i]['uri']) # v.0.04
display_stuff('bg_default', nav_array_name, marker, liststart)
elif listresult == 0: # we have no item entries
display_stuff('bg_default', obj_trans['DISPLAY']['EMPTY'], marker, liststart)
# print("on_push_browselibrary--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def reset_variable(varmode):
# start_time = time.time() # debug, time of code execution
global mode, nav_array_service, nav_array_name, nav_array_uri, nav_array_type, marker, liststart, img_check, albumart
mode = varmode
del nav_array_name[:] # v.0.0.4 del is cleaner than = []
del nav_array_uri[:]
del nav_array_type[:]
del nav_array_service[:]
marker, liststart = 0, 0
img_check, albumart = '', '' # reset albumart so display gets refreshed
# print("reset_variable--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def sendtodisplay(img):
# start_time = time.time() # debug, time of code execution
disp.display(img)
# time.sleep(0.1) # ohne sleep 82% CPU, sleep: 0.5 = 40%, 0.25 = 53%, 0.1 = 70%
# print("sendtodisplay--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def display_stuff(picture, text, marked, start, icons='nav'): # v.0.0.4 test for better performance
# start_time = time.time() # debug, time of code execution
global img3, listmax # v.0.0.4
i = 0
if picture == 'bg_default':
img3 = bg_default.copy()
else:
img3 = Image.open(picture).convert('RGBA') # v.0.0.4
draw3 = ImageDraw.Draw(img3, 'RGBA')
if isinstance(text, list): # check if text is array
result = len(text) # count items of list/array
totaltextheight = 0
# Loop for finding out the sum of textheight for positioning, only text to display
listbis = start + listmax
if listbis > result:
listbis = result
for i in range(start, listbis): # v.0.0.4 range max werteliste
len1, hei1 = draw3.textsize(text[0+i], font=font_m)
totaltextheight += hei1
i = 0
y = (HEIGHT // 2) - (totaltextheight // 2)
# Loop for creating text to display
for i in range(start, listbis): # v.0.0.4
len1, hei1 = draw3.textsize(text[0+i], font=font_m)
x2 = (WIDTH - len1)//2
if x2 < 0: # v.0.0.4 dont center text if to long
x2 = 0
if i == marked:
draw3.rectangle((x2, y + 2, x2 + len1, y + hei1), (255, 255, 255))
draw3.text((x2, y), text[0+i], font=font_m, fill=(0, 0, 0))
else:
draw3.text((x2 + 3, y + 3), text[0+i], font=font_m, fill=(15, 15, 15)) # shadow v.0.0.4
draw3.text((x2, y), text[0+i], font=font_m, fill=(255, 255, 255))
y += hei1
else:
result = 1 # needed for right pageindex
len1, hei1 = draw3.textsize(text, font=font_m)
x2 = (WIDTH - len1)//2
y2 = (HEIGHT - hei1)//2
draw3.rectangle((x2, y2, x2 + len1, y2 + hei1), (255, 255, 255))
draw3.text((x2, y2), text, font=font_m, fill=(0, 0, 0))
# draw symbols
if icons == 'nav': # v.0.0.4
draw3.text((0, 50), u"\uf14a", font=font_fas, fill=(255, 255, 255)) # Fontawesome symbols ok
draw3.text((210, 50), u"\uf151", font=font_fas, fill=(255, 255, 255)) # Fontawesome symbols up
draw3.text((0, 170), u"\uf0e2", font=font_fas, fill=(255, 255, 255)) # Fontawesome symbols back
draw3.text((210, 170), u"\uf150", font=font_fas, fill=(255, 255, 255)) # Fontawesome symbols down
elif icons == 'info':
draw3.text((10, 10), u"\uf05a", font=font_fas, fill=(255, 255, 255)) # Fontawesome symbols info
elif icons == 'seek':
draw3.text((210, 50), u"\uf04e", font=font_fas, fill=(255, 255, 255)) # Fontawesome symbols forward
draw3.text((0, 170), u"\uf0e2", font=font_fas, fill=(255, 255, 255)) # Fontawesome symbols back
draw3.text((210, 170), u"\uf04a", font=font_fas, fill=(255, 255, 255)) # Fontawesome symbols backward
page = int(math.ceil((float(marked) + 1)/float(listmax)))
pages = int(math.ceil(float(result)/float(listmax)))
if pages != 1: # only show index if more than one site
pagestring = str(page) + '/' + str(pages)
len1, hei1 = draw3.textsize(pagestring, font=font_m)
x2 = (WIDTH - len1)//2
draw3.text((x2, HEIGHT - hei1), pagestring, font=font_m, fill=(255, 255, 255))
sendtodisplay(img3)
# print("displaystuff--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
# position in code is important, so display_stuff works v.0.0.4
display_stuff('bg_default', obj_trans['DISPLAY']['WAIT'], 0, 0, 'info')
socketIO = SocketIO('localhost', 3000)
def seeking(direction):
# start_time = time.time() # debug, time of code execution
global seek, duration
step = 60000 # 60 seconds
if direction == '+':
if int(float((seek + step)/1000)) < duration:
seek += step
socketIO.emit('seek', int(float(seek/1000)))
display_stuff('bg_default', [obj_trans['DISPLAY']['SEEK'], strftime("%M:%S", gmtime(int(float(seek/1000)))) + ' / ' + strftime("%M:%S", gmtime(duration))], 0, 0, 'seek')
else:
if int(float((seek - step)/1000)) > 0:
seek -= step
socketIO.emit('seek', int(float(seek/1000)))
display_stuff('bg_default', [obj_trans['DISPLAY']['SEEK'], strftime("%M:%S", gmtime(int(float(seek/1000)))) + ' / ' + strftime("%M:%S", gmtime(duration))], 0, 0, 'seek')
# print("seeking--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def prevnext(direction):
# start_time = time.time() # debug, time of code execution
global position
if direction == 'prev':
position -= 1
else:
position += 1
if position > len_queue - 1: # set position to first entry to loop through playlist infinite
position = 0
elif position < 0: # set position to last entry to loop through playlist infinite
position = len_queue - 1
display_stuff('bg_default', [str(position + 1) + '/' + str(len_queue), obj_trans['DISPLAY']['PREVNEXT'], title_queue[position]], 1, 0, 'seek')
socketIO.emit('stop')
socketIO.emit('play', {"value": position})
# print("prevnext--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def on_push_queue(*args):
global title_queue, len_queue
# reset variables first
del title_queue[:]
len_queue = 0
if len(args[0]) != 0:
len_queue = len(args[0])
append_t = title_queue.append # to avoid dots in for loop
for i in range(len_queue):
append_t(args[0][i]['name'])
def on_push_state(*args):
# start_time = time.time() # debug, time of code execution
global img, img2, dark, txt_col, str_col, bar_bgcol, bar_col, status, service, volume, albumart, img_check, mode, seek, duration, position
def f_textsize(text, fontsize):
w1, y1 = draw.textsize(text, fontsize)
return w1
def f_drawtext(x, y, text, fontstring, fillstring):
draw.text((x, y), text, font=fontstring, fill=fillstring)
def f_x1(textwidth):
if textwidth <= WIDTH:
x1 = (WIDTH - textwidth)//2
else:
x1 = 0
return x1
def f_content(field, fontsize, top, shadowoffset=1):
if field in args[0]:
if args[0][field] is not None:
w1 = f_textsize(args[0][field], fontsize)
x1 = f_x1(w1)
f_drawtext(x1 + shadowoffset, top + shadowoffset, args[0][field], fontsize, str_col) # shadow
f_drawtext(x1, top, args[0][field], fontsize, txt_col)
# return args[0][field]
volume = int(args[0]['volume'])
position = args[0]['position'] # v.0.0.4
if mode == 'player':
#status = args[0]['status'].encode('ascii', 'ignore')
status = args[0]['status'] # v0.0.6
#service = args[0]['service'].encode('ascii', 'ignore')
service = args[0]['service'] # v0.0.6
# if args[0]['albumart'].encode('ascii', 'ignore') != albumart: # Load albumcover or radio cover (and only if changes)
if args[0]['albumart'].encode('ascii', 'ignore').decode('utf-8') != albumart: #v0.0.6 # Load albumcover or radio cover (and only if changes)
# albumart = args[0]['albumart'].encode('ascii', 'ignore')
albumart = args[0]['albumart'].encode('ascii', 'ignore').decode('utf-8') #v0.0.6
print('Albumart', albumart) #v0.0.6
albumart2 = albumart
if len(albumart2) == 0: # to catch a empty field on start
albumart2 = 'http://localhost:3000/albumart'
if 'http' not in albumart2:
albumart2 = 'http://localhost:3000'+args[0]['albumart']
response = requests.get(albumart2)
# img = Image.open(BytesIO(response.content)) # v.0.04 gab bei spotify probleme
img = Image.open(BytesIO(response.content)).convert('RGBA') # v.0.04 gab bei spotify probleme
img = img.resize((WIDTH, HEIGHT))
img = img.filter(ImageFilter.BLUR) # Blur
draw = ImageDraw.Draw(img, 'RGBA')
# draw = ImageDraw.Draw(img) # v.0.04 gab bei spotify probleme
img2 = img.copy()
# Light / Dark Symbols and bars, depending on background
im_stat = ImageStat.Stat(img)
im_mean = im_stat.mean
mn = mean(im_mean)
txt_col = (255, 255, 255)
str_col = (15, 15, 15) # v0.0.4 needed for shadow
bar_bgcol = (200, 200, 200)
bar_col = (255, 255, 255)
dark = False
if mn > 175:
txt_col = (55, 55, 55)
str_col = (200, 200, 200) # v0.0.4 needed for shadow
dark = True
bar_bgcol = (255, 255, 255)
bar_col = (100, 100, 100)
if mn < 80:
txt_col = (200, 200, 200)
else: # if albumart didnt change, copy the last unpasted version
img = img2.copy()
draw = ImageDraw.Draw(img, 'RGBA')
# paste button symbol overlay in light/dark mode
if status == 'play':
# draw.text((4, 53), u"\uf04C", font=font_fas, fill=txt_col) # Fontawesome symbol pause
f_drawtext(4, 53, u"\uf04C", font_fas, txt_col)
else:
# draw.text((4, 53), u"\uf04b", font=font_fas, fill=txt_col) # Fontawesome symbol play
f_drawtext(4, 53, u"\uf04b", font_fas, txt_col)
# draw.text((210, 53), u"\uf0c9", font=font_fas, fill=txt_col) # Fontawesome symbol menu
f_drawtext(210, 53, u"\uf0c9", font_fas, txt_col)
# draw.text((210, 174), u"\uf028", font=font_fas, fill=txt_col) # Fontawesome symbol speaker
f_drawtext(210, 174, u"\uf028", font_fas, txt_col)
f_content('artist', font_m, 7, 2) # 'artist', font_m
f_content('album', font_m, 35, 2)
f_content('title', font_l, 105, 2) # falscher top wert
# volume bar
vol_x = int((float(args[0]['volume'])/100)*(WIDTH - 33))
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]:
if 'seek' in args[0] and args[0]['seek'] is not None: # v0.0.4 sometime seek = null or None
seek = args[0]['seek'] # time elapsed seconds
# if seek != 0: # v0.0.4 seek=0 is ok to show
el_time = int(float(args[0]['seek'])/1000)
du_time = int(float(args[0]['duration']))
dur_x = int((float(el_time)/float(du_time))*(WIDTH-10))
draw.rectangle((5, 230, WIDTH-5, 230 + 8), bar_bgcol) # background
draw.rectangle((5, 230, dur_x, 230 + 8), bar_col)
# v0.0.4 show remaining time of track
remaining = '-' + strftime("%M:%S", gmtime(duration - int(float(seek)/1000)))
# w4, y4 = draw.textsize(remaining, font_m)
w4 = f_textsize(remaining, font_m)
# draw.text((WIDTH - w4 - 2 + 2, 206 - 2 + 2), remaining, font=font_m, fill=str_col) # shadow, fill by mean
f_drawtext(WIDTH - w4 - 2 + 2, 206 - 2 + 2, remaining, font_m, str_col) # shadow, fill by mean)
# draw.text((WIDTH - w4 - 2, 206 - 2), remaining, font=font_m, fill=txt_col) # fill by mean
f_drawtext(WIDTH - w4 - 2, 206 - 2, remaining, font_m, txt_col) # fill by mean)
# display only if img changed
if img_check != img:
img_check = img
sendtodisplay(img)
# print("on_push_state--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
img = Image.new('RGBA', (240, 240), color=(0, 0, 0, 25))
draw = ImageDraw.Draw(img, 'RGBA')
socketIO.once('connect', on_connect)
socketIO.on('disconnect', on_disconnect)
def handle_button(pin):
# start_time = time.time() # debug, time of code execution
global mode, marker, liststart,position, pwm_value, seek, duration # v.0.0.4
browselibrary = False
step = 30000 # 30 seconds
if pin == 5: # Button A, only needs single press function
print("Button 5 service:", service)
if mode == 'player':
if (status == 'play') and (service == 'webradio'):
socketIO.emit('stop')
elif (status == 'play'):
socketIO.emit('pause')
else:
socketIO.emit('play')
elif mode == 'navigation':
if len(nav_array_uri) != 0:
if len(nav_array_type) == 0:
browselibrary = True
else:
if nav_array_type[marker] == 'song' or nav_array_type[marker] == 'webradio' or nav_array_type[marker] == 'mywebradio': # v.0.0.4 fix for mywebradio
socketIO.emit('replaceAndPlay', {"service": nav_array_service[marker], "type": nav_array_type[marker], "title": nav_array_name[marker], "uri": nav_array_uri[marker]})
reset_variable('player')
elif nav_array_type[marker] == 'playlist' and nav_array_service[marker] == 'mpd': # v.0.0.4 modified because of spotifiy
socketIO.emit('playPlaylist', {'name': nav_array_name[marker]})
reset_variable('player')
elif nav_array_type[marker] == 'playlist' and nav_array_service[marker] == 'spop': # v.0.0.4 condition added because of spotifiy
socketIO.emit('stop') # v.0.0.4 fix otherwise change from any playing source to spotify dont work
time.sleep(2) # v.0.0.4 fix otherwise change from any playing source to spotify dont work
socketIO.emit('replaceAndPlay', {"service": nav_array_service[marker], "type": nav_array_type[marker], "title": nav_array_name[marker], "uri": nav_array_uri[marker]})
reset_variable('player')
elif 'folder' in nav_array_type[marker]:
if nav_array_service[marker] == 'podcast':
display_stuff('bg_default', obj_trans['DISPLAY']['WAIT'], marker, liststart) # note, please wait
browselibrary = True
elif 'radio-' in nav_array_type[marker]: # the minus (-) is important, otherwise i cant decide between 'radiofolder' and 'webradiostream'
browselibrary = True
elif 'streaming-' in nav_array_type[marker]:
browselibrary = True
else:
display_stuff('bg_default', obj_trans['DISPLAY']['NOTSUPPORTED'], marker, liststart)
if browselibrary is True:
# replace "mnt/" in uri through "music-library/", otherwise calling them dont work (at least in favourites)
uri = nav_array_uri[marker]
uri = uri.replace('mnt/', 'music-library/')
socketIO.emit('browseLibrary', {'uri': uri})
browselibrary = False
else:
reset_variable('player')
socketIO.emit('getState', '', on_push_state)
elif mode == 'menu':
# socketIO.emit('getQueue', on_push_queue) # refresh variables of queue
if nav_array_type[marker] == 'emit':
if 'setSleep' in nav_array_uri[marker][0]:
socketIO.emit(nav_array_uri[marker][0], nav_array_uri[marker][1])
display_stuff('bg_default', obj_trans['DISPLAY']['SETSLEEPTIMER'], 0, 0, 'info')
#disp.set_backlight(False)
pwm_value = 0
pi_pwm.ChangeDutyCycle(pwm_value)
else:
socketIO.emit(nav_array_uri[marker])
display_stuff('bg_default', ['executing:', nav_array_uri[marker]], 0, 0, 'info')
elif nav_array_type[marker] == 'seek': # v.0.0.4
mode = 'seek'
display_stuff('bg_default', obj_trans['DISPLAY']['SEEK'], 0, 0, 'seek')
elif nav_array_type[marker] == 'prevnext': # v.0.0.4
socketIO.emit('getQueue', on_push_queue) # refresh variables of queue
mode = 'prevnext'
display_stuff('bg_default', [str(position + 1) + '/' + str(len_queue), obj_trans['DISPLAY']['PREVNEXT'], title_queue[position]], 1, 0, 'seek')
else: # browsesource
reset_variable('navigation')
socketIO.emit('getBrowseSources', '', on_push_browsesources)
else:
reset_variable('player')
socketIO.emit('getState', '', on_push_state)
if pin == 6: # Button B, needs a pressed function in player mode
if mode == 'player':
tic = time.perf_counter()
#toc = tic
while not GPIO.input(6):
toc = time.perf_counter()
if toc - tic > 0.5 :
##prevnext('prev')
#position -= 1
#if position < 0: # set position to last entry to loop through playlist infinite
# position = len_queue - 1
#socketIO.emit('stop')
#socketIO.emit('play', {"value": position})
if int(float((seek - step)/1000)) > 0:
seek -= step
socketIO.emit('seek', int(float(seek/1000)))
time.sleep(0.25)
if toc - tic < 0.25 and volume > 0 :
socketIO.emit('volume', '-')
elif toc - tic < 0.5 :
position -= 1
if position < 0: # set position to last entry to loop through playlist infinite
position = len_queue - 1
socketIO.emit('stop')
socketIO.emit('play', {"value": position})
# while not GPIO.input(6) and volume > 0: # limit/exit at volume 0 so amixer dont go crazy
# socketIO.emit('volume', '-')
# time.sleep(0.5)
elif mode == 'navigation' or mode == 'menu' or mode == 'seek' or mode == 'prevnext':
reset_variable('player')
socketIO.emit('getState', '', on_push_state)
if pin == 16: # Button X, needs a pressed function in navigation and menu mode
if mode == 'player':
tic = time.perf_counter()
toc = tic
while not GPIO.input(16):
toc = time.perf_counter()
if toc - tic > 0.75 :
#disp.set_backlight(False)
pwm_value -= 20
if pwm_value < 0 :
pwm_value = 100
pi_pwm.ChangeDutyCycle(pwm_value)
#disp.set_pwm_backlight(50)
time.sleep(0.25)
if toc - tic < 0.5 :
navigation_handler()
#disp.set_backlight(True) # v.0.0.4
if pwm_value < 10:
pwm_value = 100
pi_pwm.ChangeDutyCycle(pwm_value)
elif mode == 'navigation' or mode == 'menu':
while not GPIO.input(16):
marker -= 1 # count minus 1
if marker < 0: # blaettere nach oben durch
marker = listresult - 1
if listresult > listmax - 1: # dann aendere auch noch den liststart
liststart = int(liststart + (math.floor(listresult/listmax)*listmax))
liststart = int(math.floor(marker/listmax)*listmax) # definiert das blaettern zur naechsten Seite
display_stuff('bg_default', nav_array_name, marker, liststart)
elif mode == 'seek': # v.0.0.4
seeking('+')
elif mode == 'prevnext': # v.0.0.4
prevnext('next')
if pin == BUTTONS[3]: # Button Y, needs a pressed function in all modes
if mode == 'seek':
seeking('-')
elif mode == 'prevnext':
prevnext('prev')
elif mode == 'player':
tic = time.perf_counter()
#toc = tic
while not GPIO.input(BUTTONS[3]):
toc = time.perf_counter()
if toc - tic > 0.5 :
##prevnext('next')
#position += 1
#if position > len_queue - 1: # set position to first entry to loop through playlist infinite
# position = 0
#socketIO.emit('stop')
#socketIO.emit('play', {"value": position})
if int(float((seek + step)/1000)) < duration:
seek += step
socketIO.emit('seek', int(float(seek/1000)))
time.sleep(0.25)
if toc - tic < 0.25 and volume < 100 :
socketIO.emit('volume', '+')
elif toc - tic < 0.5 :
position += 1
if position > len_queue - 1: # set position to first entry to loop through playlist infinite
position = 0
socketIO.emit('stop')
socketIO.emit('play', {"value": position})
else:
while not GPIO.input(BUTTONS[3]):
#if mode == 'player' and volume < 100: # limit/exit at volume 100 so amixer dont go crazy:
# socketIO.emit('volume', '+')
# time.sleep(0.5)
#elif mode == 'navigation' or mode == 'menu':
if mode == 'navigation' or mode == 'menu':
marker += 1 # count plus 1
liststart = int(math.floor(marker/listmax)*listmax) # definiert das blaettern zur naechsten Seite
if marker > listresult - 1: # blaettere nach unten durch
marker = 0
liststart = 0
display_stuff('bg_default', nav_array_name, marker, liststart)
# print("handle_button--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
def setup_channel(channel):
# start_time = time.time() # debug, time of code execution
try:
#print('register %d') % channel
print('register %d' % channel) #v0.0.6
GPIO.setup(channel, GPIO.IN, GPIO.PUD_UP)
GPIO.add_event_detect(channel, GPIO.FALLING, handle_button, bouncetime=250)
print('success')
except (ValueError, RuntimeError) as e:
print('ERROR:', e)
# print("setup_channel--- %s seconds ---" % (time.time() - start_time)) # debug, time of code execution
for x in BUTTONS:
setup_channel(x)
def main():
socketIO.wait()
time.sleep(0.5)
try:
main()
except KeyboardInterrupt:
clean()
pass
Hi!
I am new to the community and new to using the Raspberry Pi.
Could you please write step by step how to install PirateAudio plugin on fresh Volumio v.3.233.
I have Pi Zero and Pirate Audio Headphone Amp. I have installed a clean Volumio OS - it works through the computer. And I don’t know what to do next, because I’m green with it… Thanks.
Please have a look to post #108.
Cheers
Hi @raydorf, I’m glad you like it.
The 3mf files for 3D printing are here on Github.
You are all welcome to make one. (easiest way for non-github users to get the files is to right click the green code button and “download zip”)
I’ve all hacked the display.py a bit to make it a bit more like volumio in its appearance.
Smiles
Drew
whats the recommended install methiod now for a pi 2 zero w ?