Skip to content

Instantly share code, notes, and snippets.

/audio.py Secret

Created November 2, 2016 08:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/e57ae98e5063cf90337b0fe2f8172115 to your computer and use it in GitHub Desktop.
Save anonymous/e57ae98e5063cf90337b0fe2f8172115 to your computer and use it in GitHub Desktop.
import time, math, threading, abc, socket, fcntl, struct, sys, os
class display:
__metaclass__ = abc.ABCMeta
# Class constructor, receives I2C address of display (usually it's 0x27, check with i2cdetect)
# Also, number of rows and columns (for example, 2x16 or 4x20 LCD)
def __init__(self, address, rows, columns, temp_screen_period, scroll_period):
self.address = address
self.rows = rows
self.columns = columns
# Default update time (period)
self.period = 0.01 # s
# Backlight state (needed for toggle)
self.backlight_state = True
# Initialize LCD
self.lcd_initialize()
# Unlock the display; this variable is used to prevent multiple write to display
self.lock_display = False
# Temporary screen period (volume, repeat, shuffle...)
self.temporary_screen_period = temp_screen_period / self.period # We have to divide it with period
# Scroll period
self.scroll_period = scroll_period / self.period # We have to divide it with period
# Create array for storing data for display
# Each element represents one row, and it contains string
self.display_data = []
# Fill all rows with empty spaces
for i in range(0, self.rows):
self.display_data.append('')
for j in range(self.columns):
self.display_data[i] = self.display_data[i] + ' '
# Prepare global data for scrolling
self.scroll = []
for i in range(self.columns):
temp = {'data': '', 'position': 0, 'direction': 0}
self.scroll.append(temp.copy())
# Select first screen
self.screen = 0
# Number of screens
self.screens = 6
# Initially, there's no temporary screen (like volume)
self.temporary_screen = False
# Current wait time for display
self.wait_time = 0
# Initially, we don't have to show volume screen
self.volume_screen = False
self.volume_value = 0
# Also, we don't have to show play mode screen
self.play_mode_screen = False
self.play_mode_type = 0
self.play_mode_state = False
# Initially, data didn't changed
self.data_changed = False
self.time_changed = False
# We don't have enough space for custom characters so we have to load them when needed
# But it takes resources so we will load them into LCD only if needed
# This variable contains currently loaded characters:
# 0 - play/pause/stop icons
# 1 - volume
# 2 - shuffle
# 3 - repeat all
# 4 - repeat single
self.current_custom_chars = -1 # Initially we want it to load something
# Icons for display
self.display_icons = [
[0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b00000], # Stop
[0b00000, 0b01000, 0b01100, 0b01110, 0b01110, 0b01100, 0b01000, 0b00000], # Play
[0b00000, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b00000], # Pause
[0b00000, 0b11111, 0b11011, 0b10001, 0b10001, 0b10001, 0b11111, 0b00000], # Ethernet
[0b00000, 0b00000, 0b00001, 0b00001, 0b00101, 0b00101, 0b10101, 0b00000], # Wireless
[0b00000, 0b01111, 0b01001, 0b01001, 0b01001, 0b11011, 0b11011, 0b00000], # Music note
[0b00000, 0b00100, 0b00100, 0b10101, 0b10101, 0b10001, 0b01110, 0b00000] # Power
]
''' LCD METHODS '''
# These methods are abstract; if you want to use any other library to write to LCD
# Just inherit this display class and implement (override) these methods
''' ABSTRACT METHOD, YOU HAVE TO IMPLEMENT IT IN INHERITED CLASS '''
@abc.abstractmethod
def lcd_initialize(self):
# This method initializes the display
# It MUST return LCD instance!!
return
''' ABSTRACT METHOD, YOU HAVE TO IMPLEMENT IT IN INHERITED CLASS '''
@abc.abstractmethod
def lcd_backlight(self, state):
# This method turns on and off LCD backlight
# It receives True or False
return
''' ABSTRACT METHOD, YOU HAVE TO IMPLEMENT IT IN INHERITED CLASS '''
@abc.abstractmethod
def lcd_message(self, message):
# This method writes the whole message to the LCD display
# To go to the new line, it uses "\n" character
return
''' ABSTRACT METHOD, YOU HAVE TO IMPLEMENT IT IN INHERITED CLASS '''
@abc.abstractmethod
def lcd_load_custom_chars(self, data):
# This method loads custom characters in LCD display
# It receives an array with data for each custom character
# Each array item contains 8 values for each line in one box
''' EXAMPLE '''
''' data = [
[ 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b00000 ],
[ 0b00000, 0b01000, 0b01100, 0b01110, 0b01110, 0b01100, 0b01000, 0b00000 ],
[ 0b00000, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b00000 ]
] '''
# This will load 3 custom characters (stop, play, pause icons) on places 0-2
return
# We need to register MPD client to be able to retrieve data from it
def register(self, mpd):
self.mpd = mpd
# Function for updating LCD display
def update_display(self):
tmp = '';
# Iterate through all lines
for i in range(self.rows):
tmp = tmp + self.display_data[i];
if (i != (self.rows - 1)):
tmp = tmp + '\n';
self.lcd_message(tmp);
# Function for toggling on/off LCD backlight
def toggle_backlight(self):
# If it's on currently, turn it off
if self.backlight_state:
self.lcd_backlight(False)
self.backlight_state = False
# Otherwise, turn it on
else:
self.lcd_backlight(True)
self.backlight_state = True
# Function for scrolling text, receives row id and text
# Every time it's called, it will scroll one character
# If it reaches the end, it will change direction
# If the text changes, it will start from scratch
# It returns the text ready for display; it checks if there's a need to scroll or not
# It uses global variables for current position, direction and data to scroll
def scroll_row(self, row, text):
# Maybe there's no need to scroll
if (len(text) == self.columns):
return text
# If text is shorter than LCD width, fill the rest with spaces
elif (len(text) < self.columns):
temp = text
for i in range(self.columns - len(text)):
temp = temp + ' '
return temp
# Check if text has changed
if (self.scroll[row]['data'] != text): # scroll[row] is a dictionary with scrolling data for 'row'
self.scroll[row]['data'] = text # It contains text to scroll (data), position and direction
self.scroll[row]['position'] = 0 # Start from the beginning
self.scroll[row]['direction'] = 0 # Reset direction to forwards
# Take the part of the text to currently show on the screen
temp = self.scroll[row]['data'][self.scroll[row]['position']:self.scroll[row]['position'] + self.columns]
# If direction is FORWARDS
if (self.scroll[row]['direction'] == 0):
# If we've reached the end, let us go backwards (change direction)
if (self.scroll[row]['position'] == (len(self.scroll[row]['data']) - self.columns)):
self.scroll[row]['direction'] = 1 # Set direction to backwards
else:
self.scroll[row]['position'] = self.scroll[row]['position'] + 1 # Move to next position
# If direction is BACKWARDS
if (self.scroll[row]['direction'] == 1):
# If we've reached the beginning, let us go forwards (change direction)
if (self.scroll[row]['position'] == 0):
self.scroll[row]['direction'] = 0 # Set direction to forwards
else:
self.scroll[row]['position'] = self.scroll[row]['position'] - 1 # Move to next position
return temp
# Function for showing volume screen
def show_volume(self):
# Speaker icon custom characters
speaker_icon = [
[0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b10001, 0b10001, 0b10001],
[0b00001, 0b00011, 0b00101, 0b01001, 0b10001, 0b00001, 0b00001, 0b00001],
[0b10001, 0b10001, 0b10001, 0b11111, 0b00000, 0b00000, 0b00000, 0b00000],
[0b00001, 0b00001, 0b00001, 0b10001, 0b01001, 0b00101, 0b00011, 0b00001]
]
# If display is 4x20, this will be displayed in lines 2 and 3, otherwise 1 and 2 (2x16)
if (self.rows >= 4):
skip_lines = 1
else:
skip_lines = 0
# Load custom characters if needed
if (self.current_custom_chars != 1):
self.lcd_load_custom_chars(speaker_icon)
self.current_custom_chars = 1
# Show first part of icon + "volume"
self.display_data[skip_lines] = chr(0) + chr(1) + ' Volume'
# Show second part of icon
self.display_data[skip_lines + 1] = chr(2) + chr(3) + ' '
# If volume is 0 or 100 ...
if (self.volume_value == 0 or self.volume_value == 100):
# Fill spaces between; columns - 3 (2 for icon + space) - 12 (length of 'VolumeMIN' or 'VolumeMAX')
for i in range(self.columns - 12):
self.display_data[skip_lines] += ' '
# If volume is 0, show minimum and no blocks in third line
if (self.volume_value == 0):
self.display_data[skip_lines] += 'MIN'
for i in range(self.columns - 3): # -3 for icon (2) and space
self.display_data[skip_lines + 1] += ' '
# If volume is 100, show maximum and all blocks in third line
elif (self.volume_value == 100):
self.display_data[skip_lines] += 'MAX'
for i in range(self.columns - 3):
self.display_data[skip_lines + 1] += chr(255)
# Else, show value in % and the corresponding number of blocks
else:
# Fill spaces between; columns - 3 (icon + space) - 10 (length of 'VolumeXX %)
for i in range(self.columns - 13):
self.display_data[skip_lines] += ' '
# Check if there is only one digit and append another space
if (self.volume_value < 10):
self.display_data[skip_lines] += ' '
# Append volume value
self.display_data[skip_lines] += `self.volume_value` + ' %'
# Calculate number of blocks to show
temp = (self.volume_value / int(math.ceil((100.0 / (self.columns - 3))))) + 1
# Append the blocks
for i in range(temp):
self.display_data[skip_lines + 1] += chr(255)
# Fill remaining space with ' '
for i in range(self.columns - temp - 3):
self.display_data[skip_lines + 1] += ' '
# If display is 4x20, we will leave first and forth line empty for now
if (self.rows >= 4):
self.display_data[0] = ''
for i in range(self.columns):
self.display_data[0] += ' '
self.display_data[3] = ''
for i in range(self.columns):
self.display_data[3] += ' '
# At the end, update the display
while (self.lock_display):
pass
self.lock_display = True
self.update_display()
self.lock_display = False
# Function for showing shuffle (0), repeat all (1) or repeat single (2) screen
def show_play_mode(self):
# Shuffle icon custom characters
shuffle_icon = [
[0b00000, 0b00000, 0b00000, 0b11100, 0b00010, 0b00010, 0b00010, 0b00001],
[0b00000, 0b00000, 0b00010, 0b00111, 0b01010, 0b01000, 0b01000, 0b10000],
[0b00001, 0b00010, 0b00010, 0b00010, 0b11100, 0b00000, 0b00000, 0b00000],
[0b10000, 0b01000, 0b01000, 0b01010, 0b00111, 0b00010, 0b00000, 0b00000]
]
repeat_all_icon = [
[0b00000, 0b00000, 0b00000, 0b00011, 0b00100, 0b01000, 0b10000, 0b10000],
[0b00000, 0b00000, 0b00000, 0b11000, 0b00101, 0b00011, 0b00111, 0b00000],
[0b10000, 0b10000, 0b01000, 0b00100, 0b00011, 0b00000, 0b00000, 0b00000],
[0b00000, 0b00000, 0b00010, 0b00100, 0b11000, 0b00000, 0b00000, 0b00000]
]
repeat_single_icon = [
[0b00000, 0b00000, 0b00000, 0b00011, 0b00100, 0b01000, 0b00000, 0b00000],
[0b00000, 0b00000, 0b00000, 0b11000, 0b00101, 0b00011, 0b00111, 0b00000],
[0b00000, 0b11100, 0b11000, 0b10100, 0b00011, 0b00000, 0b00000, 0b00000],
[0b00000, 0b00000, 0b00010, 0b00100, 0b11000, 0b00000, 0b00000, 0b00000]
]
# If display is 4x20, this will be displayed in lines 2 and 3, otherwise 1 and 2 (2x16)
if (self.rows >= 4):
skip_lines = 1
else:
skip_lines = 0
# Choose icon and text, according to type
if (self.play_mode_type == 0):
icon = shuffle_icon
text = "Shuffle Mode"
elif (self.play_mode_type == 1):
icon = repeat_all_icon
text = "Repeat All"
elif (self.play_mode_type == 2):
icon = repeat_single_icon
text = "Repeat One"
# Load custom characters if needed
if (self.current_custom_chars != (self.play_mode_type + 2)):
self.lcd_load_custom_chars(icon)
self.current_custom_chars = (self.play_mode_type + 2)
# Show first part of icon
self.display_data[skip_lines] = chr(0) + chr(1)
# Show second part of icon
self.display_data[skip_lines + 1] = chr(2) + chr(3)
# We need to center the text so we have to calculate how much spaces depending on screen width
temp = self.columns - 2 - len(text) # 2 (for icon) and length of text
# Add spaces from left side
if ((temp % 2) != 0):
lside = (temp / 2) + 1
else:
lside = (temp / 2)
for i in range(lside):
self.display_data[skip_lines] += ' '
# Add the text
self.display_data[skip_lines] += text
# Add spaces from right side
for i in range(temp / 2):
self.display_data[skip_lines] += ' '
# Now it's time for second row, check if shuffle is enabled or disabled:
if (self.play_mode_state == True):
temp_text = "ENABLED"
else:
temp_text = "DISABLED"
# We need to center the text so we have to calculate how much spaces depending on screen width
temp = self.columns - 2 - len(temp_text) # 2 (for icon) and length of "Enabled" or "Disabled"
# Add spaces from left side
for i in range(temp / 2):
self.display_data[skip_lines + 1] += ' '
# Add the text
self.display_data[skip_lines + 1] += temp_text
# Add spaces from right side
if ((temp % 2) != 0):
rside = (temp / 2) + 1
else:
rside = (temp / 2)
for i in range(rside):
self.display_data[skip_lines + 1] += ' '
# If display is 4x20 ...
if (self.rows >= 4):
# We will leave first line empty for now
self.display_data[0] = ''
for i in range(self.columns):
self.display_data[0] += ' '
# We will leave forth line empty for now
self.display_data[3] = ''
for i in range(self.columns):
self.display_data[3] += ' '
# At the end, update the display
while (self.lock_display):
pass
self.lock_display = True
self.update_display()
self.lock_display = False
# This function is called by MPD when volume is changed
def volume_changed(self, value):
# We only have to notify the display thread
self.volume_value = value
self.volume_screen = True
# This function is called by MPD when play mode is changed
def play_mode_changed(self, type, state):
# We only have to notify the display thread
self.play_mode_type = type
self.play_mode_state = state
self.play_mode_screen = True
# This function is called by MPD when data changes (for example, song)
def data_change(self):
self.data_changed = True
# This function is called by MPD when time changes (for example, second passed in elapsed time)
def time_change(self):
self.time_changed = True
# This function is called by remote or button to change screen mode
def change_screen(self):
# For 4x20 display, jump by 2, for 2x16 by 1
if (self.rows >= 4):
self.screen += 2
else:
self.screen += 1
# If we reached the end, let's go from beginning
if (self.screen >= self.screens):
self.screen = 0
# Convert seconds to M:S (type = 0), H:M:S (type = 1) or D:H:M:S (type = 2)
def convert_time(self, seconds, type):
# Initialize
sec = 0
min = 0
hour = 0
day = 0
# In any type, we need seconds
sec = seconds % 60;
# M:S type, get seconds
if (type == 0):
min = int(int(seconds) / 60)
# H:M:S type, get hours and seconds
elif (type == 1):
min = int(int(seconds) / 60) % 60
hour = int(int(seconds) / 3600)
# D:H:M:S type, get days, hours and seconds
elif (type == 2):
min = int(int(seconds) / 60) % 60
hour = int(int(seconds) / 3600) % 24
day = int(int(seconds) / 86400)
temp = ''
# Create string to return
if (sec < 10):
temp += '0' + `sec`
else:
temp += `sec`
if (min < 10):
temp = '0' + `min` + ':' + temp
else:
temp = `min` + ':' + temp
if (type == 1 or type == 2):
if (hour < 10):
temp = '0' + `hour` + ':' + temp
else:
temp = `hour` + ':' + temp
if (type == 2):
temp = `day` + ' d, ' + temp
return temp
# Get IP addresses to show on screen: ifname = eth0 | ifname = elan0
def get_ip_address(self, ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname)
)[20:24])
except IOError:
return ''
# Return CPU temperature as a character string
def getCPUtemperature(self):
res = os.popen('/opt/vc/bin/vcgencmd measure_temp').readline()
return (res.replace("temp=", "").replace("'C\n", ""))
# Return RAM information (unit=kb) in a list
# Index 0: total RAM
# Index 1: used RAM
# Index 2: free RAM
def getRAMinfo(self):
p = os.popen('free')
i = 0
while 1:
i = i + 1
line = p.readline()
if i == 2:
data = line.split()[1:4]
break
# For 16x2 LCD, remove decimal value (to fit on the screen)
if (self.columns < 20):
display_format = "{0:.0f}"
else:
display_format = "{0:.1f}"
# Convert it to MB and show as xy.z
temp = []
temp.append(display_format.format(int(data[0]) / 1024.0))
temp.append(display_format.format(int(data[1]) / 1024.0))
temp.append(display_format.format(int(data[2]) / 1024.0))
return temp
# Return date and time
def get_datetime(self):
p = os.popen('date')
line = p.readline().strip()
data = line.split()
# Get clock
try:
clock = data[3]
except KeyError:
clock = ''
print data
# Get date
try:
date = data[2] + ' ' + data[1] + ' ' + data[5]
except KeyError:
date = ''
return {'clock': clock, 'date': date}
# Screen 0 shows artist and song name, times and track info
# Returns whether the data has changed or not
def screen_0(self):
data_changed = False
# FIRST ROW: Get artist data from MPD and pass it to scroll function
try:
temp = self.scroll_row(0, self.mpd.getData()['artist'])
except KeyError:
temp = ''
# Check if data has changed
if (temp != self.display_data[0]):
self.display_data[0] = temp
data_changed = True
# SECOND ROW: Get song name data from MPD and pass it to scroll function
try:
temp = self.scroll_row(1, self.mpd.getData()['title'])
except KeyError:
temp = ''
# Check if data has changed
if (temp != self.display_data[1]):
self.display_data[1] = temp
data_changed = True
self.wait_time = self.scroll_period
return data_changed
# This screen shows time and track/station info
# Returns whether the data has changed or not
def screen_1(self):
data_changed = False
temp = ''
# If display is 4x20, this will be displayed in lines 3 and 4, otherwise 1 and 2 (2x16)
if (self.rows >= 4):
skip_lines = 2
else:
skip_lines = 0
# If file is playing
if (self.mpd.getData()['type'] == 0):
# Get elapsed time
elapsed_time = self.convert_time(self.mpd.getData()['elapsed_time'], 0)
# Get total track time
total_time = self.convert_time(self.mpd.getData()['total_time'], 0)
# Get state icon
icon = chr(self.mpd.getData()['state'])
temp = ''
# Show elapsed_time
temp += elapsed_time
# We have to center the icon so we have to count how much spaces to put
space_count = (self.columns - len(elapsed_time) - len(total_time) - 1) # -1 is for icon
# We have to be careful for numbers not dividable by 2
if ((space_count % 2) != 0):
i = space_count / 2 + 1
else:
i = space_count / 2
# Fill spaces
for j in range(space_count / 2):
temp += ' '
# Show icon
temp += icon
# Fill remaining spaces
for j in range(i):
temp += ' '
# Show total time
temp += total_time
# else if radio is playing
elif (self.mpd.getData()['type'] == 1):
# Get elapsed time
elapsed_time = self.convert_time(self.mpd.getData()['elapsed_time'], 1)
# Get state icon
icon = chr(self.mpd.getData()['state'])
temp = ''
# Show elapsed_time
temp += elapsed_time
# Show something instead total time
if (self.columns >= 20):
word = 'STREAMING'
else:
word = 'STREAM'
# We have to center the icon so we have to count how much spaces to put
space_count = (
self.columns - len(elapsed_time) - len(word) - 1) # -1 is for icon, -6 is for length of 'STREAM'
# We have to be careful for numbers not dividable by 2
if ((space_count % 2) != 0):
i = space_count / 2 + 1
else:
i = space_count / 2
# Fill spaces
for j in range(space_count / 2):
temp += ' '
# Show icon
temp += icon
# Fill remaining spaces
for j in range(i):
temp += ' '
# Show the word
temp += word
# Check if data has changed
if (temp != self.display_data[skip_lines]):
self.display_data[skip_lines + 1] = temp
data_changed = True
# Last line shows RADIO/FILE and bitrate
if (self.mpd.getData()['type'] == 0):
word = "FILE"
bitrate = `self.mpd.getData()['bitrate']` + ' kbps'
elif (self.mpd.getData()['type'] == 1):
word = "RADIO"
bitratefile_obj = open('/proc/asound/card1/pcm0p/sub0/hw_params', 'r')
rate_data = bitratefile_obj.read()
ratestart = rate_data.find('rate:') + 6
ratestop = rate_data.find('(') - 1
brate = str(rate_data[ratestart:ratestop])
if brate[-3:] == '000':
bitrate = brate[:-3] + ' kHz'
elif brate[-2:] == '00':
rate = brate[:-2]
bitrate = brate[:-1] + '.' + brate[-1:] + ' kHz'
elif brate[-1:] == '0':
brate = brate[:-1]
bitrate = brate[:-2] + '.' + brate[-2:] + ' kHz'
else:
bitrate = brate[:-3] + '.' + brate[-3:] + ' kHz'
# Show type
temp = word
# Between word and bitrate we will fill spaces
for i in range(self.columns - len(word) - len(bitrate)):
temp += ' '
# Show bitrate
temp += bitrate
# Check if data has changed
if (temp != self.display_data[skip_lines + 1]):
self.display_data[skip_lines] = temp
data_changed = True
# If data changed, see if there's a need to load custom charachters
if (data_changed):
# Load custom characters, if needed
if (self.current_custom_chars != 0):
self.lcd_load_custom_chars(self.display_icons)
self.current_custom_chars = 0
return data_changed
# This screen shows Ethernet and Wi-Fi IP address, if connected
def screen_2(self):
data_changed = False
temp = ''
# Get Ethernet IP
ethip = self.get_ip_address('eth0')
# If there's no IP address
if (ethip == ''):
ethip = 'Not connected'
# Check if we will show the icon or not
if ((len(ethip) + 2) > self.columns):
temp = ethip
# Else show icon as well
else:
temp = chr(3) + ' ' + ethip
# Will remaining space with ' '
for i in range(self.columns - len(temp)):
temp += ' '
# Check if data changed
if (temp != self.display_data[0]):
self.display_data[0] = temp
data_changed = True
temp = ''
# Get Wireless IP
wifiip = self.get_ip_address('wlan0')
# If there's no IP address
if (wifiip == ''):
wifiip = 'Not connected'
# Check if we will show the icon or not
if ((len(wifiip) + 2) > self.columns):
temp = wifiip
# Else show icon as well
else:
temp = chr(4) + ' ' + wifiip
# Will remaining space with ' '
for i in range(self.columns - len(temp)):
temp += ' '
# Check if data changed
if (temp != self.display_data[1]):
self.display_data[1] = temp
data_changed = True
self.wait_time = 1000 / self.period
return data_changed
# This screen shows playtime and total uptime from last reboot
def screen_3(self):
data_changed = False
skip_lines = 0
# If it's a 2x16 display, show this in lines 1 and 2, in case of 4x20 display, show it in 3 and 4
if (self.rows >= 4):
skip_lines = 2
else:
skip_lines = 0
# Get playtime from last reboot
playtime = self.convert_time(self.mpd.getData()['playtime'], 2)
# If there's enough space for it, convert 'd' to 'days', 3 for 'ays', 2 for icon and space
if ((len(playtime) + 3 + 2) <= self.columns):
playtime = playtime.replace('d,', 'days,')
# Check if there's enough space for showing icon, and add it if it's possible
if ((len(playtime) + 2) <= self.columns):
playtime = chr(5) + ' ' + playtime
# Fill empty space with ' '
for i in range(self.columns - len(playtime)):
playtime += ' '
# Check if data has changed
if (playtime != self.display_data[skip_lines]):
self.display_data[skip_lines] = playtime
data_changed = True
# Get uptime from last reboot
uptime = self.convert_time(self.mpd.getData()['uptime'], 2)
# If there's enough space for it, convert 'd' to 'days', 3 for 'ays', 2 for icon and space
if ((len(uptime) + 3 + 2) <= self.columns):
uptime = uptime.replace('d,', 'days,')
# Check if there's enough space for showing icon, and add it if it's possible
if ((len(uptime) + 2) <= self.columns):
uptime = chr(6) + ' ' + uptime
# Fill empty space with ' '
for i in range(self.columns - len(uptime)):
uptime += ' '
# Check if data has changed
if (uptime != self.display_data[skip_lines + 1]):
self.display_data[skip_lines + 1] = uptime
data_changed = True
# If data changed, see if there's a need to load custom charachters
if (data_changed):
# Load custom characters, if needed
if (self.current_custom_chars != 0):
self.lcd_load_custom_chars(self.display_icons)
self.current_custom_chars = 0
self.wait_time = 1000 / self.period
return data_changed
# Show clock and date
def screen_4(self):
data_changed = False
# Get clock and date
data = self.get_datetime()
# Show clock label
temp = 'Clock'
# Fill remaining space with ' '; 5 is for len('clock')
for i in range(self.columns - len(data['clock']) - 5):
temp += ' '
# Show clock
temp += data['clock']
# Check if data has changed
if (self.display_data[0] != temp):
self.display_data[0] = temp
data_changed = True
temp = ''
# If it can fit, show date label
if ((len('Date') + 1 + len(data['date'])) <= self.columns):
temp += 'Date'
# Fill remaining space with ' '
for i in range(self.columns - (len('Date') + len(data['date']))):
temp += ' '
# Show date
temp += data['date']
# Fill remaining space with ' '
for i in range(self.columns - len(temp)):
temp += ' '
# Check if data has changed
if (self.display_data[1] != temp):
self.display_data[1] = temp
data_changed = True
self.wait_time = 1000 / self.period
return data_changed
# This screen shows CPU temperature and RAM usage
def screen_5(self):
data_changed = False
skip_lines = 0
# If it's a 2x16 display, show this in lines 1 and 2, in case of 4x20 display, show it in 3 and 4
if (self.rows >= 4):
skip_lines = 2
else:
skip_lines = 0
# Get CPU temperature
temperature = self.getCPUtemperature()
temp = ''
# Show label
temp += 'CPU Temp'
# If it fits, put a dot on temp :), 3 is for celsius sign, C and space
if ((len('CPU Temp. ') + len(temperature) + 3) <= self.columns):
temp += '.'
# Fill remaining space with ' '
for i in range(self.columns - len(temp) - len(temperature) - 3):
temp += ' '
# Show temperature and sign
temp += temperature + ' ' + chr(223) + 'C'
# Check if data has changed
if (self.display_data[skip_lines] != temp):
self.display_data[skip_lines] = temp
data_changed = True
# Get RAM usage
ram = self.getRAMinfo()
temp = ''
# Show label
temp += 'RAM'
# Fill remaining space with ' '
for i in range(self.columns - len(temp) - len(ram[1] + '/' + ram[0]) - 3):
temp += ' '
# Show RAM
temp += ram[1] + '/' + ram[0] + ' MB'
# Check if data has changed
if (self.display_data[skip_lines + 1] != temp):
self.display_data[skip_lines + 1] = temp
data_changed = True
self.wait_time = 1000 / self.period
return data_changed
# Main function which is running all the time to update display
def main_function(self):
while True:
# Set period for checking for changes
time.sleep(self.period)
# Check if volume is set
if (self.volume_screen):
self.volume_screen = False
self.show_volume()
self.wait_time = self.temporary_screen_period
self.temporary_screen = True
# Check if play mode is set
if (self.play_mode_screen):
self.play_mode_screen = False
self.show_play_mode()
self.wait_time = self.temporary_screen_period
self.temporary_screen = True
# Check if data changed - time to update display
if (self.data_changed):
self.data_changed = False
self.wait_time = 0 # Skip waiting
# Check if time has changed
if (self.time_changed):
self.time_changed = False
# If there's a temporary screen (volume), we don't want to interrupt it by clock
if (self.temporary_screen == False):
self.wait_time = 0
# Check if there's a wait time to pass
if (self.wait_time > 0):
self.wait_time -= 1
continue
# Temporary screen passed
self.temporary_screen = False
data_changed = False
# If screen 0 is selected
if (self.screen == 0):
# If display is 4x20
if (self.rows >= 4):
data_changed1 = self.screen_0()
data_changed2 = self.screen_1()
data_changed = data_changed1 or data_changed2
# Else it's a 2x16
else:
data_changed = self.screen_0()
# Else if screen is 1
elif (self.screen == 1):
# 1 is only for 2x16 display
if (self.rows < 4):
data_changed = self.screen_1()
# Else return to screen 0
else:
self.screen == 0
# Else if screen is 2
elif (self.screen == 2):
# If display is 4x20
if (self.rows >= 4):
data_changed1 = self.screen_2()
data_changed2 = self.screen_3()
data_changed = data_changed1 or data_changed2
# Else it's a 2x16
else:
data_changed = self.screen_2()
# Else if screen is 3
elif (self.screen == 3):
# 1 is only for 2x16 display
if (self.rows < 4):
data_changed = self.screen_3()
# Else return to screen 2
else:
self.screen == 2
# Else if screen is 4
elif (self.screen == 4):
# If display is 4x20
if (self.rows >= 4):
data_changed1 = self.screen_4()
data_changed2 = self.screen_5()
data_changed = data_changed1 or data_changed2
# Else it's a 2x16
else:
data_changed = self.screen_4()
# Else if screen is 5
elif (self.screen == 5):
# 1 is only for 2x16 display
if (self.rows < 4):
data_changed = self.screen_5()
# Else return to screen 4
else:
self.screen == 4
# If data has changed, update display
if (data_changed):
while (self.lock_display):
continue
self.lock_display = True
self.update_display()
self.lock_display = False
data_changed = False
# Function for starting display thread
def start(self):
self.lcd_t = threading.Thread(target=self.main_function, args=()) # Create thread for updating LCD
self.lcd_t.daemon = True # Yep, it's a daemon, when main thread finish, this one will finish too
self.lcd_t.start() # Start it!
# Function for waiting the thread to finish
def join(self):
self.lcd_t.join()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment