Add Python-WiFi-Radio (as deprecated code)
This commit is contained in:
parent
606d35129f
commit
47e3c8a275
3 changed files with 594 additions and 0 deletions
527
Python-WiFi-Radio/PiPhi.py
Executable file
527
Python-WiFi-Radio/PiPhi.py
Executable file
|
|
@ -0,0 +1,527 @@
|
||||||
|
#! /usr/bin/python
|
||||||
|
|
||||||
|
# UI wrapper for 'pianobar' client for Pandora, using Adafruit 16x2 LCD
|
||||||
|
# Pi Plate for Raspberry Pi.
|
||||||
|
# Written by Adafruit Industries. MIT license.
|
||||||
|
#
|
||||||
|
# Required hardware includes any internet-connected Raspberry Pi
|
||||||
|
# system, any of the Adafruit 16x2 LCD w/Keypad Pi Plate varieties
|
||||||
|
# and either headphones or amplified speakers.
|
||||||
|
# Required software includes the Adafruit Raspberry Pi Python Code
|
||||||
|
# repository, pexpect library and pianobar. A Pandora account is
|
||||||
|
# also necessary.
|
||||||
|
#
|
||||||
|
# Resources:
|
||||||
|
# http://www.adafruit.com/products/1109 RGB Positive 16x2 LCD + Keypad
|
||||||
|
# http://www.adafruit.com/products/1110 RGB Negative 16x2 LCD + Keypad
|
||||||
|
# http://www.adafruit.com/products/1115 Blue & White 16x2 LCD + Keypad
|
||||||
|
|
||||||
|
import atexit, pexpect, pickle, socket, time, subprocess
|
||||||
|
from Adafruit_I2C import Adafruit_I2C
|
||||||
|
from Adafruit_MCP230xx import Adafruit_MCP230XX
|
||||||
|
from Adafruit_CharLCDPlate import Adafruit_CharLCDPlate
|
||||||
|
|
||||||
|
|
||||||
|
# Constants:
|
||||||
|
RGB_LCD = False # Set to 'True' if using color backlit LCD
|
||||||
|
HALT_ON_EXIT = False # Set to 'True' to shut down system when exiting
|
||||||
|
MAX_FPS = 6 if RGB_LCD else 4 # Limit screen refresh rate for legibility
|
||||||
|
VOL_MIN = -20
|
||||||
|
VOL_MAX = 15
|
||||||
|
VOL_DEFAULT = 0
|
||||||
|
HOLD_TIME = 3.0 # Time (seconds) to hold select button for shut down
|
||||||
|
PICKLEFILE = '/home/pi/.config/pianobar/state.p'
|
||||||
|
|
||||||
|
# Global state:
|
||||||
|
volCur = VOL_MIN # Current volume
|
||||||
|
volNew = VOL_DEFAULT # 'Next' volume after interactions
|
||||||
|
volSpeed = 1.0 # Speed of volume change (accelerates w/hold)
|
||||||
|
volSet = False # True if currently setting volume
|
||||||
|
paused = False # True if music is paused
|
||||||
|
staSel = False # True if selecting station
|
||||||
|
volTime = 0 # Time of last volume button interaction
|
||||||
|
playMsgTime = 0 # Time of last 'Playing' message display
|
||||||
|
staBtnTime = 0 # Time of last button press on station menu
|
||||||
|
xTitle = 16 # X position of song title (scrolling)
|
||||||
|
xInfo = 16 # X position of artist/album (scrolling)
|
||||||
|
xStation = 0 # X position of station (scrolling)
|
||||||
|
xTitleWrap = 0
|
||||||
|
xInfoWrap = 0
|
||||||
|
xStationWrap = 0
|
||||||
|
songTitle = ''
|
||||||
|
songInfo = ''
|
||||||
|
stationNum = 0 # Station currently playing
|
||||||
|
stationNew = 0 # Station currently highlighted in menu
|
||||||
|
stationList = ['']
|
||||||
|
stationIDs = ['']
|
||||||
|
|
||||||
|
# Char 7 gets reloaded for different modes. These are the bitmaps:
|
||||||
|
charSevenBitmaps = [
|
||||||
|
[0b10000, # Play (also selected station)
|
||||||
|
0b11000,
|
||||||
|
0b11100,
|
||||||
|
0b11110,
|
||||||
|
0b11100,
|
||||||
|
0b11000,
|
||||||
|
0b10000,
|
||||||
|
0b00000],
|
||||||
|
[0b11011, # Pause
|
||||||
|
0b11011,
|
||||||
|
0b11011,
|
||||||
|
0b11011,
|
||||||
|
0b11011,
|
||||||
|
0b11011,
|
||||||
|
0b11011,
|
||||||
|
0b00000],
|
||||||
|
[0b00000, # Next Track
|
||||||
|
0b10100,
|
||||||
|
0b11010,
|
||||||
|
0b11101,
|
||||||
|
0b11010,
|
||||||
|
0b10100,
|
||||||
|
0b00000,
|
||||||
|
0b00000]]
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# Exit handler tries to leave LCD in a nice state.
|
||||||
|
def cleanExit():
|
||||||
|
if lcd is not None:
|
||||||
|
time.sleep(0.5)
|
||||||
|
lcd.backlight(lcd.OFF)
|
||||||
|
lcd.clear()
|
||||||
|
lcd.stop()
|
||||||
|
if pianobar is not None:
|
||||||
|
pianobar.kill(0)
|
||||||
|
|
||||||
|
|
||||||
|
def shutdown():
|
||||||
|
lcd.clear()
|
||||||
|
if HALT_ON_EXIT:
|
||||||
|
if RGB_LCD: lcd.backlight(lcd.YELLOW)
|
||||||
|
lcd.message('Wait 30 seconds\nto unplug...')
|
||||||
|
# Ramp down volume over 5 seconds while 'wait' message shows
|
||||||
|
steps = int((volCur - VOL_MIN) + 0.5) + 1
|
||||||
|
pause = 5.0 / steps
|
||||||
|
for i in range(steps):
|
||||||
|
pianobar.send('(')
|
||||||
|
time.sleep(pause)
|
||||||
|
subprocess.call("sync")
|
||||||
|
cleanExit()
|
||||||
|
subprocess.call(["shutdown", "-h", "now"])
|
||||||
|
else:
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
# Draws song title or artist/album marquee at given position.
|
||||||
|
# Returns new position to avoid global uglies.
|
||||||
|
def marquee(s, x, y, xWrap):
|
||||||
|
lcd.setCursor(0, y)
|
||||||
|
if x > 0: # Initially scrolls in from right edge
|
||||||
|
lcd.message(' ' * x + s[0:16-x])
|
||||||
|
else: # Then scrolls w/wrap indefinitely
|
||||||
|
lcd.message(s[-x:16-x])
|
||||||
|
if x < xWrap: return 0
|
||||||
|
return x - 1
|
||||||
|
|
||||||
|
|
||||||
|
def drawPlaying():
|
||||||
|
lcd.createChar(7, charSevenBitmaps[0])
|
||||||
|
lcd.setCursor(0, 1)
|
||||||
|
lcd.message('\x07 Playing ')
|
||||||
|
return time.time()
|
||||||
|
|
||||||
|
|
||||||
|
def drawPaused():
|
||||||
|
lcd.createChar(7, charSevenBitmaps[1])
|
||||||
|
lcd.setCursor(0, 1)
|
||||||
|
lcd.message('\x07 Paused ')
|
||||||
|
|
||||||
|
|
||||||
|
def drawNextTrack():
|
||||||
|
lcd.createChar(7, charSevenBitmaps[2])
|
||||||
|
lcd.setCursor(0, 1)
|
||||||
|
lcd.message('\x07 Next track... ')
|
||||||
|
|
||||||
|
|
||||||
|
# Draw station menu (overwrites fulls screen to facilitate scrolling)
|
||||||
|
def drawStations(stationNew, listTop, xStation, staBtnTime):
|
||||||
|
last = len(stationList)
|
||||||
|
if last > 2: last = 2 # Limit stations displayed
|
||||||
|
ret = 0 # Default return value (for station scrolling)
|
||||||
|
line = 0 # Line counter
|
||||||
|
msg = '' # Clear output string to start
|
||||||
|
for s in stationList[listTop:listTop+2]: # For each station...
|
||||||
|
sLen = len(s) # Length of station name
|
||||||
|
if (listTop + line) == stationNew: # Selected station?
|
||||||
|
msg += chr(7) # Show selection cursor
|
||||||
|
if sLen > 15: # Is station name longer than line?
|
||||||
|
if (time.time() - staBtnTime) < 0.5:
|
||||||
|
# Just show start of line for half a sec
|
||||||
|
s2 = s[0:15]
|
||||||
|
else:
|
||||||
|
# After that, scrollinate
|
||||||
|
s2 = s + ' ' + s[0:15]
|
||||||
|
xStationWrap = -(sLen + 2)
|
||||||
|
s2 = s2[-xStation:15-xStation]
|
||||||
|
if xStation > xStationWrap:
|
||||||
|
ret = xStation - 1
|
||||||
|
else: # Short station name - pad w/spaces if needed
|
||||||
|
s2 = s[0:15]
|
||||||
|
if sLen < 15: s2 += ' ' * (15 - sLen)
|
||||||
|
else: # Not currently-selected station
|
||||||
|
msg += ' ' # No cursor
|
||||||
|
s2 = s[0:15] # Clip or pad name to 15 chars
|
||||||
|
if sLen < 15: s2 += ' ' * (15 - sLen)
|
||||||
|
msg += s2 # Add station name to output message
|
||||||
|
line += 1
|
||||||
|
if line == last: break
|
||||||
|
msg += '\n' # Not last line - add newline
|
||||||
|
lcd.setCursor(0, 0)
|
||||||
|
lcd.message(msg)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def getStations():
|
||||||
|
lcd.clear()
|
||||||
|
lcd.message('Retrieving\nstation list...')
|
||||||
|
pianobar.expect('Select station: ', timeout=20)
|
||||||
|
# 'before' is now string of stations I believe
|
||||||
|
# break up into separate lines
|
||||||
|
a = pianobar.before.splitlines()
|
||||||
|
names = []
|
||||||
|
ids = []
|
||||||
|
# Parse each line
|
||||||
|
for b in a[:-1]: # Skip last line (station select prompt)
|
||||||
|
# Occasionally a queued up 'TIME: -XX:XX/XX:XX' string or
|
||||||
|
# 'new playlist...' appears in the output. Station list
|
||||||
|
# entries have a known format, so it's straightforward to
|
||||||
|
# skip these bogus lines.
|
||||||
|
# print '\"{}\"'.format(b)
|
||||||
|
if (b.find('playlist...') >= 0) or (b.find('Autostart') >= 0):
|
||||||
|
continue
|
||||||
|
# if b[0:5].find(':') >= 0: continue
|
||||||
|
# if (b.find(':') >= 0) or (len(b) < 13): continue
|
||||||
|
# Alternate strategy: must contain either 'QuickMix' or 'Radio':
|
||||||
|
# Somehow the 'playlist' case would get through this check. Buh?
|
||||||
|
if b.find('Radio') or b.find('QuickMix'):
|
||||||
|
id = b[5:7].strip()
|
||||||
|
name = b[13:].strip()
|
||||||
|
# If 'QuickMix' found, always put at head of list
|
||||||
|
if name == 'QuickMix':
|
||||||
|
ids.insert(0, id)
|
||||||
|
names.insert(0, name)
|
||||||
|
else:
|
||||||
|
ids.append(id)
|
||||||
|
names.append(name)
|
||||||
|
return names, ids
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Initialization
|
||||||
|
|
||||||
|
atexit.register(cleanExit)
|
||||||
|
|
||||||
|
lcd = Adafruit_CharLCDPlate()
|
||||||
|
lcd.begin(16, 2)
|
||||||
|
lcd.clear()
|
||||||
|
|
||||||
|
# Create volume bargraph custom characters (chars 0-5):
|
||||||
|
for i in range(6):
|
||||||
|
bitmap = []
|
||||||
|
bits = (255 << (5 - i)) & 0x1f
|
||||||
|
for j in range(8): bitmap.append(bits)
|
||||||
|
lcd.createChar(i, bitmap)
|
||||||
|
|
||||||
|
# Create up/down icon (char 6)
|
||||||
|
lcd.createChar(6,
|
||||||
|
[0b00100,
|
||||||
|
0b01110,
|
||||||
|
0b11111,
|
||||||
|
0b00000,
|
||||||
|
0b00000,
|
||||||
|
0b11111,
|
||||||
|
0b01110,
|
||||||
|
0b00100])
|
||||||
|
|
||||||
|
# By default, char 7 is loaded in 'pause' state
|
||||||
|
lcd.createChar(7, charSevenBitmaps[1])
|
||||||
|
|
||||||
|
# Get last-used volume and station name from pickle file
|
||||||
|
try:
|
||||||
|
f = open(PICKLEFILE, 'rb')
|
||||||
|
v = pickle.load(f)
|
||||||
|
f.close()
|
||||||
|
volNew = v[0]
|
||||||
|
defaultStation = v[1]
|
||||||
|
except:
|
||||||
|
defaultStation = None
|
||||||
|
|
||||||
|
# Show IP address (if network is available). System might be freshly
|
||||||
|
# booted and not have an address yet, so keep trying for a couple minutes
|
||||||
|
# before reporting failure.
|
||||||
|
t = time.time()
|
||||||
|
while True:
|
||||||
|
if (time.time() - t) > 120:
|
||||||
|
# No connection reached after 2 minutes
|
||||||
|
if RGB_LCD: lcd.backlight(lcd.RED)
|
||||||
|
lcd.message('Network is\nunreachable')
|
||||||
|
time.sleep(30)
|
||||||
|
exit(0)
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect(('8.8.8.8', 0))
|
||||||
|
if RGB_LCD: lcd.backlight(lcd.GREEN)
|
||||||
|
else: lcd.backlight(lcd.ON)
|
||||||
|
lcd.message('My IP address is\n' + s.getsockname()[0])
|
||||||
|
time.sleep(5)
|
||||||
|
break # Success -- let's hear some music!
|
||||||
|
except:
|
||||||
|
time.sleep(1) # Pause a moment, keep trying
|
||||||
|
|
||||||
|
# Launch pianobar as pi user (to use same config data, etc.) in background:
|
||||||
|
print('Spawning pianobar...')
|
||||||
|
pianobar = pexpect.spawn('sudo -u pi pianobar')
|
||||||
|
print('Receiving station list...')
|
||||||
|
pianobar.expect('Get stations... Ok.\r\n', timeout=60)
|
||||||
|
stationList, stationIDs = getStations()
|
||||||
|
try: # Use station name from last session
|
||||||
|
stationNum = stationList.index(defaultStation)
|
||||||
|
except: # Use first station in list
|
||||||
|
stationNum = 0
|
||||||
|
print 'Selecting station ' + stationIDs[stationNum]
|
||||||
|
pianobar.sendline(stationIDs[stationNum])
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Main loop. This is not quite a straight-up state machine; there's some
|
||||||
|
# persnickety 'nesting' and canceling among mode states, so instead a few
|
||||||
|
# global booleans take care of it rather than a mode variable.
|
||||||
|
|
||||||
|
if RGB_LCD: lcd.backlight(lcd.ON)
|
||||||
|
lastTime = 0
|
||||||
|
|
||||||
|
pattern_list = pianobar.compile_pattern_list(['SONG: ', 'STATION: ', 'TIME: '])
|
||||||
|
|
||||||
|
while pianobar.isalive():
|
||||||
|
|
||||||
|
# Process all pending pianobar output
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
x = pianobar.expect(pattern_list, timeout=0)
|
||||||
|
if x == 0:
|
||||||
|
songTitle = ''
|
||||||
|
songInfo = ''
|
||||||
|
xTitle = 16
|
||||||
|
xInfo = 16
|
||||||
|
xTitleWrap = 0
|
||||||
|
xInfoWrap = 0
|
||||||
|
x = pianobar.expect(' \| ')
|
||||||
|
if x == 0: # Title | Artist | Album
|
||||||
|
print 'Song: "{}"'.format(pianobar.before)
|
||||||
|
s = pianobar.before + ' '
|
||||||
|
n = len(s)
|
||||||
|
xTitleWrap = -n + 2
|
||||||
|
# 1+ copies + up to 15 chars for repeating scroll
|
||||||
|
songTitle = s * (1 + (16 / n)) + s[0:16]
|
||||||
|
x = pianobar.expect(' \| ')
|
||||||
|
if x == 0:
|
||||||
|
print 'Artist: "{}"'.format(pianobar.before)
|
||||||
|
artist = pianobar.before
|
||||||
|
x = pianobar.expect('\r\n')
|
||||||
|
if x == 0:
|
||||||
|
print 'Album: "{}"'.format(pianobar.before)
|
||||||
|
s = artist + ' < ' + pianobar.before + ' > '
|
||||||
|
n = len(s)
|
||||||
|
xInfoWrap = -n + 2
|
||||||
|
# 1+ copies + up to 15 chars for repeating scroll
|
||||||
|
songInfo = s * (2 + (16 / n)) + s[0:16]
|
||||||
|
elif x == 1:
|
||||||
|
x = pianobar.expect(' \| ')
|
||||||
|
if x == 0:
|
||||||
|
print 'Station: "{}"'.format(pianobar.before)
|
||||||
|
elif x == 2:
|
||||||
|
# Time doesn't include newline - prints over itself.
|
||||||
|
x = pianobar.expect('\r', timeout=1)
|
||||||
|
if x == 0:
|
||||||
|
print 'Time: {}'.format(pianobar.before)
|
||||||
|
# Periodically dump state (volume and station name)
|
||||||
|
# to pickle file so it's remembered between each run.
|
||||||
|
try:
|
||||||
|
f = open(PICKLEFILE, 'wb')
|
||||||
|
pickle.dump([volCur, stationList[stationNum]], f)
|
||||||
|
f.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except pexpect.EOF:
|
||||||
|
break
|
||||||
|
except pexpect.TIMEOUT:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
# Poll all buttons once, avoids repeated I2C traffic for different cases
|
||||||
|
b = lcd.buttons()
|
||||||
|
btnUp = b & (1 << lcd.UP)
|
||||||
|
btnDown = b & (1 <<lcd.DOWN)
|
||||||
|
btnLeft = b & (1 <<lcd.LEFT)
|
||||||
|
btnRight = b & (1 <<lcd.RIGHT)
|
||||||
|
btnSel = b & (1 <<lcd.SELECT)
|
||||||
|
|
||||||
|
# Certain button actions occur regardless of current mode.
|
||||||
|
# Holding the select button (for shutdown) is a big one.
|
||||||
|
if btnSel:
|
||||||
|
|
||||||
|
t = time.time() # Start time of button press
|
||||||
|
while lcd.buttonPressed(lcd.SELECT): # Wait for button release
|
||||||
|
if (time.time() - t) >= HOLD_TIME: # Extended hold?
|
||||||
|
shutdown() # We're outta here
|
||||||
|
# If tapped, different things in different modes...
|
||||||
|
if staSel: # In station select menu...
|
||||||
|
pianobar.send('\n') # Cancel station select
|
||||||
|
staSel = False # Cancel menu and return to
|
||||||
|
if paused: drawPaused() # play or paused state
|
||||||
|
else: # In play/pause state...
|
||||||
|
volSet = False # Exit volume-setting mode (if there)
|
||||||
|
paused = not paused # Toggle play/pause
|
||||||
|
pianobar.send('p') # Toggle pianobar play/pause
|
||||||
|
if paused: drawPaused() # Display play/pause change
|
||||||
|
else: playMsgTime = drawPlaying()
|
||||||
|
|
||||||
|
# Right button advances to next track in all modes, even paused,
|
||||||
|
# when setting volume, in station menu, etc.
|
||||||
|
elif btnRight:
|
||||||
|
|
||||||
|
drawNextTrack()
|
||||||
|
if staSel: # Cancel station select, if there
|
||||||
|
pianobar.send('\n')
|
||||||
|
staSel = False
|
||||||
|
paused = False # Un-pause, if there
|
||||||
|
volSet = False
|
||||||
|
pianobar.send('n')
|
||||||
|
|
||||||
|
# Left button enters station menu (if currently in play/pause state),
|
||||||
|
# or selects the new station and returns.
|
||||||
|
elif btnLeft:
|
||||||
|
|
||||||
|
staSel = not staSel # Toggle station menu state
|
||||||
|
if staSel:
|
||||||
|
# Entering station selection menu. Don't return to volume
|
||||||
|
# select, regardless of outcome, just return to normal play.
|
||||||
|
pianobar.send('s')
|
||||||
|
lcd.createChar(7, charSevenBitmaps[0])
|
||||||
|
volSet = False
|
||||||
|
cursorY = 0 # Cursor position on screen
|
||||||
|
stationNew = 0 # Cursor position in list
|
||||||
|
listTop = 0 # Top of list on screen
|
||||||
|
xStation = 0 # X scrolling for long station names
|
||||||
|
# Just keep the list we made at start-up
|
||||||
|
# stationList, stationIDs = getStations()
|
||||||
|
staBtnTime = time.time()
|
||||||
|
drawStations(stationNew, listTop, 0, staBtnTime)
|
||||||
|
else:
|
||||||
|
# Just exited station menu with selection - go play.
|
||||||
|
stationNum = stationNew # Make menu selection permanent
|
||||||
|
print 'Selecting station: "{}"'.format(stationIDs[stationNum])
|
||||||
|
pianobar.sendline(stationIDs[stationNum])
|
||||||
|
paused = False
|
||||||
|
|
||||||
|
# Up/down buttons either set volume (in play/pause) or select station
|
||||||
|
elif btnUp or btnDown:
|
||||||
|
|
||||||
|
if staSel:
|
||||||
|
# Move up or down station menu
|
||||||
|
if btnDown:
|
||||||
|
if stationNew < (len(stationList) - 1):
|
||||||
|
stationNew += 1 # Next station
|
||||||
|
if cursorY < 1: cursorY += 1 # Move cursor
|
||||||
|
else: listTop += 1 # Y-scroll
|
||||||
|
xStation = 0 # Reset X-scroll
|
||||||
|
elif stationNew > 0: # btnUp implied
|
||||||
|
stationNew -= 1 # Prev station
|
||||||
|
if cursorY > 0: cursorY -= 1 # Move cursor
|
||||||
|
else: listTop -= 1 # Y-scroll
|
||||||
|
xStation = 0 # Reset X-scroll
|
||||||
|
staBtnTime = time.time() # Reset button time
|
||||||
|
xStation = drawStations(stationNew, listTop, 0, staBtnTime)
|
||||||
|
else:
|
||||||
|
# Not in station menu
|
||||||
|
if volSet is False:
|
||||||
|
# Just entering volume-setting mode; init display
|
||||||
|
lcd.setCursor(0, 1)
|
||||||
|
volCurI = int((volCur - VOL_MIN) + 0.5)
|
||||||
|
n = int(volCurI / 5)
|
||||||
|
s = (chr(6) + ' Volume ' +
|
||||||
|
chr(5) * n + # Solid brick(s)
|
||||||
|
chr(volCurI % 5) + # Fractional brick
|
||||||
|
chr(0) * (6 - n)) # Spaces
|
||||||
|
lcd.message(s)
|
||||||
|
volSet = True
|
||||||
|
volSpeed = 1.0
|
||||||
|
# Volume-setting mode now active (or was already there);
|
||||||
|
# act on button press.
|
||||||
|
if btnUp:
|
||||||
|
volNew = volCur + volSpeed
|
||||||
|
if volNew > VOL_MAX: volNew = VOL_MAX
|
||||||
|
else:
|
||||||
|
volNew = volCur - volSpeed
|
||||||
|
if volNew < VOL_MIN: volNew = VOL_MIN
|
||||||
|
volTime = time.time() # Time of last volume button press
|
||||||
|
volSpeed *= 1.15 # Accelerate volume change
|
||||||
|
|
||||||
|
# Other logic specific to unpressed buttons:
|
||||||
|
else:
|
||||||
|
if staSel:
|
||||||
|
# In station menu, X-scroll active station name if long
|
||||||
|
if len(stationList[stationNew]) > 15:
|
||||||
|
xStation = drawStations(stationNew, listTop, xStation,
|
||||||
|
staBtnTime)
|
||||||
|
elif volSet:
|
||||||
|
volSpeed = 1.0 # Buttons released = reset volume speed
|
||||||
|
# If no interaction in 4 seconds, return to prior state.
|
||||||
|
# Volume bar will be erased by subsequent operations.
|
||||||
|
if (time.time() - volTime) >= 4:
|
||||||
|
volSet = False
|
||||||
|
if paused: drawPaused()
|
||||||
|
|
||||||
|
# Various 'always on' logic independent of buttons
|
||||||
|
if not staSel:
|
||||||
|
# Play/pause/volume: draw upper line (song title)
|
||||||
|
if songTitle is not None:
|
||||||
|
xTitle = marquee(songTitle, xTitle, 0, xTitleWrap)
|
||||||
|
|
||||||
|
# Integerize current and new volume values
|
||||||
|
volCurI = int((volCur - VOL_MIN) + 0.5)
|
||||||
|
volNewI = int((volNew - VOL_MIN) + 0.5)
|
||||||
|
volCur = volNew
|
||||||
|
# Issue change to pianobar
|
||||||
|
if volCurI != volNewI:
|
||||||
|
d = volNewI - volCurI
|
||||||
|
if d > 0: s = ')' * d
|
||||||
|
else: s = '(' * -d
|
||||||
|
pianobar.send(s)
|
||||||
|
|
||||||
|
# Draw lower line (volume or artist/album info):
|
||||||
|
if volSet:
|
||||||
|
if volNewI != volCurI: # Draw only changes
|
||||||
|
if(volNewI > volCurI):
|
||||||
|
x = int(volCurI / 5)
|
||||||
|
n = int(volNewI / 5) - x
|
||||||
|
s = chr(5) * n + chr(volNewI % 5)
|
||||||
|
else:
|
||||||
|
x = int(volNewI / 5)
|
||||||
|
n = int(volCurI / 5) - x
|
||||||
|
s = chr(volNewI % 5) + chr(0) * n
|
||||||
|
lcd.setCursor(x + 9, 1)
|
||||||
|
lcd.message(s)
|
||||||
|
elif paused == False:
|
||||||
|
if (time.time() - playMsgTime) >= 3:
|
||||||
|
# Display artist/album (rather than 'Playing')
|
||||||
|
xInfo = marquee(songInfo, xInfo, 1, xInfoWrap)
|
||||||
|
|
||||||
|
# Throttle frame rate, keeps screen legible
|
||||||
|
while True:
|
||||||
|
t = time.time()
|
||||||
|
if (t - lastTime) > (1.0 / MAX_FPS): break
|
||||||
|
lastTime = t
|
||||||
4
Python-WiFi-Radio/README.md
Normal file
4
Python-WiFi-Radio/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Python-WiFi-Radio
|
||||||
|
|
||||||
|
DEPRECATED CODE, originally to accompany this guide:
|
||||||
|
https://learn.adafruit.com/pi-wifi-radio
|
||||||
63
Python-WiFi-Radio/config
Normal file
63
Python-WiFi-Radio/config
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
# This is an example configuration file for pianobar. You may remove the # from
|
||||||
|
# lines you need and copy/move this file to ~/.config/pianobar/config
|
||||||
|
# See manpage for a description of the config keys
|
||||||
|
#
|
||||||
|
# User
|
||||||
|
user = YOUR_EMAIL_ADDRESS
|
||||||
|
password = YOUR_PASSWORD
|
||||||
|
# or
|
||||||
|
#password_command = gpg --decrypt ~/password
|
||||||
|
|
||||||
|
# Proxy (for those who are not living in the USA)
|
||||||
|
#control_proxy = http://127.0.0.1:9090/
|
||||||
|
|
||||||
|
# Keybindings
|
||||||
|
act_help = ?
|
||||||
|
act_songlove = +
|
||||||
|
act_songban = -
|
||||||
|
act_stationaddmusic = a
|
||||||
|
act_stationcreate = c
|
||||||
|
act_stationdelete = d
|
||||||
|
act_songexplain = e
|
||||||
|
act_stationaddbygenre = g
|
||||||
|
act_songinfo = i
|
||||||
|
act_addshared = j
|
||||||
|
act_songmove = m
|
||||||
|
act_songnext = n
|
||||||
|
act_songpause = p
|
||||||
|
act_quit = q
|
||||||
|
act_stationrename = r
|
||||||
|
act_stationchange = s
|
||||||
|
act_songtired = t
|
||||||
|
act_upcoming = u
|
||||||
|
act_stationselectquickmix = x
|
||||||
|
act_voldown = (
|
||||||
|
act_volup = )
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
#audio_quality = low
|
||||||
|
autostart_station = 123456
|
||||||
|
|
||||||
|
#event_command = /home/pi/.config/pianobar/scripts/eventcmd.sh
|
||||||
|
#fifo = /home/pi/.config/pianobar/ctl
|
||||||
|
#sort = quickmix_10_name_az
|
||||||
|
#love_icon = [+]
|
||||||
|
#ban_icon = [-]
|
||||||
|
volume = -20
|
||||||
|
|
||||||
|
# Format strings
|
||||||
|
format_nowplaying_song = SONG: %t | %a | %l
|
||||||
|
format_nowplaying_station = STATION: %n | %i
|
||||||
|
format_msg_time = TIME: %s
|
||||||
|
# No special prefix on songs, stations or info
|
||||||
|
format_msg_nowplaying = %s
|
||||||
|
format_msg_info = %s
|
||||||
|
|
||||||
|
# high-quality audio (192k mp3, for Pandora One subscribers only!)
|
||||||
|
#audio_quality = high
|
||||||
|
#rpc_host = internal-tuner.pandora.com
|
||||||
|
#partner_user = pandora one
|
||||||
|
#partner_password = TVCKIBGS9AO9TSYLNNFUML0743LH82D
|
||||||
|
#device = D01
|
||||||
|
#encrypt_password = 2%3WCL*JU$MP]4
|
||||||
|
#decrypt_password = U#IO$RZPAB%VX2
|
||||||
Loading…
Reference in a new issue