diff --git a/Logger.py b/Logger.py new file mode 100644 index 0000000..575db8a --- /dev/null +++ b/Logger.py @@ -0,0 +1,48 @@ +# Copyright (c) 2021 Rene Juen +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +import logging +from logging.handlers import RotatingFileHandler + +class Logger: + + def __init__(self, name, debug): + self.name = name + self.level = debug + if self.level: + self.loglevelflag = logging.DEBUG + else: + self.loglevelflag = logging.INFO + self._config() + + def start(self): + pass + + def stop(self): + pass + + def info(self, message): + self.logger.info(message) + + def error(self, message): + self.logger.error(message) + + def warning(self, message): + self.logger.warning(message) + + def critical(self, message): + self.logger.critical(message) + + def debug(self, message): + self.logger.debug(message) + + def _config(self): + self.logger = logging.getLogger(self.name) + filename = '/var/log/nr1ui.log' + hdlr = RotatingFileHandler(filename, mode='a', maxBytes=5*1024*1024,backupCount=2, encoding=None, delay=0) + formatter = logging.Formatter('%(asctime)s [%(name)s] %(levelname)s %(message)s') + hdlr.setFormatter(formatter) + self.logger.addHandler(hdlr) + self.logger.setLevel(self.loglevelflag) diff --git a/modules/display1306.py b/modules/display1306.py index b86d784..5fa5fa4 100644 --- a/modules/display1306.py +++ b/modules/display1306.py @@ -4,13 +4,17 @@ from PIL import ImageDraw from PIL import ImageFont +import Logger as logger + +log = logger.Logger(__name__, True) + def show_logo(filename, device): logoImage = Image.new('1', (device.width, device.height)) img_path = os.path.dirname(os.path.realpath(__file__)) + '/../img/' try: logoImage = Image.open(img_path + filename).convert('1') #.resize((device.width, device.height), Image.ANTIALIAS) except IOError: - print("Cannot open file %s" % filename) + log.error("Cannot open file %s" % filename) pass device.display(logoImage) @@ -19,7 +23,7 @@ def load_font(filename, font_size): try: font = ImageFont.truetype(font_path + filename, font_size) except IOError: - print('font file not found -> using default font') + log.error('font file not found -> using default font') font = ImageFont.load_default() return font diff --git a/modules/display1322.py b/modules/display1322.py index 8d4edef..802c931 100644 --- a/modules/display1322.py +++ b/modules/display1322.py @@ -3,6 +3,9 @@ from PIL import Image from PIL import ImageDraw from PIL import ImageFont +import Logger as logger + +log = logger.Logger(__name__, True) def show_logo(filename, device): logoImage = Image.new('RGB', (device.width, device.height)) @@ -10,7 +13,7 @@ def show_logo(filename, device): try: logoImage = Image.open(img_path + filename).convert('RGB') #.resize((device.width, device.height), Image.ANTIALIAS) except IOError: - print("Cannot open file %s" % filename) + log.error("Cannot open file %s" % filename) pass device.display(logoImage) @@ -19,7 +22,7 @@ def load_font(filename, font_size): try: font = ImageFont.truetype(font_path + filename, font_size) except IOError: - print('font file not found -> using default font') + log.error('font file not found -> using default font') font = ImageFont.load_default() return font diff --git a/modules/display1351.py b/modules/display1351.py index 8a56bd3..ffcee5d 100644 --- a/modules/display1351.py +++ b/modules/display1351.py @@ -3,6 +3,9 @@ from PIL import Image from PIL import ImageDraw from PIL import ImageFont +import Logger as logger + +log = logger.Logger(__name__, True) def show_logo(filename, device): logoImage = Image.new('RGB', (device.width, device.height)) @@ -10,7 +13,7 @@ def show_logo(filename, device): try: logoImage = Image.open(img_path + filename).convert('RGB') #.resize((device.width, device.height), Image.ANTIALIAS) except IOError: - print("Cannot open file %s" % filename) + log.error("Cannot open file %s" % filename) pass device.display(logoImage) @@ -19,7 +22,7 @@ def load_font(filename, font_size): try: font = ImageFont.truetype(font_path + filename, font_size) except IOError: - print('font file not found -> using default font') + log.error('font file not found -> using default font') font = ImageFont.load_default() return font diff --git a/modules/display7735.py b/modules/display7735.py index 0e3a9f0..332043a 100644 --- a/modules/display7735.py +++ b/modules/display7735.py @@ -3,16 +3,19 @@ from PIL import Image from PIL import ImageDraw from PIL import ImageFont +import Logger as logger + +log = logger.Logger(__name__, True) def show_logo(filename, device): - print(device.width) - print(device.height) + log.debug("Device width [%d]" % device.width) + log.debug("Device height [%d]" % device.height) logoImage = Image.new('RGB', (device.width, device.height)) img_path = os.path.dirname(os.path.realpath(__file__)) + '/../img/' try: logoImage = Image.open(img_path + filename).convert('RGB') #.resize((device.width, device.height), Image.ANTIALIAS) except IOError: - print("Cannot open file %s" % filename) + log.error("Cannot open file %s" % filename) pass device.display(logoImage) @@ -21,7 +24,7 @@ def load_font(filename, font_size): try: font = ImageFont.truetype(font_path + filename, font_size) except IOError: - print('font file not found -> using default font') + log.error('font file not found -> using default font') font = ImageFont.load_default() return font diff --git a/modules/displayBraun.py b/modules/displayBraun.py index 152efea..8c1bd20 100644 --- a/modules/displayBraun.py +++ b/modules/displayBraun.py @@ -3,6 +3,9 @@ from PIL import Image from PIL import ImageDraw from PIL import ImageFont +import Logger as logger + +log = logger.Logger(__name__, True) def show_logo(filename, device): logoImage = Image.new('RGB', (device.width, device.height)) @@ -10,7 +13,7 @@ def show_logo(filename, device): try: logoImage = Image.open(img_path + filename).convert('RGB') #.resize((device.width, device.height), Image.ANTIALIAS) except IOError: - print("Cannot open file %s" % filename) + log.error("Cannot open file %s" % filename) pass device.display(logoImage) @@ -19,7 +22,7 @@ def load_font(filename, font_size): try: font = ImageFont.truetype(font_path + filename, font_size) except IOError: - print('font file not found -> using default font') + log.error('font file not found -> using default font') font = ImageFont.load_default() return font diff --git a/modules/pushbutton.py b/modules/pushbutton.py index 5fdaf67..a1e52e0 100644 --- a/modules/pushbutton.py +++ b/modules/pushbutton.py @@ -1,6 +1,9 @@ from time import sleep import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) +import Logger as logger + +log = logger.Logger(__name__, True) class PushButton: @@ -27,5 +30,5 @@ def callback(self, channel): measured_time += 0.02 sleep(0.02) if measured_time >= self.minimum_time and channel == self.pin and self.callbackFunction: - print('PIN: '+str(self.pin)+', time: '+str(measured_time)) + log.debug('PIN: [%d] \t time: [%d]' % (self.pin, measured_time)) return self.callbackFunction(measured_time) diff --git a/modules/rotaryencoder.py b/modules/rotaryencoder.py index 3c37fc6..2bd6d15 100644 --- a/modules/rotaryencoder.py +++ b/modules/rotaryencoder.py @@ -1,4 +1,7 @@ import RPi.GPIO as GPIO +import Logger as logger + +log = logger.Logger(__name__, True) class RotaryEncoder: @@ -13,15 +16,18 @@ def __init__(self, pinA, pinB, pulses_per_cycle=4): self.ppc = pulses_per_cycle self.direction = RotaryEncoder.UNKNOWN self.prevState = 0b11 - self.relposition = 0; + self.relposition = 0 GPIO.setup(self.pinA, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.pinB, GPIO.IN, pull_up_down=GPIO.PUD_UP) def setCallback(self, callback_function): self.callbackFunction = callback_function - GPIO.add_event_detect(self.pinA, GPIO.BOTH, callback=self.decodeRotation) - GPIO.add_event_detect(self.pinB, GPIO.BOTH, callback=self.decodeRotation) + try: + GPIO.add_event_detect(self.pinA, GPIO.BOTH, callback=self.decodeRotation) + GPIO.add_event_detect(self.pinB, GPIO.BOTH, callback=self.decodeRotation) + except: + log.error("No encoder detected. Please make shure you have wired it rigth up") def decodeRotation(self, channel): self.direction = RotaryEncoder.UNKNOWN @@ -37,11 +43,13 @@ def decodeRotation(self, channel): if self.relposition <= -self.ppc: self.relposition = 0 self.direction = RotaryEncoder.LEFT + log.debug("Rotaryencoder direction set to [LEFT]") elif (sm == 0b1110 or sm == 0b0111 or sm == 0b0001 or sm == 0b1000): self.relposition += 1 if self.relposition >= self.ppc: self.relposition = 0 self.direction = RotaryEncoder.RIGHT + log.debug("Rotaryencoder direction set to [Right]") if newState == 0b11: #locking position self.relposition = 0 diff --git a/nr1ui.py b/nr1ui.py index c0ffed0..d0dc5c6 100644 --- a/nr1ui.py +++ b/nr1ui.py @@ -36,6 +36,9 @@ import pprint import subprocess import RPi.GPIO as GPIO +# initial Logger +import Logger as logger + from time import* from datetime import timedelta as timedelta from threading import Thread @@ -57,6 +60,12 @@ import ssl import re import fnmatch +# initial Logger +#import Logger as logger + +log = logger.Logger(__name__, True) +log.info('starting nr1ui service') + sleep(5.0) # Socket-IO-Configuration for Rest API @@ -69,19 +78,19 @@ if SpectrumActive == True: with open('/etc/mpd.conf') as f1: if '/tmp/mpd.fifo' in f1.read(): - print("CAVA1 Fifo-Output is present in mpd.conf") + log.debug("CAVA1 Fifo-Output is present in mpd.conf") else: - print('CAVA1 FIFO-Output in /etc/mpd.conf is missing!') - print('Rebuilding mpd.conf now, this will take ~5 seconds.') + log.warning('CAVA1 FIFO-Output in /etc/mpd.conf is missing!') + log.warning('Rebuilding mpd.conf now, this will take ~5 seconds.') volumioIO.emit('callMethod', ReNewMPDconf) sleep(4.0) with open('/etc/mpd.conf') as f2: if '/tmp/mpd2.fifo' in f2.read(): - print("CAVA2 Fifo-Output is present in mpd.conf") + log.debug("CAVA2 Fifo-Output is present in mpd.conf") else: - print('CAVA2 FIFO-Output in /etc/mpd.conf is missing!') - print('Rebuilding mpd.conf now, this will take ~5 seconds.') + log.warning('CAVA2 FIFO-Output in /etc/mpd.conf is missing!') + log.warning('Rebuilding mpd.conf now, this will take ~5 seconds.') volumioIO.emit('callMethod', ReNewMPDconf) sleep(4.0) #________________________________________________________________________________________ @@ -124,8 +133,8 @@ ScreenList = ['Progress-Bar', 'Essential'] NowPlayingLayoutSave=open('/home/volumio/NR1-UI/ConfigurationFiles/LayoutSet.txt').readline().rstrip() -print('Layout selected during setup: ', NowPlayingLayout) -print('Last manually selected Layout: ', NowPlayingLayoutSave) +log.info('Layout selected during setup: %s'% NowPlayingLayout) +log.info('Last manually selected Layout: %s'% NowPlayingLayoutSave) if DisplayTechnology == 'spi1322': if NowPlayingLayout not in ScreenList: @@ -492,10 +501,10 @@ def sigterm_handler(signal, frame): def GetIP(): lanip = GetLANIP() LANip = str(lanip.decode('ascii')) - print('LAN IP: ', LANip) + log.debug('LAN IP: [%s]' % LANip) wanip = GetWLANIP() WLANip = str(wanip.decode('ascii')) - print('Wifi IP: ', WLANip) + log.debug('Wifi IP: [%s]' % WLANip) if LANip != '': ip = LANip elif WLANip != '': @@ -559,7 +568,7 @@ def display_update_service(): try: oled.modal.DrawOn(image) except AttributeError: - print("render error") + log.error("render error") sleep(1) cimg = image.crop((0, 0, oled.WIDTH, oled.HEIGHT)) oled.display(cimg) @@ -594,25 +603,29 @@ def SetState(status): #/_____/\__,_/\__/\__,_/ /_/ /_/\__,_/_/ /_/\__,_/_/\___/_/ (_) # def JPGPathfinder(String): - print('JPGPathfinder') + log.debug('Path where JPGPathfinder is looking for\t[%s]' % String) albumstring = String global FullJPGPath try: - p1 = 'path=(.+?)&metadata' - result = re.search(p1, albumstring) - URL = result.group(1) - URLPath = "/mnt" + URL + '/' + try: + p1 = 'path=(.+?)&metadata' + result = re.search(p1, albumstring) + URL = result.group(1) + except: + URL = albumstring + URLPath = URL + '/' accepted_extensions = ['jpg', 'jpeg', 'gif', 'png', 'bmp'] filenames = [fn for fn in os.listdir(URLPath) if fn.split(".")[-1] in accepted_extensions] JPGName = filenames[0] FullJPGPath = URLPath + JPGName - except: + except Exception as e: FullJPGPath = '/home/volumio/NR1-UI/NoCover.bmp' + log.debug('[JPGPathfinder Exception] \t %s' % e) + log.debug("Use /home/volumio/NR1-UI/NoCover.bmp for cover") JPGSave(FullJPGPath) - print('FullJPGPath: ', FullJPGPath) def JPGSave(Path): - print('JPGSave') + log.debug('Path where JPGSave is looking for\t[%s]' % Path) FullJPGPath = Path img = Image.open(FullJPGPath) # puts our image to the buffer of the PIL.Image object width, height = img.size @@ -624,18 +637,25 @@ def JPGSave(Path): img.save('/home/volumio/album.bmp') def JPGSaveURL(link): - print('JPGSaveURL') + log.debug('URL where JPGSaveURL is looking for\t[%s]' % link) try: - httpLink = urllib.parse.quote(link).replace('%3A',':') - ctx = ssl.create_default_context() - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE - with urllib.request.urlopen(httpLink, context=ctx) as url: - with open('temp.jpg', 'wb') as f: - f.write(url.read()) + #httpLink = urllib.parse.quote(link).replace('%3A',':') + #ctx = ssl.create_default_context() + #ctx.check_hostname = False + #ctx.verify_mode = ssl.CERT_NONE + #with urllib.request.urlopen(httpLink, context=ctx) as url: + # with open('temp.jpg', 'wb') as f: + # f.write(url.read()) + urllib.request.urlretrieve(link, "temp.jpg") img = Image.open('temp.jpg') - except: - img = Image.open('/home/volumio/NR1-UI/NoCover.bmp') + #except urllib.error.URLError as e: + # ResponseData = e.read().decode("utf8", 'ignore') + # log.error(ResponseData) + # img = Image.open('/home/volumio/NR1-UI/NoCover.bmp') + except Exception as e: + img = Image.open('/home/volumio/NR1-UI/NoCover.bmp') + log.debug('[JPGSaveURL Exception] \t %s' % e) + log.debug("Use /home/volumio/NR1-UI/NoCover.bmp for cover") width, height = img.size asp_rat = width/height new_width = 90 @@ -667,10 +687,10 @@ def onPushState(data): global ScrollAlbumNextRound global ScrollSpecsTag global ScrollSpecsNext - global ScrollSpecsFirstRoun + global ScrollSpecsFirstRound global ScrollSpecsNextRound OPDsave = data - #print('data: ', str(data).encode('utf-8')) + log.debug('data: [ %s ]' % str(data).encode('utf-8')) if 'title' in data: newSong = data['title'] @@ -703,7 +723,7 @@ def onPushState(data): if newFormat == True and newSong == 'HiFiBerry ADC': newFormat = 'Live-Stream' oled.activeFormat = newFormat - + if 'samplerate' in data: newSamplerate = data['samplerate'] oled.activeSamplerate = newSamplerate @@ -859,7 +879,8 @@ def onPushState(data): if NR1UIRemoteActive == True: if newAlbumart != oled.activeAlbumart: oled.activeAlbumart = newAlbumart - if AlbumArtHTTP is True and newFormat == 'WebRadio': + log.debug("AlbumartHTTP is [%s] \t newFormat is [%s]" % (AlbumArtHTTP, newFormat)) + if AlbumArtHTTP is True and (newFormat == 'WebRadio' or newFormat == 'webradio'): JPGSaveURL(newAlbumart) else: albumdecode = urllib.parse.unquote(newAlbumart, encoding='utf-8', errors='replace') @@ -950,6 +971,10 @@ def DrawOn(self, image): global ScrollAlbumNext global ScrollAlbumFirstRound global ScrollAlbumNextRound + global ScrollSpecsFirstRound + global ScrollSpecsNextRound + global ScrollSpecsTag + global ScrollSpecsNext #__________________________________________________________________________________________________________ # _ ___________ ___ __ __ # _________ (_) < /__ /__ \|__ \ / / ____ ___ ______ __ __/ /______ @@ -4145,7 +4170,7 @@ def DrawOn(self, image): # def ButtonA_PushEvent(hold_time): if hold_time < 2 and oled.state != STATE_LIBRARY_INFO: - print('ButtonA short press event') + log.debug('ButtonA short press event') if oled.state == STATE_PLAYER and oled.playState != 'stop' and oled.duration != None: if oled.playState == 'play': volumioIO.emit('pause') @@ -4156,29 +4181,25 @@ def ButtonA_PushEvent(hold_time): oled.modal.UpdateStandbyInfo() def ButtonB_PushEvent(hold_time): - #if hold_time < 2 and oled.state != STATE_LIBRARY_INFO: - # date_string = str(uuid.uuid1()) - # print(date_string) - # image.save('/home/volumio/'+date_string+'.png') if hold_time < 2 and oled.state != STATE_LIBRARY_INFO: - print('ButtonB short press event') + log.debug('ButtonB short press event') if oled.state == STATE_PLAYER and oled.playState != 'stop': volumioIO.emit('stop') oled.modal.UpdateStandbyInfo() def ButtonC_PushEvent(hold_time): if hold_time < 2: - print('ButtonC short press event') + log.debug('ButtonC short press event') if oled.state == STATE_PLAYER and oled.playState != 'stop': volumioIO.emit('prev') if oled.state == STATE_PLAYER and oled.playState == 'stop': - print ('RightKnob_PushEvent SHORT') + log.debug ('RightKnob_PushEvent SHORT') SetState(STATE_SCREEN_MENU) oled.state = 3 oled.modal = ScreenSelectMenu(oled.HEIGHT, oled.WIDTH) sleep(0.2) elif oled.state == STATE_PLAYER and oled.playState != 'stop': - print('ButtonC long press event') + log.debug('ButtonC long press event') if repeatTag == False: volumioIO.emit('setRepeat', {"value":"true"}) repeatTag = True @@ -4188,7 +4209,7 @@ def ButtonC_PushEvent(hold_time): def ButtonD_PushEvent(hold_time): if hold_time < 2: - print('ButtonD short press event') + log.debug('ButtonD short press event') if oled.state == STATE_PLAYER and oled.playState != 'stop': volumioIO.emit('next') if oled.state == STATE_PLAYER and oled.playState == 'stop': @@ -4199,7 +4220,7 @@ def ButtonD_PushEvent(hold_time): crl.perform() crl.close() get_body = b_obj.getvalue() - print('getBody',get_body) + log.debug('function [getBody] = %d' % get_body) SetState(STATE_LIBRARY_INFO) oled.playState = 'info' onPushCollectionStats(get_body) @@ -4207,7 +4228,7 @@ def ButtonD_PushEvent(hold_time): elif oled.state == STATE_LIBRARY_INFO: SetState(STATE_PLAYER) elif oled.state == STATE_PLAYER and oled.playState != 'stop': - print('ButtonD long press event') + log.debug('ButtonD long press event') if randomTag == False: volumioIO.emit('setRandom', {"value":"true"}) randomTag = True @@ -4229,7 +4250,7 @@ def RightKnob_RotaryEvent(dir): oled.selQueue = oled.modal.SelectedOption() emit_track = True elif oled.state == STATE_SCREEN_MENU and dir == RotaryEncoder.LEFT: - print('leftdir Rotary') + log.debug('leftdir Rotary') oled.modal.PrevOption() oled.SelectedScreen = oled.modal.SelectedOption() elif oled.state == STATE_SCREEN_MENU and dir == RotaryEncoder.RIGHT: @@ -4239,10 +4260,10 @@ def RightKnob_RotaryEvent(dir): def RightKnob_PushEvent(hold_time): if hold_time < 1: if oled.state == STATE_QUEUE_MENU: - print ('RightKnob_PushEvent SHORT') + log.debug ('RightKnob_PushEvent SHORT') oled.stateTimeout = 0 if oled.state == STATE_SCREEN_MENU: - print ('RightKnob_PushEvent Long') + log.debug ('RightKnob_PushEvent Long') global NowPlayingLayout oled.SelectedScreen = oled.modal.SelectedOption() Screen = ScreenList[oled.SelectedScreen] @@ -4361,10 +4382,10 @@ def PlaypositionHelper(): #/_/ /_/\__,_/_/_/ /_/ /_____/\____/\____/ .___/ (_) # while True: -# print('State: ', oled.state) -# print('palyState: ', oled.playState) -# print('newStatus: ', newStatus) -# print(oled.modal) +# log.debug('State: ', oled.state) +# log.debug('palyState: ', oled.playState) +# log.debug('newStatus: ', newStatus) +# log.debug(oled.modal) if emit_track and oled.stateTimeout < 4.5: emit_track = False try: