mirror of
https://github.com/Pecusx/piradio-mini.git
synced 2026-05-20 22:33:44 +02:00
f62ba47e57
After this update, you need to change the remote control to another. And after the restart, change the remote control back to the right one.
2771 lines
80 KiB
Python
Executable File
2771 lines
80 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#
|
|
# Raspberry Pi Internet Radio Class
|
|
# $Id: radio_class.py,v 1.246 2016/07/24 08:36:34 bob Exp $
|
|
#
|
|
#
|
|
# Author : Bob Rathbone
|
|
# Site : http://www.bobrathbone.com
|
|
#
|
|
# This class uses Music Player Daemon 'mpd' and the python-mpd library
|
|
# Use "apt-get install python-mpd" to install the library
|
|
# Modified to use python-mpd2 library mpd.wikia.com
|
|
# See http://mpd.wikia.com/wiki/Music_Player_Daemon_Wiki
|
|
#
|
|
# License: GNU V3, See https://www.gnu.org/copyleft/gpl.html
|
|
#
|
|
# Disclaimer: Software is provided as is and absolutly no warranties are implied or given.
|
|
# The authors shall not be liable for any loss or damage however caused.
|
|
#
|
|
|
|
import os
|
|
import sys
|
|
import string
|
|
import time,datetime
|
|
import re
|
|
import ConfigParser
|
|
import SocketServer
|
|
from time import strftime
|
|
import pdb
|
|
import pexpect
|
|
|
|
from udp_server_class import UDPServer
|
|
from udp_server_class import RequestHandler
|
|
from log_class import Log
|
|
from translate_class import Translate
|
|
from config_class import Configuration
|
|
from language_class import Language
|
|
from mpd import MPDClient
|
|
|
|
# System files
|
|
ConfigFile = "/etc/radiod.conf"
|
|
RadioLibDir = "/var/lib/radiod"
|
|
PlaylistsDirectory = "/var/lib/mpd/playlists"
|
|
MusicDirectory = "/var/lib/mpd/music"
|
|
CurrentStationFile = RadioLibDir + "/current_station"
|
|
CurrentPandoraFile = RadioLibDir + "/current_pandora"
|
|
CurrentTrackFile = RadioLibDir + "/current_track"
|
|
RandomSettingFile = RadioLibDir + "/random"
|
|
VolumeFile = RadioLibDir + "/volume"
|
|
TimerFile = RadioLibDir + "/timer"
|
|
AlarmFile = RadioLibDir + "/alarm"
|
|
StreamFile = RadioLibDir + "/streaming"
|
|
BoardRevisionFile = RadioLibDir + "/boardrevision"
|
|
VersionFile = "/usr/share/radio/version"
|
|
|
|
log = Log()
|
|
translate = Translate()
|
|
config = Configuration()
|
|
language = None
|
|
server = None
|
|
|
|
Mpd = "/usr/bin/mpd" # Music Player Daemon
|
|
Mpc = "/usr/bin/mpc" # Music Player Client
|
|
client = MPDClient() # Create the MPD client
|
|
|
|
class Radio:
|
|
# Input source
|
|
RADIO = 0
|
|
PLAYER = 1
|
|
PANDORA = 2
|
|
|
|
# Rotary class
|
|
ROTARY_STANDARD = config.STANDARD
|
|
ROTARY_ALTERNATIVE = config.ALTERNATIVE
|
|
|
|
# Player options
|
|
RANDOM = 0
|
|
CONSUME = 1
|
|
REPEAT = 2
|
|
RELOADLIB = 3
|
|
TIMER = 4
|
|
ALARM = 5
|
|
ALARMSETHOURS = 6
|
|
ALARMSETMINS = 7
|
|
STREAMING = 8
|
|
SELECTCOLOR = 9
|
|
OPTION_LAST = STREAMING
|
|
OPTION_ADA_LAST = SELECTCOLOR
|
|
|
|
# Display Modes
|
|
MODE_TIME = 0
|
|
MODE_SEARCH = 1
|
|
MODE_SOURCE = 2
|
|
MODE_OPTIONS = 3
|
|
MODE_RSS = 4
|
|
MODE_IP = 5
|
|
MODE_SLEEP = 6 # Sleep after timer or waiting for alarm
|
|
MODE_SHUTDOWN = -1
|
|
|
|
# Alarm definitions
|
|
ALARM_OFF = 0
|
|
ALARM_ON = 1
|
|
ALARM_REPEAT = 2
|
|
ALARM_WEEKDAYS = 3
|
|
ALARM_LAST = ALARM_WEEKDAYS
|
|
|
|
# Other definitions
|
|
OK = -1
|
|
UP = 0
|
|
DOWN = 1
|
|
LEFT = 2
|
|
RIGHT = 3
|
|
|
|
ONEDAYSECS = 86400 # Day in seconds
|
|
ONEDAYMINS = 1440 # Day in minutes
|
|
|
|
version = "0.0"
|
|
boardrevision = 2 # Raspberry board version type
|
|
udphost = 'localhost' # Remote IR listener UDP Host
|
|
udpport = 5100 # Remote IR listener UDP port number
|
|
mpdport = 6600 # MPD port number
|
|
volume = 80 # Volume level 0 - 100%
|
|
device_error_cnt = 0 # Device error causes an abort
|
|
isMuted = False # Is radio state "pause" or "stop"
|
|
isPandoraPaused = True
|
|
playlist = [] # Play list (tracks or radio stations)
|
|
current_id = 1 # Currently playing track or station
|
|
current_pandora_id = 3
|
|
max_pandora_id = 0
|
|
pandora_stationList = ['']
|
|
pandora_stationIDs = ['']
|
|
pandora_decision = OK # jesli nie OK to zostal wcisniety kursor i czekamy na potwierdzenie decyzji (+, -, ban) przyciskiem OK, w czasie oczekiwania na potwierdzenie zmienna przyjmuje wartosc kierunku kursora
|
|
pandora_decision_time = 0 # czas (godzina z zegara) po ktorym uplywa czekanie na decyzje
|
|
source = RADIO # Source RADIO or Player
|
|
reload = False # Reload radio stations or player playlists
|
|
artist = "" # Artist (Search routines)
|
|
error = False # Stream error handling
|
|
errorStr = "" # Error string
|
|
switch = 0 # Switch just pressed
|
|
updateLib = False # Reload radio stations or player
|
|
numevents = 0 # Number of events recieved for a rotary switch
|
|
volumeChange = False # Volume change flag (external clients)
|
|
interrupt = False # Was IR remote interrupt received
|
|
stats = None # MPD Stats array
|
|
currentsong = None # Current song / station
|
|
state = 'play' # State (used if get state fails)
|
|
getIdError = False # Prevent repeated display of ID error
|
|
StationNamesSource = config.LIST # Use own names from playlist
|
|
use_playlist_extensions = False # MPD 0.16 requires playlist.<ext>
|
|
rotary_class = config.STANDARD # Rotary class standard all alternate
|
|
|
|
mode_last = MODE_IP # Last mode a user can select
|
|
option_last = OPTION_LAST # Last available option
|
|
display_mode = MODE_TIME # Display mode
|
|
display_artist = False # Display artist (or tracck) flag
|
|
current_file = "" # Currently playing track or station
|
|
option_changed = False # Option changed
|
|
channelChanged = True # Used to display title
|
|
configOK = False # Do we have a configuration file
|
|
display_playlist_number = False # Display playlist number
|
|
|
|
# MPD Options
|
|
random = False # Random play
|
|
repeat = False # Repeat play
|
|
consume = False # Consume tracks
|
|
|
|
# Pandora titles
|
|
pandora_station_name = ''
|
|
pandora_song_name = ''
|
|
pandora_progress = ''
|
|
|
|
# Clock and timer options
|
|
timer = False # Timer on
|
|
timerValue = 30 # Timer value in minutes
|
|
timeTimer = 0 # The time when the Timer was activated in seconds
|
|
volumetime = 0 # Last volume check time
|
|
dateFormat = "%H:%M %d/%m/%Y" # Date format
|
|
|
|
alarmType = ALARM_OFF # Alarm on
|
|
alarmTime = "0:7:00" # Alarm time default type,hours,minutes
|
|
alarmTriggered = False # Alarm fired
|
|
|
|
stationName = '' # Radio station name
|
|
stationTitle = '' # Radio station title
|
|
|
|
option = RANDOM # Player option
|
|
search_index = 0 # The current search index
|
|
pandora_search_index = 0
|
|
loadnew = False # Load new track from search
|
|
streaming = False # Streaming (Icecast) disabled
|
|
|
|
ADAFRUIT = 1 # I2C backpack type AdaFruit
|
|
PCF8574 = 2 # I2C backpack type PCF8574
|
|
i2c_backpack = ADAFRUIT
|
|
i2c_address = 0x00 # Use default I2C address
|
|
|
|
speech = False # Speech for visually impaired or blind persons
|
|
|
|
# Configuration files
|
|
ConfigFiles = {
|
|
CurrentStationFile: 1,
|
|
CurrentTrackFile: 1,
|
|
CurrentPandoraFile: 0,
|
|
VolumeFile: 75,
|
|
TimerFile: 30,
|
|
AlarmFile: "07:00:00",
|
|
StreamFile: "off",
|
|
RandomSettingFile: 0,
|
|
}
|
|
|
|
# Initialisation routine
|
|
def __init__(self):
|
|
log.init('radio')
|
|
self.setupConfiguration()
|
|
return
|
|
|
|
# Set up configuration files
|
|
def setupConfiguration(self):
|
|
# Create directory
|
|
if not os.path.isfile(CurrentStationFile):
|
|
self.execCommand ("mkdir -p " + RadioLibDir )
|
|
|
|
# Get version from file
|
|
self.version = self.readFromFile(VersionFile) # (Pecus)
|
|
self.version = self.version.rstrip('\r\n')
|
|
|
|
# Initialise configuration files from ConfigFiles list
|
|
for file in self.ConfigFiles:
|
|
value = self.ConfigFiles[file]
|
|
if not os.path.isfile(file) or os.path.getsize(file) == 0:
|
|
# self.execCommand ("echo " + str(value) + " > " + file) # (Pecus)
|
|
self.writeToFile (file,str(value)) # (Pecus)
|
|
|
|
# Create mount point for USB stick and link it to the music directory
|
|
if not os.path.isfile("/media"):
|
|
self.execCommand("mkdir -p /media")
|
|
if not os.path.ismount("/media"):
|
|
self.execCommand("chown pi:pi /media")
|
|
self.execCommand("ln -f -s /media /var/lib/mpd/music")
|
|
|
|
# Create mount point for networked music library (NAS)
|
|
if not os.path.isfile("/share"):
|
|
self.execCommand("mkdir -p /share")
|
|
if not os.path.ismount("/share"):
|
|
self.execCommand("chown pi:pi /share")
|
|
self.execCommand("ln -f -s /share /var/lib/mpd/music")
|
|
|
|
self.execCommand("chown -R pi:pi " + RadioLibDir)
|
|
self.execCommand("chmod -R 777 " + RadioLibDir)
|
|
self.current_file = CurrentPandoraFile
|
|
self.current_pandora_id = self.getStoredID(self.current_file)
|
|
self.current_file = CurrentStationFile
|
|
self.current_id = self.getStoredID(self.current_file)
|
|
return
|
|
|
|
# Call back routine for the IR remote
|
|
def remoteCallback(self):
|
|
global server
|
|
key = server.getData()
|
|
log.message("IR remoteCallback " + key, log.DEBUG)
|
|
|
|
if key == 'KEY_MUTE':
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
if self.muted():
|
|
self.unmute()
|
|
else:
|
|
self.mute()
|
|
self.setInterrupt()
|
|
|
|
elif key == 'KEY_VOLUMEUP':
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.increaseVolume()
|
|
self.setInterrupt()
|
|
self.volumeChange = True
|
|
|
|
elif key == 'KEY_VOLUMEDOWN':
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.decreaseVolume()
|
|
self.setInterrupt()
|
|
self.volumeChange = True
|
|
|
|
elif key == 'KEY_CHANNELUP':
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.channelUp()
|
|
self.setInterrupt()
|
|
|
|
elif key == 'KEY_CHANNELDOWN':
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.channelDown()
|
|
self.setInterrupt()
|
|
|
|
elif key == 'KEY_MENU' or key == 'KEY_OK':
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
# jesli czekamy na potwierdzenie Pandory to wywolujemy odpowiednia procke
|
|
if self.pandora_decision != self.OK:
|
|
self.setPandoraDecision(self.pandora_decision)
|
|
self.pandora_decision = self.OK
|
|
time.sleep(0.5) # przydaloby sie tytul odswiezyc ale jak???
|
|
self.getPandoraData()
|
|
self.setInterrupt() # to chyba dziala ale dlaczego???
|
|
else:
|
|
self.cycleMenu()
|
|
self.setInterrupt()
|
|
|
|
elif key == 'KEY_LANGUAGE':
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.toggleSpeech()
|
|
self.setInterrupt()
|
|
|
|
elif key == 'KEY_INFO':
|
|
if self.display_mode != self.MODE_SLEEP: # no speak in sleep mode! (Pecus)
|
|
self.speakInformation()
|
|
self.setInterrupt()
|
|
|
|
# These come from the Web CGI script
|
|
elif key == 'MEDIA':
|
|
self.loadMedia()
|
|
self.setInterrupt()
|
|
|
|
elif key == 'RADIO':
|
|
self.loadStations()
|
|
self.setInterrupt()
|
|
|
|
elif key == 'KEY_MEDIA': # and remote dedicated button (Pecus)
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.setPlayerSource() # (Pecus)
|
|
if self.getReload(): # (Pecus)
|
|
display_mode = self.MODE_TIME # (Pecus)
|
|
self.setInterrupt() # (Pecus)
|
|
|
|
elif key == 'KEY_RADIO': # and remote dedicated button (Pecus)
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.setRadioSource() # (Pecus)
|
|
if self.getReload(): # (Pecus)
|
|
display_mode = self.MODE_TIME # (Pecus)
|
|
self.setInterrupt() # (Pecus)
|
|
|
|
elif key == 'KEY_PANDORA': # and remote dedicated button (Pecus)
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.setPandoraSource() # (Pecus)
|
|
if self.getReload(): # (Pecus)
|
|
display_mode = self.MODE_TIME # (Pecus)
|
|
self.setInterrupt() # (Pecus)
|
|
|
|
elif key == 'INTERRUPT':
|
|
self.setInterrupt()
|
|
|
|
# Sleep mode - forcing timer (Pecus)
|
|
elif key == 'KEY_SLEEP': # (Pecus)
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
self.timerValue = 1 # (Pecus)
|
|
self.timerOn() # (Pecus)
|
|
now = int(time.time()) # (Pecus)
|
|
self.timeTimer = now - self.timerValue * 60 # (Pecus)
|
|
self.fireTimer() # (Pecus)
|
|
time.sleep(0.5) # Zapobiega przewinieciu sie calego tytulu pandory na LCD przed wylaczeniem... dlaczego???
|
|
self.setInterrupt() # (Pecus)
|
|
|
|
# Wakeup from sleep - forcing menu and unmute (Pecus)
|
|
elif key == 'KEY_WAKEUP': # (Pecus)
|
|
self.unmute() # (Pecus)
|
|
display_mode = self.MODE_TIME # (Pecus)
|
|
self.setDisplayMode(display_mode) # (Pecus)
|
|
self.setInterrupt() # (Pecus)
|
|
|
|
elif key == 'KEY_TIME': # timer on/off (Pecus)
|
|
if self.display_mode != self.MODE_SLEEP: # no in sleep mode! (Pecus)
|
|
if self.getTimer():
|
|
self.timerOff()
|
|
else:
|
|
self.timerOn()
|
|
self.setInterrupt() # (Pecus)
|
|
|
|
|
|
# Handle left,right, up and down keys
|
|
else:
|
|
self.handle_key(key)
|
|
self.setInterrupt()
|
|
|
|
return
|
|
|
|
# Set up radio configuration and start the MPD daemon
|
|
def start(self):
|
|
global server
|
|
global language
|
|
|
|
config.display()
|
|
# Get Configuration parameters /etc/radiod.conf
|
|
self.boardrevision = self.getBoardRevision()
|
|
self.mpdport = config.getMpdPort()
|
|
self.udpport = config.getRemoteUdpPort()
|
|
self.udphost = config.getRemoteListenHost()
|
|
self.display_playlist_number = config.getDisplayPlaylistNumber()
|
|
self.source = config.getSource()
|
|
self.speech = config.getSpeech()
|
|
self.stationNamesSource = config.getStationNamesSource()
|
|
language = Language(self.speech) # language is a global
|
|
self.use_playlist_extensions = config.getPlaylistExtensions()
|
|
self.rotary_class = config.getRotaryClass()
|
|
|
|
# Log OS version information
|
|
OSrelease = self.execCommand("cat /etc/os-release | grep NAME")
|
|
OSrelease = OSrelease.replace("PRETTY_NAME=", "OS release: ")
|
|
OSrelease = OSrelease.replace('"', '')
|
|
log.message(OSrelease, log.INFO)
|
|
myos = self.execCommand('uname -a')
|
|
log.message(myos, log.INFO)
|
|
|
|
# Start the player daemon
|
|
self.execCommand("service mpd start")
|
|
# Connect to MPD
|
|
self.connect(self.mpdport)
|
|
client.clear()
|
|
self.randomOff()
|
|
self.consumeOff()
|
|
self.repeatOff()
|
|
self.current_id = self.getStoredID(self.current_file)
|
|
log.message("radio.start current ID " + str(self.current_id), log.DEBUG)
|
|
self.volume = self.getStoredVolume()
|
|
self.setVolume(self.volume)
|
|
|
|
# Alarm and timer settings
|
|
self.timeTimer = int(time.time())
|
|
self.timerValue = self.getStoredTimer()
|
|
self.alarmTime = self.getStoredAlarm()
|
|
sType,sHours,sMinutes = self.alarmTime.split(':')
|
|
self.alarmType = int(sType)
|
|
if self.alarmType > self.ALARM_OFF:
|
|
self.alarmType = self.ALARM_OFF
|
|
|
|
# Icecast Streaming settings
|
|
self.streaming = self.getStoredStreaming()
|
|
if self.streaming:
|
|
self.streamingOn()
|
|
else:
|
|
self.streamingOff()
|
|
|
|
# Start the remote control listener
|
|
try:
|
|
server = UDPServer((self.udphost,self.udpport),RequestHandler)
|
|
msg = "UDP Server listening on " + self.udphost + " port " + str(self.udpport)
|
|
log.message(msg, log.INFO)
|
|
server.listen(server,self.remoteCallback)
|
|
except:
|
|
log.message("UDP server could not bind to " + self.udphost
|
|
+ " port " + str(self.udpport), log.ERROR)
|
|
return
|
|
|
|
# Connect to MPD
|
|
def connect(self,port):
|
|
global client
|
|
connection = False
|
|
retry = 2
|
|
while retry > 0:
|
|
client = MPDClient() # Create the MPD client
|
|
try:
|
|
client.timeout = 10
|
|
client.idletimeout = None
|
|
client.connect("localhost", port)
|
|
log.message("Connected to MPD port " + str(port), log.INFO)
|
|
connection = True
|
|
retry = 0
|
|
except:
|
|
log.message("Failed to connect to MPD on port " + str(port), log.ERROR)
|
|
time.sleep(2.5) # Wait for interrupt in the case of a shutdown
|
|
log.message("Restarting MPD",log.DEBUG)
|
|
if retry < 2:
|
|
self.execCommand("service mpd restart")
|
|
else:
|
|
self.execCommand("service mpd start")
|
|
time.sleep(2) # Give MPD time to restart
|
|
retry -= 1
|
|
|
|
return connection
|
|
|
|
# Handle IR remote key
|
|
def handle_key(self,key):
|
|
if self.display_mode == self.MODE_TIME:
|
|
# obsluga kursorow pilota na glownym ekranie
|
|
if self.source == self.PANDORA: # Tylko dla Pandory specjalne funkcje kursorow na pilocie
|
|
if key == 'KEY_RIGHT':
|
|
# kursor w prawo w czasie odtwarzania pandory - nastepny utwor
|
|
self.PandoraNextTrack()
|
|
time.sleep(0.5)
|
|
self.getPandoraData()
|
|
self.pandora_decision = self.OK
|
|
elif key == 'KEY_UP':
|
|
self.pandora_decision = self.UP
|
|
self.pandora_decision_time = int(time.time()) + 3 # 3 sekundy na potwierdzenie OK
|
|
# kursor w gore - lubie
|
|
# ale czekamy sekunde na potwierdzenie OK
|
|
# no i tu trzeba cos wykombinowac :)
|
|
pass
|
|
elif key == 'KEY_DOWN':
|
|
self.pandora_decision = self.DOWN
|
|
self.pandora_decision_time = int(time.time()) + 3 # 3 sekundy na potwierdzenie OK
|
|
elif key == 'KEY_LEFT':
|
|
self.pandora_decision = self.LEFT
|
|
self.pandora_decision_time = int(time.time()) + 3 # 3 sekundy na potwierdzenie OK
|
|
|
|
|
|
else:
|
|
if self.display_mode == self.MODE_OPTIONS:
|
|
self.handle_options(key)
|
|
|
|
elif self.display_mode == self.MODE_SOURCE:
|
|
self.handle_source(key)
|
|
|
|
elif self.display_mode == self.MODE_SEARCH:
|
|
self.handle_search(key)
|
|
|
|
self.optionChangedTrue()
|
|
|
|
return
|
|
|
|
# Handle stepping through menu options and changing them
|
|
def handle_options(self,key):
|
|
direction = -1
|
|
|
|
if key == 'KEY_UP':
|
|
direction = self.UP
|
|
elif key == 'KEY_DOWN':
|
|
direction = self.DOWN
|
|
elif key == 'KEY_LEFT':
|
|
direction = self.LEFT
|
|
elif key == 'KEY_RIGHT':
|
|
direction = self.RIGHT
|
|
|
|
if direction == self.UP or direction == self.DOWN:
|
|
self.cycleOptions(direction)
|
|
elif direction == self.LEFT or direction == self.RIGHT:
|
|
self.changeOption(direction)
|
|
return
|
|
|
|
# Handle stepping through menu options
|
|
def cycleOptions(self,direction):
|
|
option = self.getOption()
|
|
|
|
log.message("radio.cycleOptions option=" + str(option) \
|
|
+ " direction=" + str(direction), log.DEBUG)
|
|
|
|
# Cycle through option
|
|
if direction == self.UP:
|
|
option += 1
|
|
elif direction == self.DOWN:
|
|
option -= 1
|
|
|
|
|
|
# Skip reload if not in player mode
|
|
if option == self.RELOADLIB and self.source != self.PLAYER:
|
|
if direction == self.UP:
|
|
option += 1
|
|
else:
|
|
option -= 1
|
|
|
|
if option > self.option_last:
|
|
option = self.RANDOM
|
|
|
|
elif option < 0:
|
|
option = self.option_last
|
|
if option == self.RELOADLIB and self.source != self.PLAYER:
|
|
option -= 1
|
|
|
|
self.setOption(option)
|
|
return
|
|
|
|
# Handle search (IR routine)
|
|
def handle_search(self,key):
|
|
direction = self.UP
|
|
if key == 'KEY_LEFT' or key == 'KEY_DOWN':
|
|
direction = self.DOWN
|
|
|
|
if self.source == self.RADIO:
|
|
self.getNext(direction)
|
|
elif self.source == self.PLAYER:
|
|
# Step through tracks
|
|
if key == 'KEY_LEFT' or key == 'KEY_RIGHT':
|
|
self.getNext(direction)
|
|
else:
|
|
# Step through artist
|
|
self.findNextArtist(direction)
|
|
elif self.source == self.PANDORA:
|
|
#pass
|
|
self.getPandoraNext(direction)
|
|
return
|
|
|
|
# Toggle speech
|
|
def toggleSpeech(self):
|
|
sVoice = language.getText('voice')
|
|
if self.speech:
|
|
sOff = language.getText('off')
|
|
self.speak(sVoice + ' ' + sOff)
|
|
self.speech = False
|
|
else:
|
|
self.speech = True
|
|
sOn = language.getText('on')
|
|
self.speak(sVoice + ' ' + sOn)
|
|
return
|
|
|
|
# Scroll up and down between stations/tracks
|
|
def getNext(self,direction):
|
|
playlist = self.getPlayList()
|
|
index = self.getSearchIndex()
|
|
|
|
# Artist displayed then don't increment track first time in
|
|
|
|
if not self.displayArtist():
|
|
leng = len(playlist)
|
|
if leng > 0:
|
|
if direction == self.UP:
|
|
index = index + 1
|
|
if index >= leng:
|
|
index = 0
|
|
else:
|
|
index = index - 1
|
|
if index < 0:
|
|
index = leng - 1
|
|
|
|
self.setSearchIndex(index)
|
|
self.setLoadNew(True)
|
|
name = self.getStationName(index)
|
|
if name.startswith("http:") and '#' in name:
|
|
url,name = name.split('#')
|
|
msg = "radio.getNext index " + str(index) + " "+ name
|
|
log.message(msg, log.DEBUG)
|
|
if self.speech:
|
|
self.speak(language.getText('search') + " " + str(index+1)
|
|
+ " " + name)
|
|
return
|
|
|
|
# Scroll up and down between pandora stations
|
|
def getPandoraNext(self,direction):
|
|
index = self.pandora_search_index
|
|
|
|
# Artist displayed then don't increment track first time in
|
|
|
|
if not self.displayArtist():
|
|
leng = self.max_pandora_id
|
|
if leng > 0:
|
|
if direction == self.UP:
|
|
index = index + 1
|
|
if index >= leng:
|
|
index = 0
|
|
else:
|
|
index = index - 1
|
|
if index < 0:
|
|
index = leng - 1
|
|
|
|
self.pandora_search_index = index
|
|
self.setLoadNew(True)
|
|
name = self.getPandoraStationName(index)
|
|
if name.startswith("http:") and '#' in name:
|
|
url,name = name.split('#')
|
|
msg = "radio.getPandoraNext index " + str(index) + " "+ name
|
|
log.message(msg, log.DEBUG)
|
|
if self.speech:
|
|
self.speak(language.getText('search') + " " + str(index+1)
|
|
+ " " + name)
|
|
return
|
|
|
|
# Scroll through tracks by artist
|
|
def findNextArtist(self,direction):
|
|
self.setLoadNew(True)
|
|
index = self.getSearchIndex()
|
|
playlist = self.getPlayList()
|
|
current_artist = self.getArtistName(index)
|
|
|
|
found = False
|
|
leng = len(playlist)
|
|
count = leng
|
|
while not found:
|
|
if direction == self.UP:
|
|
index = index + 1
|
|
if index >= leng:
|
|
index = 0
|
|
else:
|
|
index = index - 1
|
|
if index < 1:
|
|
index = leng - 1
|
|
|
|
new_artist = self.getArtistName(index)
|
|
if current_artist != new_artist:
|
|
found = True
|
|
|
|
count = count - 1
|
|
|
|
# Prevent everlasting loop
|
|
if count < 1:
|
|
found = True
|
|
index = self.current_id
|
|
|
|
# If a Backward Search find start of this list
|
|
found = False
|
|
if direction == self.DOWN:
|
|
self.current_artist = new_artist
|
|
while not found:
|
|
index = index - 1
|
|
new_artist = self.getArtistName(index)
|
|
if self.current_artist != new_artist:
|
|
found = True
|
|
index = index + 1
|
|
if index >= leng:
|
|
index = leng-1
|
|
|
|
self.setSearchIndex(index)
|
|
|
|
if self.speech:
|
|
self.speak( str(index+1) + " " + new_artist)
|
|
return
|
|
|
|
# Change option (Used by remote control and non display radio only)
|
|
def changeOption(self,direction):
|
|
|
|
option = self.getOption()
|
|
sOptions = language.getOptionText()
|
|
sOption = sOptions[option]
|
|
|
|
log.message("radio.changeOption " + str(option) + " " + sOption, log.DEBUG)
|
|
|
|
if option == self.RANDOM:
|
|
if self.getRandom():
|
|
self.randomOff()
|
|
else:
|
|
self.randomOn()
|
|
|
|
elif option == self.CONSUME:
|
|
if self.getConsume():
|
|
self.consumeOff()
|
|
else:
|
|
self.consumeOn()
|
|
|
|
elif option == self.REPEAT:
|
|
if self.getRepeat():
|
|
self.repeatOff()
|
|
else:
|
|
self.repeatOn()
|
|
|
|
elif option == self.ALARM:
|
|
self.alarmCycle(direction)
|
|
|
|
elif option == self.STREAMING:
|
|
self.toggleStreaming()
|
|
|
|
elif option == self.RELOADLIB:
|
|
if self.getUpdateLibrary():
|
|
self.setUpdateLibOff()
|
|
else:
|
|
self.setUpdateLibOn()
|
|
|
|
elif option == self.TIMER:
|
|
if self.getTimer():
|
|
if direction == self.RIGHT:
|
|
self.incrementTimer(1)
|
|
else:
|
|
self.decrementTimer(1)
|
|
else:
|
|
self.timerOn()
|
|
|
|
elif option == self.ALARMSETHOURS or option == self.ALARMSETMINS :
|
|
value = 1
|
|
if option == self.ALARMSETHOURS:
|
|
value = 60
|
|
if direction == self.RIGHT:
|
|
self.incrementAlarm(value)
|
|
else:
|
|
self.decrementAlarm(value)
|
|
|
|
self.optionChangedTrue()
|
|
self.speakOption(option)
|
|
return
|
|
|
|
# Handle toggling of source
|
|
def handle_source(self,key):
|
|
if key == 'KEY_UP':
|
|
self.toggleSource(self.UP)
|
|
if key == 'KEY_DOWN':
|
|
self.toggleSource(self.DOWN)
|
|
return
|
|
|
|
# Input Source RADIO, NETWORK or PLAYER
|
|
def getSource(self):
|
|
return self.source
|
|
|
|
def setSource(self,source):
|
|
self.source = source
|
|
|
|
# Reload playlists flag
|
|
def getReload(self):
|
|
return self.reload
|
|
|
|
def setReload(self,reload):
|
|
log.message("radio.setReload " + str(reload), log.DEBUG)
|
|
self.reload = reload
|
|
|
|
# Reload music library flag
|
|
def getUpdateLibrary(self):
|
|
return self.updateLib
|
|
|
|
def setUpdateLibOn(self):
|
|
self.updateLib = True
|
|
|
|
def setUpdateLibOff(self):
|
|
self.updateLib = False
|
|
|
|
# Load new track flag
|
|
def loadNew(self):
|
|
return self.loadnew
|
|
|
|
def setLoadNew(self,loadnew):
|
|
log.message("radio.setLoadNew " + str(loadnew), log.DEBUG)
|
|
self.loadnew = loadnew
|
|
return
|
|
|
|
# Get the Raspberry pi board version from /proc/cpuinfo
|
|
def getBoardRevision(self):
|
|
revision = 1
|
|
with open("/proc/cpuinfo") as f:
|
|
cpuinfo = f.read()
|
|
rev_hex = re.search(r"(?<=\nRevision)[ |:|\t]*(\w+)", cpuinfo).group(1)
|
|
rev_int = int(rev_hex,16)
|
|
if rev_int > 3:
|
|
revision = 2
|
|
self.boardrevision = revision
|
|
log.message("Board revision " + str(self.boardrevision), log.INFO)
|
|
return self.boardrevision
|
|
|
|
# Get the MPD port number
|
|
def getMpdPort(self):
|
|
port = 6600
|
|
if os.path.isfile(MpdPortFile):
|
|
try:
|
|
# port = int(self.execCommand("cat " + MpdPortFile) ) # (Pecus)
|
|
port = int(self.readFromFile(MpdPortFile) ) # (Pecus)
|
|
except ValueError:
|
|
port = 6600
|
|
else:
|
|
log.message("Error reading " + MpdPortFile, log.ERROR)
|
|
|
|
return port
|
|
|
|
# Get options (synchronise with external mpd clients)
|
|
def getOptions(self,stats):
|
|
try:
|
|
random = int(stats.get("random"))
|
|
if random == 1:
|
|
self.random = True
|
|
else:
|
|
self.random = False
|
|
|
|
repeat = int(stats.get("repeat"))
|
|
if repeat == 1:
|
|
self.repeat = True
|
|
else:
|
|
self.repeat = False
|
|
|
|
consume = int(stats.get("consume"))
|
|
if consume == 1:
|
|
self.consume = True
|
|
else:
|
|
self.consume = False
|
|
|
|
except:
|
|
log.message("radio.getOptions get error" + MpdPortFile, log.ERROR)
|
|
return
|
|
|
|
# Get volume and check if it has been changed by any MPD external client
|
|
# Slug MPD calls to no more than per 0.5 second
|
|
def getVolume(self):
|
|
volume = 0
|
|
error = False
|
|
try:
|
|
now = time.time()
|
|
if now > self.volumetime + 0.5:
|
|
stats = self.getStats()
|
|
volume = int(stats.get("volume"))
|
|
self.volumetime = time.time()
|
|
else:
|
|
volume = self.volume
|
|
except:
|
|
log.message("radio.getVolume failed", log.ERROR)
|
|
volume = 1
|
|
error = True
|
|
|
|
if volume == str("None"):
|
|
volume = -1
|
|
error = True
|
|
if volume < 0:
|
|
error = True
|
|
if not error:
|
|
if volume < config.getVolumeMin():
|
|
volume = config.getVolumeMin()
|
|
if volume > config.getVolumeMax():
|
|
wolume = config.getVolumeMax()
|
|
if volume != self.volume:
|
|
if not error:
|
|
self.device_error_cnt = 0
|
|
log.message("radio.getVolume external client changed volume "
|
|
+ str(volume),log.DEBUG)
|
|
self.setVolume(volume)
|
|
self.volumeChange = True
|
|
else:
|
|
self.device_error_cnt += 1
|
|
log.message("radio.getVolume audio device error " + str(volume), log.ERROR)
|
|
|
|
if self.device_error_cnt > 10:
|
|
msg = "Sound device error - exiting"
|
|
log.message("radio._getVolume " + msg, log.ERROR)
|
|
print msg
|
|
sys.exit(1)
|
|
|
|
return self.volume
|
|
|
|
# Check for volume change
|
|
def volumeChanged(self):
|
|
volumeChange = self.volumeChange
|
|
self.volumeChange = False
|
|
return volumeChange
|
|
|
|
# Set volume 0-100
|
|
def setVolume(self,volume):
|
|
if self.muted():
|
|
self.unmute()
|
|
else:
|
|
if volume > config.getVolumeMax():
|
|
volume = config.getVolumeMax()
|
|
elif volume < config.getVolumeMin():
|
|
volume = config.getVolumeMin()
|
|
|
|
try:
|
|
if volume != self.volume:
|
|
log.message("radio.setVolume vol=" + str(volume),log.DEBUG)
|
|
client.setvol(volume)
|
|
self.volume = volume
|
|
# Don't change stored volume (Needed for unmute function)
|
|
if not self.muted():
|
|
self.storeVolume(self.volume)
|
|
except:
|
|
log.message("radio.setVolume error vol=" + str(self.volume),log.ERROR)
|
|
|
|
return self.volume
|
|
|
|
|
|
# Increase volume
|
|
def increaseVolume(self):
|
|
increment = config.getVolumeIncrement()
|
|
volume = self.volume + increment
|
|
log.message("radio.increaseVolume vol=" + str(volume),log.DEBUG)
|
|
volume = self.setVolume(volume)
|
|
return volume
|
|
|
|
# Decrease volume
|
|
def decreaseVolume(self):
|
|
increment = config.getVolumeIncrement()
|
|
volume = self.volume - increment
|
|
log.message("radio.decreaseVolume vol=" + str(volume),log.DEBUG)
|
|
volume = self.setVolume(volume)
|
|
return volume
|
|
|
|
# Mute sound functions (Also stops MPD if not streaming)
|
|
def mute(self):
|
|
log.message("radio.mute streaming=" + str(self.streaming),log.DEBUG)
|
|
try:
|
|
if self.source == self.RADIO:
|
|
client.stop() # Disconnect from stream
|
|
log.message("radio.mute radio stop",log.DEBUG)
|
|
elif self.source == self.PLAYER:
|
|
client.pause(1) # Pause playing track
|
|
log.message("radio.mute player pause",log.DEBUG)
|
|
elif self.source == self.PANDORA:
|
|
try:
|
|
if self.pianobar.isalive():
|
|
self.pianobar.send('p')
|
|
self.isPandoraPaused = True
|
|
log.message("radio.mute pandora pause",log.DEBUG)
|
|
except:
|
|
log.message("radio.mute pianobar pause error",log.DEBUG)
|
|
self.isMuted = True
|
|
except:
|
|
log.message("radio.mute error",log.DEBUG)
|
|
return
|
|
|
|
# Unmute sound fuction, get stored volume
|
|
def unmute(self):
|
|
if self.muted():
|
|
self.volume = self.getStoredVolume()
|
|
log.message("radio.unmute volume=" + str(self.volume),log.DEBUG)
|
|
try:
|
|
if self.source == self.RADIO or self.source == self.PLAYER:
|
|
client.play()
|
|
client.setvol(self.volume)
|
|
log.message("radio.unmute radio or player play",log.DEBUG)
|
|
elif self.source == self.PANDORA:
|
|
try:
|
|
if self.pianobar.isalive():
|
|
self.pianobar.send('P')
|
|
self.isPandoraPaused = False
|
|
else:
|
|
self.setReload(True)
|
|
#self.pandora_start()
|
|
except:
|
|
self.setReload(True)
|
|
#self.pandora_start()
|
|
log.message("radio.unmute pandora unpause or reload",log.DEBUG)
|
|
self.isMuted = False
|
|
except:
|
|
log.message("radio.unmute error",log.ERROR)
|
|
return self.volume
|
|
|
|
def muted(self):
|
|
return self.isMuted
|
|
|
|
# Start MPD (Alarm mode)
|
|
def startMPD(self):
|
|
try:
|
|
client.play()
|
|
except:
|
|
log.message("radio.startMPD error",log.ERROR)
|
|
return
|
|
|
|
# Stop MPD (Alarm mode)
|
|
def stopMPD(self):
|
|
try:
|
|
client.stop()
|
|
except:
|
|
log.message("radio.stopMPD error",log.ERROR)
|
|
return
|
|
|
|
# Get the stored volume
|
|
def getStoredVolume(self):
|
|
volume = 75
|
|
if os.path.isfile(VolumeFile):
|
|
try:
|
|
# volume = int(self.execCommand("cat " + VolumeFile) ) # (Pecus)
|
|
volume = int(self.readFromFile(VolumeFile) ) # (Pecus)
|
|
except ValueError:
|
|
volume = 75
|
|
else:
|
|
log.message("Error reading " + VolumeFile, log.ERROR)
|
|
|
|
return volume
|
|
|
|
# Store volume in volume file
|
|
def storeVolume(self,volume):
|
|
# self.execCommand ("echo " + str(volume) + " > " + VolumeFile) # (Pecus)
|
|
self.writeToFile (VolumeFile,str(volume)) # (Pecus)
|
|
return
|
|
|
|
# Random setting
|
|
def getRandom(self):
|
|
if self.source == self.PLAYER:
|
|
self.random = self.getStoredRandomSetting()
|
|
return self.random
|
|
|
|
def randomOn(self):
|
|
try:
|
|
client.random(1)
|
|
self.random = True
|
|
if self.source == self.PLAYER:
|
|
# self.execCommand ("echo " + str(1) + " > " + RandomSettingFile) # (Pecus)
|
|
self.writeToFile (RandomSettingFile,str(1)) # (Pecus)
|
|
except:
|
|
log.message("radio.randomOn error",log.ERROR)
|
|
return self.random
|
|
|
|
def randomOff(self):
|
|
try:
|
|
client.random(0)
|
|
self.random = False
|
|
if self.source == self.PLAYER:
|
|
# self.execCommand ("echo " + str(0) + " > " + RandomSettingFile) # (Pecus)
|
|
self.writeToFile (RandomSettingFile,str(0)) # (Pecus)
|
|
except:
|
|
log.message("radio.randomOff error",log.ERROR)
|
|
return self.random
|
|
|
|
# Get the stored random setting 0=off 1=on
|
|
def getStoredRandomSetting(self):
|
|
random = 0
|
|
if os.path.isfile(RandomSettingFile):
|
|
try:
|
|
# random = int(self.execCommand("cat " + RandomSettingFile) ) # (Pecus)
|
|
random = int(self.readFromFile(RandomSettingFile) ) # (Pecus)
|
|
except ValueError:
|
|
random = 0
|
|
else:
|
|
log.message("Error reading " + RandomSettingFile, log.ERROR)
|
|
|
|
if random is 1:
|
|
self.random = True
|
|
else:
|
|
self.random = False
|
|
|
|
return self.random
|
|
|
|
# Repeat
|
|
def getRepeat(self):
|
|
return self.repeat
|
|
|
|
def repeatOn(self):
|
|
try:
|
|
client.repeat(1)
|
|
self.repeat = True
|
|
except:
|
|
log.message("radio.repeatOn error",log.ERROR)
|
|
return
|
|
|
|
def repeatOff(self):
|
|
try:
|
|
client.repeat(0)
|
|
self.repeat = False
|
|
except:
|
|
log.message("radio.repeatOff error",log.ERROR)
|
|
return
|
|
|
|
# Consume
|
|
def getConsume(self):
|
|
return self.consume
|
|
|
|
def consumeOn(self):
|
|
try:
|
|
client.consume(1)
|
|
self.consume = True
|
|
except:
|
|
log.message("radio.consumeOn error",log.ERROR)
|
|
return
|
|
|
|
def consumeOff(self):
|
|
try:
|
|
client.consume(0)
|
|
self.consume = False
|
|
except:
|
|
log.message("radio.consumeOff error",log.ERROR)
|
|
return
|
|
|
|
# Timer functions
|
|
def getTimer(self):
|
|
return self.timer
|
|
|
|
def timerOn(self):
|
|
self.timerValue = self.getStoredTimer()
|
|
self.timeTimer = int(time.time())
|
|
self.timer = True
|
|
return self.timer
|
|
|
|
def timerOff(self):
|
|
self.timer = False
|
|
self.timerValue = 0
|
|
return self.timer
|
|
|
|
def getTimerValue(self):
|
|
return self.timerValue
|
|
|
|
def fireTimer(self):
|
|
fireTimer = False
|
|
if self.timer and self.timerValue > 0:
|
|
now = int(time.time())
|
|
if now > self.timeTimer + self.timerValue * 60:
|
|
fireTimer = True
|
|
# Store fired value
|
|
self.storeTimer(self.timerValue)
|
|
self.timerOff()
|
|
return fireTimer
|
|
|
|
# Display the amount of time remaining
|
|
def getTimerString(self):
|
|
tstring = ''
|
|
now = int(time.time())
|
|
value = self.timeTimer + self.timerValue * 60 - now
|
|
if value > 0:
|
|
minutes,seconds = divmod(value,60)
|
|
hours,minutes = divmod(minutes,60)
|
|
if hours > 0:
|
|
tstring = '%d:%02d:%02d' % (hours,minutes,seconds)
|
|
else:
|
|
tstring = '%d:%02d' % (minutes,seconds)
|
|
else:
|
|
tstring = 'off'
|
|
return tstring
|
|
|
|
# Increment timer.
|
|
def incrementTimer(self,inc):
|
|
if self.timerValue >= 60:
|
|
inc = 10
|
|
self.timerValue += inc
|
|
if self.timerValue > self.ONEDAYMINS:
|
|
self.timerValue = self.ONEDAYMINS
|
|
self.timeTimer = int(time.time())
|
|
return self.timerValue
|
|
|
|
def decrementTimer(self,dec):
|
|
if self.timerValue > 60:
|
|
dec = 10
|
|
self.timerValue -= dec
|
|
if self.timerValue < 0:
|
|
self.timerValue = 0
|
|
self.timer = False
|
|
self.timeTimer = int(time.time())
|
|
return self.timerValue
|
|
|
|
# Get the stored timer value
|
|
def getStoredTimer(self):
|
|
timerValue = 0
|
|
if os.path.isfile(TimerFile):
|
|
try:
|
|
# timerValue = int(self.execCommand("cat " + TimerFile) ) # (Pecus)
|
|
timerValue = int(self.readFromFile(TimerFile) ) # (Pecus)
|
|
except ValueError:
|
|
timerValue = 30
|
|
else:
|
|
log.message("Error reading " + TimerFile, log.ERROR)
|
|
return timerValue
|
|
|
|
# Store timer time in timer file
|
|
def storeTimer(self,timerValue):
|
|
# self.execCommand ("echo " + str(timerValue) + " > " + TimerFile) # (Pecus)
|
|
self.writeToFile (TimerFile,str(timerValue)) # (Pecus)
|
|
return
|
|
|
|
# Radio Alarm Functions
|
|
def alarmActive(self):
|
|
alarmActive = False
|
|
if self.alarmType != self.ALARM_OFF:
|
|
alarmActive = True
|
|
return alarmActive
|
|
|
|
# Cycle through alarm types
|
|
def alarmCycle(self,direction):
|
|
if direction == self.UP:
|
|
self.alarmType += 1
|
|
else:
|
|
self.alarmType -= 1
|
|
|
|
if self.alarmType > self.ALARM_LAST:
|
|
self.alarmType = self.ALARM_OFF
|
|
elif self.alarmType < self.ALARM_OFF:
|
|
self.alarmType = self.ALARM_LAST
|
|
|
|
if self.alarmType > self.ALARM_OFF:
|
|
self.alarmTime = self.getStoredAlarm()
|
|
|
|
sType,sHours,sMinutes = self.alarmTime.split(':')
|
|
hours = int(sHours)
|
|
minutes = int(sMinutes)
|
|
self.alarmTime = '%d:%d:%02d' % (self.alarmType,hours,minutes)
|
|
self.storeAlarm(self.alarmTime)
|
|
|
|
return self.alarmType
|
|
|
|
# Switch off the alarm unless repeat or days of the week
|
|
def alarmOff(self):
|
|
if self.alarmType == self.ALARM_ON:
|
|
self.alarmType = self.ALARM_OFF
|
|
return self.alarmType
|
|
|
|
# Increment alarm time
|
|
def incrementAlarm(self,inc):
|
|
sType,sHours,sMinutes = self.alarmTime.split(':')
|
|
hours = int(sHours)
|
|
minutes = int(sMinutes) + inc
|
|
if minutes >= 60:
|
|
minutes = minutes - 60
|
|
hours += 1
|
|
if hours >= 24:
|
|
hours = 0
|
|
self.alarmTime = '%d:%d:%02d' % (self.alarmType,hours,minutes)
|
|
self.storeAlarm(self.alarmTime)
|
|
return '%d:%02d' % (hours,minutes)
|
|
|
|
# Decrement alarm time
|
|
def decrementAlarm(self,dec):
|
|
sType,sHours,sMinutes = self.alarmTime.split(':')
|
|
hours = int(sHours)
|
|
minutes = int(sMinutes) - dec
|
|
if minutes < 0:
|
|
minutes = minutes + 60
|
|
hours -= 1
|
|
if hours < 0:
|
|
hours = 23
|
|
self.alarmTime = '%d:%d:%02d' % (self.alarmType,hours,minutes)
|
|
self.storeAlarm(self.alarmTime)
|
|
return '%d:%02d' % (hours,minutes)
|
|
|
|
# Fire alarm if current hours/mins matches time now
|
|
def alarmFired(self):
|
|
|
|
fireAlarm = False
|
|
if self.alarmType > self.ALARM_OFF:
|
|
sType,sHours,sMinutes = self.alarmTime.split(':')
|
|
type = int(sType)
|
|
hours = int(sHours)
|
|
minutes = int(sMinutes)
|
|
t1 = datetime.datetime.now()
|
|
t2 = datetime.time(hours, minutes)
|
|
weekday = t1.today().weekday()
|
|
|
|
if t1.hour == t2.hour and t1.minute == t2.minute and not self.alarmTriggered:
|
|
# Is this a weekday
|
|
if type == self.ALARM_WEEKDAYS and weekday < 5:
|
|
fireAlarm = True
|
|
elif type < self.ALARM_WEEKDAYS:
|
|
fireAlarm = True
|
|
|
|
if fireAlarm:
|
|
self.alarmTriggered = fireAlarm
|
|
if type == self.ALARM_ON:
|
|
self.alarmOff()
|
|
log.message("radio.larmFired type " + str(type), log.DEBUG)
|
|
else:
|
|
self.alarmTriggered = False
|
|
|
|
return fireAlarm
|
|
|
|
# Get the stored alarm value
|
|
def getStoredAlarm(self):
|
|
alarmValue = ''
|
|
if os.path.isfile(AlarmFile):
|
|
try:
|
|
# alarmValue = self.execCommand("cat " + AlarmFile) # (Pecus)
|
|
alarmValue = self.readFromFile(AlarmFile) # (Pecus)
|
|
except ValueError:
|
|
alarmValue = "0:7:00"
|
|
else:
|
|
log.message("Error reading " + AlarmFile, log.ERROR)
|
|
return alarmValue
|
|
|
|
# Store alarm time in alarm file
|
|
def storeAlarm(self,alarmString):
|
|
# self.execCommand ("echo " + alarmString + " > " + AlarmFile) # (Pecus)
|
|
self.writeToFile (AlarmFile,alarmString) # (Pecus)
|
|
return
|
|
|
|
# Get the actual alarm time
|
|
def getAlarmTime(self):
|
|
sType,sHours,sMinutes = self.alarmTime.split(':')
|
|
hours = int(sHours)
|
|
minutes = int(sMinutes)
|
|
return '%d:%02d' % (hours,minutes)
|
|
|
|
# Get the alarm type
|
|
def getAlarmType(self):
|
|
if self.alarmType > self.ALARM_LAST:
|
|
self.alarmType = self.ALARM_OFF
|
|
return self.alarmType
|
|
|
|
# Get the date format
|
|
def getDateFormat(self):
|
|
return config.getDateFormat()
|
|
|
|
# Get the stored streaming value
|
|
def getStoredStreaming(self):
|
|
streamValue = "off"
|
|
streaming = False
|
|
if os.path.isfile(StreamFile):
|
|
try:
|
|
# streamValue = self.execCommand("cat " + StreamFile) # (Pecus)
|
|
streamValue = self.readFromFile(StreamFile) # (Pecus)
|
|
except ValueError:
|
|
streamValue = "off"
|
|
else:
|
|
log.message("Error reading " + StreamFile, log.ERROR)
|
|
|
|
if streamValue == "on":
|
|
streaming = True
|
|
else:
|
|
streaming = False
|
|
|
|
return streaming
|
|
|
|
# Toggle streaming on off
|
|
# Stream number is 2
|
|
def toggleStreaming(self):
|
|
if self.streamingAvailable():
|
|
if self.streaming:
|
|
self.streamingOff()
|
|
else:
|
|
self.streamingOn()
|
|
else:
|
|
self.streaming = False
|
|
self.storeStreaming("off")
|
|
|
|
return self.streaming
|
|
|
|
# Switch on Icecast2 streaming
|
|
def streamingOn(self):
|
|
output_id = 2
|
|
self.streaming = True
|
|
self.execCommand("service icecast2 start")
|
|
self.execMpcCommand("enable " + str(output_id))
|
|
self.storeStreaming("on")
|
|
self.streamingStatus()
|
|
return self.streaming
|
|
|
|
# Switch off Icecast2 streaming
|
|
def streamingOff(self):
|
|
output_id = 2
|
|
self.streaming = False
|
|
self.execMpcCommand("disable " + str(output_id))
|
|
self.execCommand("service icecast2 stop")
|
|
self.storeStreaming("off")
|
|
self.streamingStatus()
|
|
return self.streaming
|
|
|
|
# Display streaming status
|
|
def streamingStatus(self):
|
|
status = self.execCommand("mpc outputs | grep -i stream")
|
|
if len(status)<1:
|
|
status = "No Icecast streaming"
|
|
log.message(status, log.INFO)
|
|
return
|
|
|
|
# Check if icecast streaming installed
|
|
def streamingAvailable(self):
|
|
fpath = "/usr/bin/icecast2"
|
|
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
|
|
|
# Store stram on or off in streaming file
|
|
def storeStreaming(self,onoff):
|
|
# self.execCommand ("echo " + onoff + " > " + StreamFile) # (Pecus)
|
|
self.writeToFile (StreamFile,onoff) # (Pecus)
|
|
return
|
|
|
|
# Get the streaming value
|
|
def getStreaming(self):
|
|
return self.streaming
|
|
|
|
# Option changed
|
|
def optionChanged(self):
|
|
return self.option_changed
|
|
|
|
def optionChangedTrue(self):
|
|
#if self.speech and self.display_mode != self.MODE_SEARCH:
|
|
# self.speakOption(self.option)
|
|
self.option_changed = True
|
|
return
|
|
|
|
def optionChangedFalse(self):
|
|
self.option_changed = False
|
|
return
|
|
|
|
# Set and get Display mode
|
|
def getDisplayMode(self):
|
|
return self.display_mode
|
|
|
|
# Mode string for debugging
|
|
def getDisplayModeString(self):
|
|
sMode = ["MODE_TIME", "MODE_SEARCH", "MODE_SOURCE",
|
|
"MODE_OPTIONS", "MODE_RSS", "MODE_IP", "MODE_SLEEP"]
|
|
return sMode[self.display_mode]
|
|
|
|
# Set display mode from menu button or IR remote
|
|
def setDisplayMode(self,display_mode):
|
|
if self.display_mode >= 0:
|
|
log.message("radio.setDisplayMode " + str(display_mode), log.DEBUG)
|
|
self.display_mode = display_mode
|
|
if self.speech:
|
|
self.speakMenu(display_mode)
|
|
return
|
|
|
|
# Speak menu
|
|
def speakMenu(self,display_mode):
|
|
# Speak menu
|
|
sMenu = language.getMenuText()
|
|
if display_mode != self.MODE_SLEEP:
|
|
msg = sMenu[display_mode]
|
|
self.speak(msg)
|
|
|
|
if display_mode is self.MODE_OPTIONS:
|
|
if not self.reload:
|
|
self.speakOption(self.option)
|
|
|
|
# Speak option
|
|
def speakOption(self,option):
|
|
|
|
sYes = language.getText("yes")
|
|
sNo = language.getText("no")
|
|
sOn = language.getText("on")
|
|
sOff = language.getText("off")
|
|
sParam = sOff
|
|
|
|
sOptions = language.getOptionText()
|
|
sOption = sOptions[option]
|
|
|
|
if option == self.RANDOM:
|
|
if self.getRandom():
|
|
sParam = sOn
|
|
|
|
elif option == self.CONSUME:
|
|
if self.getConsume():
|
|
sParam = sOn
|
|
|
|
elif option == self.REPEAT:
|
|
if self.getRepeat():
|
|
sParam = sOn
|
|
|
|
#elif option == self.ALARM:
|
|
|
|
elif option == self.STREAMING:
|
|
if self.streaming:
|
|
sParam = sOn
|
|
|
|
elif option == self.RELOADLIB:
|
|
if self.updateLib:
|
|
sParam = sYes
|
|
else:
|
|
sParam = sNo
|
|
|
|
elif option == self.TIMER:
|
|
sOption = "Timer"
|
|
sParam = self.getTimerString()
|
|
sParam = sParam.replace(':', ' ')
|
|
|
|
elif option == self.ALARM:
|
|
sTypes = ['off', 'on', 'repeat', 'week days only']
|
|
type = self.getAlarmType()
|
|
sOption = "Alarm"
|
|
sParam = sTypes[type]
|
|
|
|
elif option == self.ALARMSETHOURS or self.ALARMSETMINS:
|
|
sType,sHours,sMinutes = self.alarmTime.split(':')
|
|
hours = int(sHours)
|
|
minutes = int(sMinutes)
|
|
sOption = "Hours"
|
|
if option == self.ALARMSETMINS:
|
|
sOption = "Minutes"
|
|
sParam = '%d %02d' % (hours,minutes)
|
|
|
|
self.speak(sOption + ' ' + sParam)
|
|
return
|
|
|
|
# Cycle through the menu
|
|
def cycleMenu(self):
|
|
# If a reload has been issued return to TIME display
|
|
if self.getReload():
|
|
display_mode = self.MODE_TIME
|
|
|
|
elif self.search_index+1 != self.current_id: # zmiana kanalu radiowego lub utworu przez menu
|
|
self.current_id = self.search_index+1
|
|
self.play(self.current_id)
|
|
display_mode = self.MODE_TIME
|
|
elif self.pandora_search_index+1 != self.current_pandora_id: # zmiana kanalu pandory przez menu
|
|
self.current_pandora_id = self.pandora_search_index+1
|
|
self.setPandoraStation()
|
|
display_mode = self.MODE_TIME
|
|
else:
|
|
display_mode = self.getDisplayMode() + 1
|
|
if display_mode > self.mode_last:
|
|
display_mode = self.MODE_TIME
|
|
|
|
if self.optionChanged():
|
|
display_mode = self.MODE_TIME
|
|
self.optionChangedFalse()
|
|
|
|
self.setDisplayMode(display_mode)
|
|
return
|
|
|
|
# Set any option you like here
|
|
def getOption(self):
|
|
return self.option
|
|
|
|
def setOption(self,option):
|
|
log.message("radio.setOption option " + str(option), log.DEBUG)
|
|
self.option = option
|
|
self.speakOption(self.option)
|
|
return
|
|
|
|
# Set the menu restriction for radios without a display (retro_radio.py)
|
|
def setLastMode(self,mode_last):
|
|
if mode_last >= self.MODE_OPTIONS and mode_last <= self.mode_last:
|
|
self.mode_last = mode_last
|
|
return self.mode_last
|
|
|
|
# Set the options restriction for radios without a display (retro_radio.py)
|
|
def setLastOption(self,option_last):
|
|
if option_last >= self.REPEAT and option_last <= self.RELOADLIB:
|
|
self.option_last = option_last
|
|
return self.option_last
|
|
|
|
|
|
# Execute system command
|
|
def execCommand(self,cmd):
|
|
p = os.popen(cmd)
|
|
return p.readline().rstrip('\n')
|
|
|
|
# Execute MPC comnmand via OS
|
|
# Some commands are easier using mpc and don't have
|
|
# an effect adverse on performance
|
|
def execMpcCommand(self,cmd):
|
|
return self.execCommand(Mpc + " " + cmd)
|
|
|
|
def getPandoraStations(self):
|
|
log.message("Receiving pandora station list...", log.DEBUG)
|
|
names = []
|
|
ids = []
|
|
x = self.pianobar.expect(['Select station: ', pexpect.TIMEOUT], timeout=20)
|
|
if x == 0:
|
|
# tekst pojawil sie w ciagu 20s - O.K.
|
|
# 'before' is now string of stations I believe
|
|
# break up into separate lines
|
|
a = self.pianobar.before.splitlines()
|
|
# 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)
|
|
else:
|
|
# po 20s nie mamy listy stacji - przekazujemy userowi ze cos jest nie tak
|
|
self.pandora_station_name = '*** No station list ***'
|
|
self.pandora_song_name = '---------------------'
|
|
log.message("radio.getPandoraStations error - no station list from pianobar", log.ERROR)
|
|
self.max_pandora_id = len(names)
|
|
return names, ids
|
|
|
|
# Pobranie co tam wyswietla aktualnie pianobar
|
|
# i umieszczenie w odpowiednich zmiennych
|
|
# na potrzeby innych procedur
|
|
def getPandoraData(self):
|
|
if self.pianobar.isalive():
|
|
pattern_list = self.pianobar.compile_pattern_list(['SONG: ', 'STATION: ', 'Receiving new playlist...', 'TIME: ', 'Error: ', pexpect.TIMEOUT])
|
|
# pattern_list = self.pianobar.compile_pattern_list(['SONG: ', 'STATION: ', 'Receiving new playlist...', pexpect.TIMEOUT])
|
|
end_of_texts = False
|
|
# Process all pending pianobar output
|
|
while not end_of_texts:
|
|
try:
|
|
x = self.pianobar.expect(pattern_list, timeout=0)
|
|
if x == 0:
|
|
# 'SONG: '
|
|
x = self.pianobar.expect(['\r\n', pexpect.TIMEOUT], timeout=0)
|
|
if x == 0: # Artist - Title - from: "Album"
|
|
self.pandora_song_name = self.pianobar.before
|
|
self.setInterrupt() # A to dziala !!!
|
|
elif x == 1:
|
|
# 'STATION: '
|
|
x = self.pianobar.expect([' \| ', pexpect.TIMEOUT], timeout=0)
|
|
if x == 0:
|
|
self.pandora_station_name = self.pianobar.before
|
|
elif x == 2:
|
|
# wisi na odpytywaniu o kolejne utwory ???
|
|
# to sie pianobarowi przytrafia zbyt czesto
|
|
# jak wisi, to trzeba ponownie odpalic stacje
|
|
x = self.pianobar.expect(['Ok.', 'Error: ', pexpect.TIMEOUT], timeout=3) # czekamy 3s. na Ok, moze sie uda zaladowac, a moze komunikat bledu jest
|
|
if x == 2:
|
|
# nie udalo sie - restart
|
|
self.pandora_stop()
|
|
self.pandora_start()
|
|
elif x == 1:
|
|
# jakis blad komunikat zamiast nazwy stacji
|
|
x = self.pianobar.expect(['\r\n', pexpect.TIMEOUT], timeout=0)
|
|
if x == 0:
|
|
self.pandora_station_name = '***' + self.pianobar.before + '***'
|
|
self.pandora_song_name = '---------------------'
|
|
elif x == 3:
|
|
# Time doesn't include newline - prints over itself.
|
|
x = self.pianobar.expect(['\r', pexpect.TIMEOUT], timeout=1)
|
|
if x == 0:
|
|
self.pandora_progress = self.pianobar.before
|
|
if x == 4:
|
|
# 'Error:' - gramy dalej cisze i zamiast nazwy stacji dajemy komunikat o bledzie
|
|
x = self.pianobar.expect(['\r\n', pexpect.TIMEOUT], timeout=0)
|
|
if x == 0:
|
|
self.pandora_station_name = '***' + self.pianobar.before + '***'
|
|
self.pandora_song_name = '---------------------'
|
|
elif x == 5:
|
|
# timeout jestesmy na koncu tekstow
|
|
end_of_texts = True
|
|
except:
|
|
pass
|
|
else:
|
|
# pianobar "zmarl" trzeba powiadomic jakos usera
|
|
#self.pandora_station_name = '*** pianobar dead ***'
|
|
#self.pandora_song_name = '---------------------'
|
|
self.pandora_song_name = '*** pianobar dead ***'
|
|
|
|
return
|
|
|
|
# Get the ID of the currently playing track or station ID
|
|
def getCurrentID(self):
|
|
try:
|
|
currentsong = self.getCurrentSong()
|
|
currentid = int(currentsong.get("pos")) + 1
|
|
|
|
# Only update if the Current ID has changed
|
|
if self.current_id != currentid:
|
|
log.message("radio.getCurrentID New ID " + str(currentid), log.DEBUG)
|
|
self.current_id = currentid
|
|
# Write to current ID file
|
|
# self.execCommand ("echo " + str(currentid) + " > " + self.current_file) # (Pecus)
|
|
self.writeToFile (self.current_file,str(currentid)) # (Pecus)
|
|
self.getIdError = False
|
|
except:
|
|
if not self.getIdError:
|
|
log.message("radio.getCurrentID failed", log.ERROR)
|
|
self.getIdError = True
|
|
|
|
return self.current_id
|
|
|
|
# Check to see if an error occured
|
|
def gotError(self):
|
|
return self.error
|
|
|
|
# Get the error string if a bad channel
|
|
def getErrorString(self):
|
|
self.error = False
|
|
return self.errorStr
|
|
|
|
# See if any error
|
|
def checkStatus(self):
|
|
try:
|
|
status = client.status()
|
|
self.errorStr = str(status.get("error"))
|
|
if self.errorStr != "None":
|
|
if not self.error:
|
|
self.errorStr = (self.errorStr
|
|
+ " (Station " + str(self.current_id) + ")")
|
|
log.message(self.errorStr, log.DEBUG)
|
|
self.error = True
|
|
else:
|
|
# No error
|
|
self.errorStr = ""
|
|
except:
|
|
log.message("checkStatus exception", log.ERROR)
|
|
self.errorStr = "Status exception"
|
|
return self.error
|
|
|
|
# Get the progress of the currently playing track
|
|
def getProgress(self):
|
|
if self.source == self.RADIO or self.source == self.PLAYER:
|
|
line = self.execMpcCommand("status | grep \"\[playing\]\" ")
|
|
lineParts = line.split('/',1)
|
|
if len(lineParts) >= 2:
|
|
line = lineParts[1]
|
|
while line.find(' ') > 0:
|
|
line = line.replace(' ', ' ')
|
|
lineParts = line.split(' ')
|
|
progress = lineParts[1] # + ' ' + lineParts[2] # moze bez procentow ([2]), bo sie timer nie miesci
|
|
# przerabiamy progres na taki jak w Pandorze (czas utwory odliczany do zera)
|
|
line = lineParts[1].replace('/',':') # to zeby latwiej podzelic na liczby
|
|
lineParts = line.split(':') # i mamy liste czterech liczb
|
|
sektrwa = 60 * int(lineParts[0]) + int(lineParts[1]) # czas od poczatku utworu w sekundach
|
|
sekcala = 60 * int(lineParts[2]) + int(lineParts[3]) # dlugosc utworu w sekundach
|
|
sektrwa = sekcala - sektrwa # wyliczamy zamiast tego czas do konca utworu w sekundach
|
|
# i skladamy to ponownie w string do kupy
|
|
progress = "-" + '{:0>2}'.format(sektrwa/60) + ":" + '{:0>2}'.format(sektrwa%60) + "/" + '{:0>2}'.format(sekcala/60) + ":" + '{:0>2}'.format(sekcala%60)
|
|
else:
|
|
progress = ''
|
|
|
|
if self.source == self.PANDORA:
|
|
progress = 'xx/xx'
|
|
if not self.isPandoraPaused: # zabezpieczamy sie przed tym zwisem na rozne sposoby.....
|
|
try:
|
|
self.getPandoraData() # odpalenie tego w tym momencie skutkuje zwisem.... moze wywoluje sie przed odpaleneniem stacji ???
|
|
except:
|
|
pass
|
|
progress = self.pandora_progress
|
|
|
|
return progress
|
|
|
|
# Set the new ID of the currently playing track or station (Also set search index)
|
|
def setCurrentID(self,newid):
|
|
log.message("radio.setCurrentID " + str(newid), log.DEBUG)
|
|
self.current_id = newid
|
|
|
|
# If an error (-1) reset to 1
|
|
if self.current_id <= 0:
|
|
self.current_id = 1
|
|
log.message("radio.setCurrentID reset to " + str(self.current_id), log.DEBUG)
|
|
|
|
# Don't allow an ID greater than the playlist length
|
|
if self.current_id >= len(self.playlist):
|
|
self.current_id = len(self.playlist)
|
|
log.message("radio.setCurrentID reset to " + str(self.current_id), log.DEBUG)
|
|
|
|
self.search_index = self.current_id - 1
|
|
log.message("radio.setCurrentID index= " + str(self.search_index), log.DEBUG)
|
|
# self.execCommand ("echo " + str(self.current_id) + " > " + self.current_file) # (Pecus)
|
|
self.writeToFile (self.current_file,str(self.current_id)) # (Pecus)
|
|
name = self.getCurrentTitle()
|
|
log.message("radio.setCurrentID (" + str(self.current_id) + ") " + name, log.INFO)
|
|
return
|
|
|
|
# Get stats array
|
|
def getStats(self):
|
|
try:
|
|
stats = client.status()
|
|
self.stats = stats # Only if above call works
|
|
except:
|
|
log.message("radio.getStats failed", log.ERROR)
|
|
|
|
self.getOptions(self.stats) # Get options
|
|
return self.stats
|
|
|
|
# Get current state (play or pause or stop) if changed externally
|
|
def getState(self):
|
|
state = "play"
|
|
try:
|
|
stats = self.getStats()
|
|
state = str(stats.get("state"))
|
|
except:
|
|
log.message("radio.getState failed", log.ERROR)
|
|
self.connect(self.mpdport)
|
|
state = self.state
|
|
|
|
if self.source == self.RADIO or self.source == self.PLAYER:
|
|
# Mute if pause or stop but not if there are no playlists
|
|
if (state == "pause" or state == "stop") and self.current_id != 0:
|
|
self.isMuted = True
|
|
else:
|
|
self.isMuted = False
|
|
elif self.source == self.PANDORA:
|
|
state = "play"
|
|
self.isMuted = self.isPandoraPaused
|
|
if self.isPandoraPaused:
|
|
state = "pause"
|
|
self.state = state
|
|
return state
|
|
|
|
# Get paused state (REDUNDANT)
|
|
def paused(self):
|
|
self.getState()
|
|
return self.pause
|
|
|
|
# Get current song information (Only for use within this module)
|
|
def getCurrentSong(self):
|
|
try:
|
|
currentsong = client.currentsong()
|
|
self.currentsong = currentsong
|
|
except:
|
|
# Try re-connect and status
|
|
try:
|
|
if self.connect(self.mpdport):
|
|
currentsong = client.currentsong()
|
|
self.currentsong = currentsong
|
|
except:
|
|
log.message("radio.getCurrentSong failed", log.ERROR)
|
|
return self.currentsong
|
|
|
|
# Get the currently playing radio station from mpd
|
|
# This is usually from "name" but some stations use the "title" field
|
|
def getRadioStation(self):
|
|
if self.source == self.RADIO or self.source == self.PLAYER:
|
|
currentsong = self.getCurrentSong()
|
|
try:
|
|
name = str(currentsong.get("name"))
|
|
except:
|
|
name = "No name"
|
|
# If no name returned check that the file name is returned OK
|
|
# and use name from the search index
|
|
if name == "None":
|
|
try:
|
|
time.sleep(0.2)
|
|
currentsong.get("file")
|
|
name = self.getStationName(self.search_index)
|
|
except:
|
|
name = "Bad stream (" + str(self.current_id) + ")"
|
|
|
|
if self.display_playlist_number:
|
|
name = name + ' (' + str(self.current_id) + ')'
|
|
self.stationName = translate.escape(name)
|
|
|
|
# Use name from the playlist as the station name
|
|
if self.stationNamesSource == config.LIST :
|
|
self.stationName = self.getStationName(self.current_id -1)
|
|
|
|
if self.source == self.PANDORA:
|
|
self.stationName = 'no station information'
|
|
self.getPandoraData()
|
|
self.stationName = self.pandora_station_name
|
|
|
|
return self.stationName
|
|
|
|
# Get the title of the currently playing station or track from mpd
|
|
def getCurrentTitle(self):
|
|
if self.source == self.RADIO or self.source == self.PLAYER:
|
|
try:
|
|
currentsong = self.getCurrentSong()
|
|
title = str(currentsong.get("title"))
|
|
title = translate.escape(title)
|
|
except:
|
|
title = ''
|
|
|
|
if title == 'None':
|
|
title = ''
|
|
|
|
try:
|
|
genre = str(currentsong.get("genre"))
|
|
except:
|
|
genre = ''
|
|
if genre == 'None':
|
|
genre = ''
|
|
|
|
# If the title contained the station name blank it out
|
|
if title == self.stationName:
|
|
title = ''
|
|
|
|
|
|
if self.channelChanged:
|
|
self.channelChanged = False
|
|
if config.verbose():
|
|
if self.source == self.RADIO or self.source == self.PANDORA:
|
|
sSource = "Station "
|
|
else:
|
|
sSource = "Track "
|
|
if self.speech:
|
|
self.speak(sSource + str(self.current_id)
|
|
+ ' ' + self.stationName)
|
|
if title != self.stationTitle and len(title) > 0:
|
|
log.message ("Title: " + str(title), log.DEBUG)
|
|
|
|
self.stationTitle = title
|
|
|
|
if self.source == self.PANDORA:
|
|
title = 'no song information'
|
|
self.getPandoraData()
|
|
title = self.pandora_song_name
|
|
|
|
return title
|
|
|
|
# Get the currently playing radio station from mpd
|
|
# Returns the same format as the mpc current command
|
|
def getCurrentStation(self):
|
|
name = self.getRadioStation()
|
|
title = self.getCurrentTitle()
|
|
if len(title) > 0:
|
|
currentPlaying = name + ": " + title
|
|
else:
|
|
currentPlaying = name
|
|
self.checkStatus()
|
|
return currentPlaying
|
|
|
|
# Get the name of the current artist mpd (Music librarry only)
|
|
def getCurrentArtist(self):
|
|
try:
|
|
currentsong = self.getCurrentSong()
|
|
title = str(currentsong.get("title"))
|
|
title = translate.escape(title)
|
|
artist = str(currentsong.get("artist"))
|
|
if str(artist) == 'None':
|
|
artist = "Unknown artist"
|
|
self.artist = artist
|
|
except:
|
|
log.message ("radio.getCurrentArtist error", log.ERROR)
|
|
return self.artist
|
|
|
|
# Get bit rate - aways returns 0 in diagnostic mode
|
|
def getBitRate(self):
|
|
try:
|
|
status = client.status()
|
|
bitrate = int(status.get('bitrate'))
|
|
except:
|
|
bitrate = -1
|
|
return bitrate
|
|
|
|
# Get the last ID stored in /var/lib/radiod
|
|
def getStoredID(self,current_file):
|
|
current_id = 0
|
|
if os.path.isfile(self.current_file):
|
|
current_id = 1
|
|
try:
|
|
# current_id = int(self.execCommand("cat " + self.current_file) ) # (Pecus)
|
|
current_id = int(self.readFromFile(self.current_file) ) # (Pecus)
|
|
except ValueError:
|
|
current_id = 1
|
|
else:
|
|
log.message("Error reading " + self.current_file, log.ERROR)
|
|
|
|
if current_id <= 0:
|
|
current_id = 1
|
|
return current_id
|
|
|
|
# Po potwierdzeniu decyzji (+, -, ban) wyslanie odpowiedniego rozkazu do Pandory
|
|
def setPandoraDecision(self, direction):
|
|
if direction == self.UP:
|
|
self.PandoraPress('+') # Like
|
|
elif direction == self.DOWN:
|
|
self.PandoraPress('-') # Don't like (ban)
|
|
elif direction == self.LEFT:
|
|
self.PandoraPress('t') # Tired (ban for 1 month)
|
|
return
|
|
|
|
# Nastepny utwor pandory
|
|
def PandoraNextTrack(self):
|
|
# dodatkowe zabezpieczenia sprawdzamy czy gra, a jak gra to czy faktycznie proces zyje
|
|
if not self.isPandoraPaused:
|
|
if self.pianobar.isalive():
|
|
self.pianobar.send('n')
|
|
log.message('Pandora next track', log.DEBUG)
|
|
return
|
|
|
|
# Wyslanie do pianobar wcisniecia klawisza
|
|
def PandoraPress(self,key):
|
|
# dodatkowe zabezpieczenia sprawdzamy czy gra, a jak gra to czy faktycznie proces zyje
|
|
if not self.isPandoraPaused:
|
|
if self.pianobar.isalive():
|
|
self.pianobar.send(key)
|
|
log.message('Pandora key: ' + key, log.DEBUG)
|
|
return
|
|
|
|
# Zmiana stacji pandory
|
|
def setPandoraStation(self):
|
|
# Entering station selection menu. Don't return to volume
|
|
# select, regardless of outcome, just return to normal play.
|
|
if self.pianobar.isalive():
|
|
self.pianobar.send('s')
|
|
# Just keep the list we made at start-up
|
|
# stationList, stationIDs = getStations()
|
|
log.message('Selecting pandora station ' + self.pandora_stationIDs[self.current_pandora_id - 1], log.DEBUG)
|
|
self.pianobar.sendline(self.pandora_stationIDs[self.current_pandora_id - 1])
|
|
self.pandora_search_index = self.current_pandora_id - 1
|
|
# zapis do pliku nie przez setCurrentID bo za duzo sprawdza a w pandorze ID moze sie np rownac 0
|
|
self.current_file = CurrentPandoraFile
|
|
self.writeToFile (self.current_file,str(self.current_pandora_id)) # (Pecus)
|
|
# self.setCurrentID(self.current_pandora_id)
|
|
time.sleep(1) # nie wiedziec czemu ten sleep powoduje ze nazwy utworow i stacji po ich zmianie wyswietlaja sie szybko
|
|
else:
|
|
log.message('radio.setPandoraStation error - pianobar is dead?', log.ERROR)
|
|
return
|
|
|
|
|
|
# Change radio station up
|
|
def channelUp(self):
|
|
new_id = self.getCurrentID()
|
|
if self.source == self.RADIO or self.source == self.PLAYER:
|
|
new_id = self.getCurrentID() + 1
|
|
log.message("radio.channelUp " + str(new_id), log.DEBUG)
|
|
if new_id > len(self.playlist):
|
|
new_id = 1
|
|
self.play(new_id)
|
|
else:
|
|
try:
|
|
client.next()
|
|
except:
|
|
log.message("radio.channelUp error", log.ERROR)
|
|
|
|
new_id = self.getCurrentID()
|
|
self.setCurrentID(new_id)
|
|
|
|
# If any error MPD will skip to next channel
|
|
self.checkStatus()
|
|
self.channelChanged = True
|
|
if self.source == self.PANDORA:
|
|
self.current_pandora_id = self.current_pandora_id + 1
|
|
if self.current_pandora_id > self.max_pandora_id:
|
|
self.current_pandora_id = 1
|
|
log.message("pandora.channelUp " + str(self.current_pandora_id), log.DEBUG)
|
|
self.setPandoraStation()
|
|
self.channelChanged = True
|
|
|
|
return new_id
|
|
|
|
# Change radio station down
|
|
def channelDown(self):
|
|
new_id = self.getCurrentID()
|
|
if self.source == self.RADIO or self.source == self.PLAYER:
|
|
new_id = self.getCurrentID() - 1
|
|
log.message("radio.channelDown " + str(new_id), log.DEBUG)
|
|
if new_id <= 0:
|
|
new_id = len(self.playlist)
|
|
self.play(new_id)
|
|
else:
|
|
try:
|
|
client.previous()
|
|
except:
|
|
log.message("radio.channelDown error", log.ERROR)
|
|
|
|
new_id = self.getCurrentID()
|
|
self.setCurrentID(new_id)
|
|
|
|
# Check if error if so try next channel down
|
|
if self.checkStatus():
|
|
new_id -= 1
|
|
if new_id <= 0:
|
|
new_id = len(self.playlist)
|
|
self.play(new_id)
|
|
self.channelChanged = True
|
|
if self.source == self.PANDORA:
|
|
self.current_pandora_id = self.current_pandora_id - 1
|
|
if self.current_pandora_id < 1:
|
|
self.current_pandora_id = self.max_pandora_id
|
|
log.message("pandora.channelDown " + str(self.current_pandora_id), log.DEBUG)
|
|
self.setPandoraStation()
|
|
self.channelChanged = True
|
|
|
|
return new_id
|
|
|
|
# Toggle the input source (Reload is done when Reload requested)
|
|
def toggleSource(self,direction):
|
|
# Remember! RADIO=0, PLAYER=1, PANDORA=2 always!
|
|
if direction == self.UP:
|
|
self.source +=1
|
|
if self.source == self.PANDORA and (not config.getPandoraAvailable()):
|
|
self.source +=1
|
|
elif direction == self.DOWN:
|
|
self.source -=1
|
|
if self.source < self.RADIO:
|
|
if config.getPandoraAvailable():
|
|
self.source = self.PANDORA
|
|
else:
|
|
self.source = self.PANDORA - 1 # PLAYER
|
|
if self.source > self.PANDORA:
|
|
self.source = self.RADIO
|
|
|
|
self.setReload(True)
|
|
|
|
if self.speech:
|
|
self.speak(sSource)
|
|
|
|
return self.source
|
|
|
|
# Set the input source to radio (Reload is done when Reload requested) (Pecus)
|
|
def setRadioSource(self): # (Pecus)
|
|
if self.source != self.RADIO: # (Pecus)
|
|
self.source = self.RADIO # (Pecus)
|
|
sSource = language.getText('source_radio') # (Pecus)
|
|
self.setReload(True) # (Pecus)
|
|
if self.speech: # (Pecus)
|
|
self.speak(sSource) # (Pecus)
|
|
return self.source # (Pecus)
|
|
|
|
# Set the input source to player (Reload is done when Reload requested) (Pecus)
|
|
def setPlayerSource(self): # (Pecus)
|
|
if self.source != self.PLAYER: # (Pecus)
|
|
self.source = self.PLAYER # (Pecus)
|
|
sSource = language.getText('source_media') # (Pecus)
|
|
self.setReload(True) # (Pecus)
|
|
if self.speech: # (Pecus)
|
|
self.speak(sSource) # (Pecus)
|
|
return self.source # (Pecus)
|
|
|
|
# Set the input source to pandora (Reload is done when Reload requested) (Pecus)
|
|
def setPandoraSource(self): # (Pecus)
|
|
if config.getPandoraAvailable():
|
|
if self.source != self.PANDORA: # (Pecus)
|
|
self.source = self.PANDORA # (Pecus)
|
|
sSource = language.getText('source_pandora') # (Pecus)
|
|
self.setReload(True) # (Pecus)
|
|
if self.speech: # (Pecus)
|
|
self.speak(sSource) # (Pecus)
|
|
return self.source # (Pecus)
|
|
|
|
|
|
def pandora_stop(self):
|
|
log.message("radio.pandora_stop", log.DEBUG)
|
|
try:
|
|
if self.pianobar.isalive():
|
|
log.message("radio.pandora_stop terminate() pianobar process", log.DEBUG)
|
|
self.pianobar.sendcontrol('c') # wysylamy Ctrl-C jakby akurat nie byl w trybie odtwarzania
|
|
self.pianobar.send('q') # a teraz normalnie quit
|
|
self.pianobar.terminate(True) # aa to nie wiem po co ale dla pewnosci :)
|
|
except:
|
|
pass
|
|
self.isPandoraPaused = True
|
|
#log.message("radio.pandora_stop kill pianobar proces", log.DEBUG)
|
|
#self.execCommand ("killall pianobar")
|
|
self.pandora_station_name = ''
|
|
self.pandora_song_name = ''
|
|
self.pandora_progress = ''
|
|
return
|
|
|
|
# Load radio stations
|
|
def loadStations(self):
|
|
self.pandora_stop()
|
|
log.message("radio.loadStations", log.DEBUG)
|
|
if self.speech:
|
|
self.speak(language.getText('loading_radio'))
|
|
try:
|
|
client.clear()
|
|
except:
|
|
log.message("radio.loadStations mpc clear error", log.ERROR)
|
|
|
|
dirList = os.listdir(PlaylistsDirectory)
|
|
dirList.sort()
|
|
for fname in dirList:
|
|
fname = fname.rstrip()
|
|
if fname is "share":
|
|
continue
|
|
if os.path.isdir(fname):
|
|
continue
|
|
|
|
# MPD version 0.19 plus does not require extions such as m3u, pls
|
|
if not self.use_playlist_extensions:
|
|
try:
|
|
fname,ext = fname.split('.')
|
|
except:
|
|
log.message("radio.loadStations missing file extension in" + fname, log.ERROR)
|
|
continue
|
|
|
|
log.message("load " + fname, log.DEBUG)
|
|
try:
|
|
client.load(fname)
|
|
except:
|
|
log.message("radio.loadStations failed " + fname, log.ERROR)
|
|
|
|
self.randomOff()
|
|
self.consumeOff()
|
|
self.repeatOff()
|
|
self.playlist = self.createPlayList()
|
|
self.current_file = CurrentStationFile
|
|
self.current_id = self.getStoredID(self.current_file)
|
|
self.play(self.current_id)
|
|
self.search_index = self.current_id - 1
|
|
self.source = self.RADIO
|
|
return
|
|
|
|
# Load music library
|
|
def loadMusic(self):
|
|
self.pandora_stop()
|
|
log.message("radio.loadMusic", log.DEBUG)
|
|
if self.speech:
|
|
self.speak(language.getText('loading_media'))
|
|
self.execMpcCommand("stop")
|
|
self.execMpcCommand("clear")
|
|
directory = "/var/lib/mpd/music/"
|
|
|
|
dirList=os.listdir(directory)
|
|
dirList.sort()
|
|
for fname in dirList:
|
|
fname = fname.strip("\n")
|
|
if os.path.isfile(directory+fname):
|
|
continue
|
|
path = directory + fname
|
|
nfiles = len(os.listdir(path))
|
|
if nfiles > 0:
|
|
cmd = "add \"" + fname + "\""
|
|
log.message("radio.loadMusic " + cmd,log.DEBUG)
|
|
log.message(str(nfiles) + " files/directories found",log.DEBUG)
|
|
try:
|
|
self.execMpcCommand(cmd)
|
|
except:
|
|
log.message("Failed to load music directory " + fname, log.ERROR)
|
|
else:
|
|
log.message(path + " is empty", log.INFO)
|
|
|
|
self.playlist = self.createPlayList()
|
|
self.current_file = CurrentTrackFile
|
|
self.current_id = self.getStoredID(self.current_file)
|
|
|
|
# Old playlists may have been longer.
|
|
length = len(self.playlist)
|
|
if self.current_id > length:
|
|
self.current_id = 1
|
|
log.message("radio.loadMusic ID " + str(self.current_id), log.DEBUG)
|
|
|
|
# Important use mpc not python-mpd calls as these give problems
|
|
if length > 0:
|
|
log.message("radio.loadMusic play " + str(self.current_id), log.DEBUG)
|
|
self.execMpcCommand("play " + str(self.current_id))
|
|
self.search_index = self.current_id - 1
|
|
self.execMpcCommand("random on")
|
|
self.execMpcCommand("repeat off")
|
|
self.execMpcCommand("consume off")
|
|
self.random = True # Random play
|
|
self.repeat = False # Repeat play
|
|
self.consume = False # Consume tracks
|
|
else:
|
|
log.message("radio.loadMusic playlist length = " + str(length), log.ERROR)
|
|
|
|
log.message("radio.loadMusic complete", log.DEBUG)
|
|
return length
|
|
|
|
def pandora_start(self):
|
|
# kasujemy plik stanu, by pianobar startowal na czysto
|
|
# jak jest ten plik, to od razu zaczyna grac - bez listy
|
|
# stacji itp. I program glupieje
|
|
self.execCommand ("rm /home/pi/.config/pianobar/state")
|
|
log.message("radio.pandora_start", log.DEBUG)
|
|
#self.pandora_stop() # dla pewnosci
|
|
self.pandora_decision = self.OK
|
|
log.message("radio.pandora_start Spawning pianobar...", log.DEBUG)
|
|
self.pianobar = pexpect.spawn('sudo -u pi pianobar')
|
|
# Sprawdzmy bledy sieci czy logowania
|
|
log.message("radio.pandora_start Wait for pianobar Login...", log.DEBUG)
|
|
x = self.pianobar.expect(['Login... ', pexpect.TIMEOUT], timeout=10)
|
|
if x == 0: # Jest Login - czekamy dalej
|
|
log.message("radio.pandora_start Wait LF after Login...", log.DEBUG)
|
|
x = self.pianobar.expect(['\r\n', pexpect.TIMEOUT], timeout=25)
|
|
if x == 0: # nie wisi na login wiec sprawdzamy jaki komunikat
|
|
pmessage = self.pianobar.before
|
|
else:
|
|
pmessage = "Login timeout error" # wisi na "Login..." ponad 25 s.
|
|
log.message("radio.pandora_start kill pianobar proces", log.DEBUG)
|
|
self.execCommand ("killall pianobar") # wisi wiec kilujemy
|
|
else: # Przez 10s nie pojawilo sie "Login..."
|
|
pmessage = "Connection timeout error" # wisi bez "Login..." ponad 10 s.
|
|
log.message("radio.pandora_start kill pianobar proces", log.DEBUG)
|
|
self.execCommand ("killall pianobar") # wisi wiec kilujemy
|
|
#if not(self.pianobar.isalive()):
|
|
# pmessage = 'Network error'
|
|
log.message("radio.pandora_start Pianobar login message: " + pmessage, log.DEBUG)
|
|
# jesli nie ma bledow logowania pojawi sie lista stacji
|
|
if pmessage == 'Ok.':
|
|
# czekamy 30s na info o pobraniu listy stacji
|
|
log.message("radio.pandora_start Wait for pianobar stations list...", log.DEBUG)
|
|
x = self.pianobar.expect(['Get stations... Ok.\r\n', pexpect.TIMEOUT], timeout=30)
|
|
if x == 0: # lista pobrana odpalamy
|
|
self.pandora_stationList, self.pandora_stationIDs = self.getPandoraStations()
|
|
if self.current_pandora_id > self.max_pandora_id:
|
|
self.current_pandora_id = 1
|
|
log.message('radio.pandora_start Selecting pandora station ' + self.pandora_stationIDs[self.current_pandora_id - 1], log.DEBUG)
|
|
self.pianobar.sendline(self.pandora_stationIDs[self.current_pandora_id - 1])
|
|
self.pandora_search_index = self.current_pandora_id - 1
|
|
self.isPandoraPaused = False
|
|
else:
|
|
# przez 30s nie bylo Ok. przy pobieraniu listy stacji...
|
|
self.pandora_station_name = '*** Station list error ***'
|
|
self.pandora_song_name = '---------------------'
|
|
self.isPandoraPaused = True
|
|
else:
|
|
# po logowaniu nie bylo Ok. a to co bylo wyswietlamy zamiast nazwy stacji
|
|
self.pandora_station_name = '** ' + pmessage + ' **'
|
|
self.pandora_song_name = '---------------------'
|
|
self.isPandoraPaused = True
|
|
return
|
|
|
|
#Load Pandora
|
|
def loadPandora(self):
|
|
self.pandora_stop()
|
|
log.message("radio.loadPandora", log.DEBUG)
|
|
if self.speech:
|
|
self.speak(language.getText('loading_pandora'))
|
|
#stop MPD
|
|
#client.pause(1)
|
|
client.stop()
|
|
log.message("radio.loadPandora MPD stopped", log.DEBUG)
|
|
self.current_file = CurrentPandoraFile
|
|
self.current_pandora_id = self.getStoredID(self.current_file)
|
|
self.pandora_start()
|
|
log.message("radio.loadPandora Pandora started", log.DEBUG)
|
|
return
|
|
|
|
|
|
# Update music library
|
|
def updateLibrary(self):
|
|
log.message("radio.updateLibrary", log.DEBUG)
|
|
self.execMpcCommand("-w update")
|
|
self.loadMusic()
|
|
self.setUpdateLibOff()
|
|
return
|
|
|
|
# Play a new track using search index
|
|
def playNew(self,index):
|
|
self.setLoadNew(False)
|
|
self.play(index + 1)
|
|
return
|
|
|
|
# Play a track number (Starts at 1)
|
|
def play(self,number):
|
|
log.message("radio.play " + str(number), log.DEBUG)
|
|
log.message("radio.play Playlist len " + str(len(self.playlist)), log.DEBUG)
|
|
if number > 0 and number <= len(self.playlist):
|
|
self.current_id = number
|
|
self.setCurrentID(self.current_id)
|
|
else:
|
|
log.message("play invalid station/track number "+ str(number), log.ERROR)
|
|
self.setCurrentID(1)
|
|
|
|
# Client play function starts at 0 not 1
|
|
log.message("play station/track number "+ str(self.current_id), log.DEBUG)
|
|
try:
|
|
client.play(self.current_id-1)
|
|
self.checkStatus()
|
|
except:
|
|
log.message("radio.play error", log.ERROR)
|
|
return
|
|
|
|
# Clear streaming and other errors
|
|
def clearError(self):
|
|
log.message("radio.clearError", log.DEBUG)
|
|
try:
|
|
client.clearerror()
|
|
self.errorStr = ""
|
|
self.error = False
|
|
except:
|
|
log.message("radio.clearError failed", log.ERROR)
|
|
return
|
|
|
|
# Get list of tracks or stations
|
|
def getPlayList(self):
|
|
return self.playlist
|
|
|
|
# Create list of tracks or stations
|
|
def createPlayList(self):
|
|
log.message("radio.createPlaylist", log.DEBUG)
|
|
list = []
|
|
line = ""
|
|
cmd = "playlist"
|
|
p = os.popen(Mpc + " " + cmd)
|
|
while True:
|
|
line = p.readline().strip('\n')
|
|
if line.__len__() < 1:
|
|
break
|
|
line = translate.escape(line)
|
|
if line.startswith("http:") and '#' in line:
|
|
url,line = line.split('#')
|
|
list.append(line)
|
|
self.playlist = list
|
|
log.message("radio.createPlaylist length " + str(len(self.playlist)), log.DEBUG)
|
|
return self.playlist
|
|
|
|
# Get the length of the current list
|
|
def getListLength(self):
|
|
return len(self.playlist)
|
|
|
|
# Display artist True or False
|
|
def displayArtist(self):
|
|
return self.display_artist
|
|
|
|
def setDisplayArtist(self,dispArtist):
|
|
self.display_artist = dispArtist
|
|
|
|
# Set Search index
|
|
def getSearchIndex(self):
|
|
return self.search_index
|
|
|
|
def setSearchIndex(self,index):
|
|
self.search_index = index
|
|
return
|
|
|
|
# Get Pandora Radio station name by Index
|
|
def getPandoraStationName(self,index):
|
|
station = ""
|
|
try:
|
|
station = self.pandora_stationList[index]
|
|
except:
|
|
log.message("radio.getPandoraStationName bad index " + str(index), log.ERROR)
|
|
station = "Station index error"
|
|
return station
|
|
|
|
# Get Radio station name by Index (Used in search routines)
|
|
def getStationName(self,index):
|
|
station = ""
|
|
if self.source == self.RADIO:
|
|
station = "No stations found"
|
|
else:
|
|
station = "No tracks found"
|
|
try:
|
|
if len(self.playlist) > 0:
|
|
station = self.playlist[index]
|
|
except:
|
|
log.message("radio.getStationName bad index " + str(index), log.ERROR)
|
|
return station
|
|
|
|
# Get track name by Index
|
|
def getTrackNameByIndex(self,index):
|
|
if len(self.playlist) < 1:
|
|
track = "No tracks"
|
|
else:
|
|
sections = self.playlist[index].split(' - ')
|
|
leng = len(sections)
|
|
if leng > 1:
|
|
track = sections[1]
|
|
else:
|
|
track = "No track"
|
|
track = translate.escape(track)
|
|
if str(track) == 'None':
|
|
track = "Unknown track"
|
|
return track
|
|
|
|
# Get artist name by Index
|
|
def getArtistName(self,index):
|
|
if len(self.playlist) < 1:
|
|
artist = "No playlists"
|
|
else:
|
|
sections = self.playlist[index].split(' - ')
|
|
leng = len(sections)
|
|
if leng > 1:
|
|
artist = sections[0]
|
|
else:
|
|
artist = "Unknown artist"
|
|
artist = translate.escape(artist)
|
|
return artist
|
|
|
|
# Switch store and retrieval routines
|
|
def setSwitch(self,switch):
|
|
self.switch = switch
|
|
return
|
|
|
|
def getSwitch(self):
|
|
return self.switch
|
|
|
|
# Routines for storing rotary encoder events
|
|
def incrementEvent(self):
|
|
self.numevents += 1
|
|
return self.numevents
|
|
|
|
def decrementEvent(self):
|
|
self.numevents -= 1
|
|
if self.numevents < 0:
|
|
self.numevents = 0
|
|
return self.numevents
|
|
|
|
def getEvents(self):
|
|
return self.numevents
|
|
|
|
def resetEvents(self):
|
|
self.numevents = 0
|
|
return self.numevents
|
|
|
|
# Version number
|
|
def getVersion(self):
|
|
return self.version
|
|
|
|
# Get the the backlight color number (option = bg_color etc)
|
|
def getBackColor(self,option):
|
|
return config.getBackColor(option)
|
|
|
|
# Get the the backlight color number
|
|
def getBackColorName(self,iColor):
|
|
return config.getBackColorName(iColor)
|
|
|
|
# Cycle colors
|
|
def cycleColor(self,direction):
|
|
return config.cycleColor(direction)
|
|
|
|
# Get I2C backpack type
|
|
def getBackPackType(self):
|
|
self.i2c_backpack = config.getBackPackType()
|
|
log.message("Backpack type " + str(self.i2c_backpack), log.DEBUG)
|
|
return self.i2c_backpack
|
|
|
|
# Get I2C address
|
|
def getI2Caddress(self):
|
|
self.i2c_address = config.getI2Caddress()
|
|
log.message("I2C address " + hex(self.i2c_address), log.DEBUG)
|
|
return self.i2c_address
|
|
|
|
# Set an interrupt received
|
|
def setInterrupt(self):
|
|
self.interrupt = True
|
|
return
|
|
|
|
# See if interrupt received from IR remote control
|
|
def getInterrupt(self):
|
|
interrupt = self.interrupt
|
|
self.interrupt = False
|
|
return interrupt
|
|
|
|
# Load media
|
|
def loadMedia(self):
|
|
self.unmountAll()
|
|
self.mountUsb()
|
|
self.mountShare()
|
|
self.loadMusic()
|
|
self.random = self.getStoredRandomSetting()
|
|
return
|
|
|
|
# Mount the USB stick
|
|
def mountUsb(self):
|
|
usbok = False
|
|
if os.path.exists("/dev/sda1"):
|
|
device = "/dev/sda1"
|
|
usbok = True
|
|
|
|
elif os.path.exists("/dev/sdb1"):
|
|
device = "/dev/sdb1"
|
|
usbok = True
|
|
|
|
if usbok:
|
|
self.execCommand("/bin/mount -o rw,uid=1000,gid=1000 "+ device + " /media")
|
|
log.message(device + " mounted on /media", log.DEBUG)
|
|
dirList=os.listdir(MusicDirectory)
|
|
for fname in dirList:
|
|
file = MusicDirectory + '/' + fname
|
|
# Skip playlist files
|
|
if os.path.isfile(file):
|
|
continue
|
|
log.message("Loading " + file, log.DEBUG)
|
|
else:
|
|
msg = "No USB stick found!"
|
|
log.message(msg, log.WARNING)
|
|
return
|
|
|
|
|
|
# Mount any remote network drive
|
|
def mountShare(self):
|
|
if os.path.exists("/var/lib/radiod/share"):
|
|
# myshare = self.execCommand("cat /var/lib/radiod/share") # (Pecus)
|
|
myshare = self.readFromFile("/var/lib/radiod/share") # (Pecus)
|
|
if myshare[:1] != '#':
|
|
self.execCommand(myshare)
|
|
log.message(myshare,log.DEBUG)
|
|
return
|
|
|
|
# Unmount all drives
|
|
def unmountAll(self):
|
|
self.execCommand("/bin/umount /dev/sda1 2>&1 >/dev/null") # Unmount USB stick
|
|
self.execCommand("/bin/umount /dev/sdb1 2>&1 >/dev/null") # Unmount USB stick
|
|
self.execCommand("/bin/umount /share 2>&1 >/dev/null") # Unmount network drive
|
|
return
|
|
|
|
# Speak a message
|
|
def speak(self, msg):
|
|
msg = msg.lstrip()
|
|
if self.speech and len(msg) > 1:
|
|
if msg[0] != '!':
|
|
speech_volume = config.getSpeechVolume()
|
|
volume = self.volume * speech_volume/100
|
|
if volume < 5:
|
|
volume = 5
|
|
self.mute()
|
|
language.speak(msg,volume)
|
|
self.unmute()
|
|
return
|
|
# Speeak a message always (if speech os off too) (Pecus)
|
|
def speakAlways(self, msg): # (Pecus)
|
|
speech_volume = config.getSpeechVolume() # (Pecus)
|
|
volume = self.volume * 2 * speech_volume/100 # (Pecus)
|
|
self.mute() # (Pecus)
|
|
language.speak(msg,volume) # (Pecus)
|
|
self.unmute() # (Pecus)
|
|
return # (Pecus)
|
|
|
|
# Speak a message
|
|
def speakInformation(self):
|
|
log.message("radio.speakInformation" , log.DEBUG)
|
|
msg = ''
|
|
if self.source == self.RADIO:
|
|
#current = "Station " + str(self.current_id ) # (Pecus)
|
|
current = language.getText('Station') + " " + str(self.current_id ) # from language file :) (Pecus)
|
|
name = self.getStationName(self.search_index)
|
|
title = self.getCurrentTitle()
|
|
elif self.source == self.PLAYER:
|
|
#current = "Track " + str(self.current_id ) # (Pecus)
|
|
current = language.getText('Track') + " " + str(self.current_id ) # from language file :) (Pecus)
|
|
name = self.getCurrentArtist()
|
|
title = self.getCurrentTitle()
|
|
elif self.source == self.PANDORA:
|
|
current = language.getText('Station') + " " + str(self.current_pandora_id ) # from language file :) (Pecus)
|
|
name = self.pandora_station_name
|
|
title = self.pandora_song_name
|
|
|
|
# Can you speek hours in polish - Yes :) (Pecus)
|
|
# It may by not be pretty, but it works !
|
|
#sTime = strftime("%H %M") # (Pecus)
|
|
sMin = strftime("%M") # (Pecus)
|
|
sHour = strftime("%H") # (Pecus)
|
|
sTime = language.getText('h'+sHour) + " " + sMin # hour from language file (Pecus)
|
|
time = language.getText('the_time') + " " + sTime
|
|
#self.speak(time) # one command instead of 4 sounds better in player mode (Pecus)
|
|
#self.speak(current) # (Pecus)
|
|
#self.speak(name) # (Pecus)
|
|
#self.speak(title) # (Pecus)
|
|
self.speakAlways(time + ' . ' + current + '. ' + name + '. ' + title) # says even id excluded speaking in config file (Pecus)
|
|
return
|
|
|
|
# Is speech enabled
|
|
def speechEnabled(self):
|
|
return self.speech
|
|
|
|
# Get language text
|
|
def getLangText(self,label):
|
|
return language.getText(label)
|
|
|
|
# Get switch GPIO configuration
|
|
def getSwitchGpio(self,label):
|
|
return config.getSwitchGpio(label)
|
|
|
|
# Get rotary class
|
|
def getRotaryClass(self):
|
|
return config.getRotaryClass()
|
|
|
|
def dummy(self):
|
|
log.message("dummy" , log.DEBUG)
|
|
return
|
|
|
|
# Write text to file (Pecus)
|
|
def writeToFile(self,fname,text): # (Pecus)
|
|
file = open(fname, "w") # (Pecus)
|
|
file.write(text) # (Pecus)
|
|
file.close() # (Pecus)
|
|
return # (Pecus)
|
|
|
|
# Read text line from file (Pecus)
|
|
def readFromFile(self,fname): # (Pecus)
|
|
file = open(fname, "r") # (Pecus)
|
|
text = file.readline() # (Pecus)
|
|
file.close() # (Pecus)
|
|
return text # (Pecus)
|
|
|
|
|
|
# End of Radio Class
|
|
|
|
### Test routine ###
|
|
if __name__ == "__main__":
|
|
print "Test radio_class.py"
|
|
radio = Radio()
|
|
radio.mountUsb()
|
|
print "Version",radio.getVersion()
|
|
print "Board revision", radio.getBoardRevision()
|
|
iColor = radio.getBackColor('bg_color')
|
|
colorName = radio.getBackColorName(iColor)
|
|
print 'bg_color',colorName, iColor
|
|
|
|
# Start radio and load the radio stations
|
|
backpack = radio.getBackPackType()
|
|
print "Backpack", backpack
|
|
i2c_address = radio.getI2Caddress()
|
|
print "I2C address", hex(i2c_address)
|
|
radio.start()
|
|
radio.loadStations()
|
|
radio.play(1)
|
|
current_id = radio.getCurrentID()
|
|
index = current_id - 1
|
|
print "Current ID ", current_id
|
|
print "Station",current_id,":", radio.getStationName(index)
|
|
print "Bitrate", radio.getBitRate()
|
|
|
|
# Test volume controls
|
|
print "Stored volume", radio.getStoredVolume()
|
|
radio.setVolume(15)
|
|
radio.increaseVolume()
|
|
radio.decreaseVolume()
|
|
radio.getVolume()
|
|
time.sleep(5)
|
|
print "Mute"
|
|
radio.mute()
|
|
time.sleep(3)
|
|
print "Unmute"
|
|
radio.unmute()
|
|
print "Volume", radio.getVolume()
|
|
time.sleep(5)
|
|
# Test channel functions
|
|
current_id = radio.channelUp()
|
|
print "Channel up"
|
|
index = current_id - 1
|
|
print "Station",current_id,":", radio.getStationName(index)
|
|
time.sleep(5)
|
|
current_id = radio.channelDown()
|
|
print "Channel down"
|
|
index = current_id - 1
|
|
print "Station",current_id,":", radio.getStationName(index)
|
|
|
|
# Check library load
|
|
print "Load music library"
|
|
radio.loadMusic()
|
|
|
|
# Check state
|
|
print "Paused " + str(radio.paused())
|
|
|
|
# Check timer
|
|
print "Set Timer 1 minute"
|
|
radio.timerValue = 1
|
|
radio.timerOn()
|
|
|
|
while not radio.fireTimer():
|
|
time.sleep(1)
|
|
print "Timer fired"
|
|
|
|
# Exit
|
|
sys.exit(0)
|
|
|
|
# End of __main__ routine
|
|
|
|
|