class Lightstrip(object): def __init__(self, cfg): nps = cfg['neopixel'] self.strip = Adafruit_NeoPixel(nps['led-count'], \ nps['led-pin'], nps['led-freq-hz'], nps['led-dma'], \ nps['led-invert'], nps['led-brightness'], nps['led-channel']) self.reversed = cfg['custom']['reversed'] self.strip.begin() def _cleanup(self): self.strip._cleanup() def show(self): self.strip.show() # Sets the pixel without updating the strip # Allows reversal of direction of the strip # Ensures bounded pixel index from [0, numPixels) def setPixel(self, n, color): pixelNum = self.strip.numPixels() - 1 - n if self.reversed else n pixelNum %= self.strip.numPixels() self.strip.setPixelColor(pixelNum, color) # Sets the pixel and immediately updates the lightstrip visually def setPixelUpdate(self, n, color): self.setPixel(n, color) self.show() def setBrightness(self, n): self.strip.setBrightness(n) def getBrightness(self): self.strip.getBrightness() def setReversed(self, rev): self.reversed = rev def getReversed(self): return self.reversed def numPixels(self): return self.strip.numPixels() # The only animation I am baking into the lightstrip class because # it has pretty universal importance among other animations and # the runner class too def clear(self): for i in range(self.strip.numPixels()): self.setPixel(i, Color(0, 0, 0)) self.show() def clearPixel(self, n): self.setPixel(n, Color(0, 0, 0))
class wordclock_display: """ Class to display any content on the wordclock display Depends on the (individual) wordclock layout/wiring """ def __init__(self, config, wci): """ Initialization """ # Get the wordclocks wiring-layout self.wcl = wiring.wiring(config) self.wci = wci self.config = config self.base_path = config.get('wordclock', 'base_path') try: default_brightness = config.getint('wordclock_display', 'brightness') except: default_brightness = 255 print( 'WARNING: Default brightness value not set in config-file: ' 'To do so, add a "brightness" between 1..255 to the [wordclock_display]-section.' ) if config.getboolean('wordclock', 'developer_mode'): from GTKstrip import GTKstrip self.strip = GTKstrip(wci) self.default_font = 'wcfont.ttf' else: try: from neopixel import Adafruit_NeoPixel, ws self.strip = Adafruit_NeoPixel( self.wcl.LED_COUNT, self.wcl.LED_PIN, self.wcl.LED_FREQ_HZ, self.wcl.LED_DMA, self.wcl.LED_INVERT, default_brightness, 0, ws.WS2811_STRIP_GRB) except: print( 'Update deprecated external dependency rpi_ws281x. ' 'For details see also https://github.com/jgarff/rpi_ws281x/blob/master/python/README.md' ) if config.get('wordclock_display', 'default_font') == 'wcfont': self.default_font = self.base_path + '/wcfont.ttf' else: self.default_font = os.path.join( '/usr/share/fonts/truetype/freefont/', config.get('wordclock_display', 'default_font') + '.ttf') # Initialize the NeoPixel object self.strip.begin() self.default_fg_color = wcc.WWHITE self.default_bg_color = wcc.BLACK # Choose language try: language = ''.join(config.get('wordclock_display', 'language')) except: # For backward compatibility language = ''.join(config.get('plugin_time_default', 'language')) print(' Setting language to ' + language + '.') if language == 'dutch': self.taw = time_dutch.time_dutch() elif language == 'english': self.taw = time_english.time_english() elif language == 'german': self.taw = time_german.time_german() elif language == 'german2': self.taw = time_german2.time_german2() elif language == 'swabian': self.taw = time_swabian.time_swabian() elif language == 'swabian2': self.taw = time_swabian2.time_swabian2() elif language == 'bavarian': self.taw = time_bavarian.time_bavarian() elif language == 'swiss_german': self.taw = time_swiss_german.time_swiss_german() elif language == 'swiss_german2': self.taw = time_swiss_german2.time_swiss_german2() else: print('Could not detect language: ' + language + '.') print('Choosing default: german') self.taw = time_german.time_german() def setPixelColor(self, pixel, color): """ Sets the color for a pixel, while considering the brightness, set within the config file """ self.strip.setPixelColor(pixel, color) def getBrightness(self): """ Sets the color for a pixel, while considering the brightness, set within the config file """ return self.strip.getBrightness() def setBrightness(self, brightness): """ Sets the color for a pixel, while considering the brightness, set within the config file """ self.strip.setBrightness(brightness) self.show() def setColorBy1DCoordinates(self, *args, **kwargs): """ Sets a pixel at given 1D coordinates """ return self.wcl.setColorBy1DCoordinates(*args, **kwargs) def setColorBy2DCoordinates(self, *args, **kwargs): """ Sets a pixel at given 2D coordinates """ return self.wcl.setColorBy2DCoordinates(*args, **kwargs) def get_wca_height(self): """ Returns the height of the WCA """ return self.wcl.WCA_HEIGHT def get_wca_width(self): """ Returns the width of the WCA """ return self.wcl.WCA_WIDTH def get_led_count(self): """ Returns the overall number of LEDs """ return self.wcl.LED_COUNT def dispRes(self): """ Returns the resolution of the wordclock array as string E.g. to choose the correct resolution of animations and icons """ return str(self.wcl.WCA_WIDTH) + 'x' + str(self.wcl.WCA_HEIGHT) def setColorToAll(self, color, includeMinutes=True): """ Sets a given color to all leds If includeMinutes is set to True, color will also be applied to the minute-leds. """ if includeMinutes: for i in range(self.wcl.LED_COUNT): self.setPixelColor(i, color) else: for i in self.wcl.getWcaIndices(): self.setPixelColor(i, color) def setColorTemperatureToAll(self, temperature, includeMinutes=True): """ Sets a color to all leds based on the provided temperature in Kelvin If includeMinutes is set to True, color will also be applied to the minute-leds. """ self.setColorToAll(wcc.color_temperature_to_rgb(temperature), includeMinutes) def resetDisplay(self): """ Reset display """ self.setColorToAll(wcc.BLACK, True) def showIcon(self, plugin, iconName): """ Dispays an icon with a specified name. The icon needs to be provided within the graphics/icons folder. """ self.setImage(self.base_path + '/wordclock_plugins/' + plugin + '/icons/' + self.dispRes() + '/' + iconName + '.png') def setImage(self, absPathToImage): """ Set image (provided as absolute path) to current display """ img = Image.open(absPathToImage) width, height = img.size for x in range(0, width): for y in range(0, height): rgb_img = img.convert('RGB') r, g, b = rgb_img.getpixel((x, y)) self.wcl.setColorBy2DCoordinates(self.strip, x, y, wcc.Color(r, g, b)) self.show() def animate(self, plugin, animationName, fps=10, count=1, invert=False): """ Runs an animation plugin: Plugin-name num_of_frames: Number of frames to be displayed count: Number of runs fps: frames per second invert: Invert order of animation """ animation_dir = self.base_path + '/wordclock_plugins/' + plugin + '/animations/' + self.dispRes( ) + '/' + animationName + '/' num_of_frames = len( [file_count for file_count in os.listdir(animation_dir)]) if invert: animation_range = range(num_of_frames - 1, -1, -1) else: animation_range = range(0, num_of_frames) for _ in range(count): for i in animation_range: self.setImage(animation_dir + str(i).zfill(3) + '.png') if self.wci.waitForExit(1.0 / fps): return def showText(self, text, font=None, fg_color=None, bg_color=None, fps=10, count=1): """ Display text on display """ if font is None: font = self.default_font if fg_color is None: fg_color = self.default_fg_color if bg_color is None: bg_color = self.default_bg_color text = ' ' + text + ' ' fnt = fontdemo.Font(font, self.wcl.WCA_HEIGHT) text_width, text_height, text_max_descent = fnt.text_dimensions(text) text_as_pixel = fnt.render_text(text) # Display text count times for i in range(count): # Erase previous content self.setColorToAll(bg_color, includeMinutes=True) # Assure here correct rendering, if the text does not fill the whole display render_range = self.wcl.WCA_WIDTH if self.wcl.WCA_WIDTH < text_width else text_width for y in range(text_height): for x in range(render_range): self.wcl.setColorBy2DCoordinates( self.strip, x, y, fg_color if text_as_pixel.pixels[y * text_width + x] else bg_color) # Show first frame for 0.5 seconds self.show() if self.wci.waitForExit(0.5): return # Shift text from left to right to show all. for cur_offset in range(text_width - self.wcl.WCA_WIDTH + 1): for y in range(text_height): for x in range(self.wcl.WCA_WIDTH): self.wcl.setColorBy2DCoordinates( self.strip, x, y, fg_color if text_as_pixel.pixels[y * text_width + x + cur_offset] else bg_color) self.show() if self.wci.waitForExit(1.0 / fps): return def setMinutes(self, time, color): if time.minute % 5 != 0: for i in range(1, time.minute % 5 + 1): self.setPixelColor(self.wcl.mapMinutes(i), color) def show(self): """ This function provides the current color settings to the LEDs """ self.strip.show()
class wordclock_display: """ Class to display any content on the wordclock display Depends on the (individual) wordclock layout/wiring """ def __init__(self, config, wci): """ Initialization """ # Get the wordclocks wiring-layout self.wcl = wiring.wiring(config) self.wci = wci try: default_brightness = config.getint('wordclock_display', 'brightness') except: default_brightness = 255 print( 'WARNING: Default brightness value not set in config-file: ' 'To do so, add a "brightness" between 1..255 to the [wordclock_display]-section.') if config.getboolean('wordclock', 'developer_mode'): from GTKstrip import GTKstrip self.strip = GTKstrip(wci) self.default_font = os.path.join('/usr/share/fonts/TTF/', config.get('wordclock_display', 'default_font') + '.ttf') else: try: from neopixel import Adafruit_NeoPixel, ws self.strip = Adafruit_NeoPixel(self.wcl.LED_COUNT, self.wcl.LED_PIN, self.wcl.LED_FREQ_HZ, self.wcl.LED_DMA, self.wcl.LED_INVERT, default_brightness , 0, ws.WS2811_STRIP_GRB) except: print('Update deprecated external dependency rpi_ws281x. ' 'For details see also https://github.com/jgarff/rpi_ws281x/blob/master/python/README.md') self.default_font = os.path.join('/usr/share/fonts/truetype/freefont/', config.get('wordclock_display', 'default_font') + '.ttf') # Initialize the NeoPixel object self.strip.begin() self.default_fg_color = wcc.WWHITE self.default_bg_color = wcc.BLACK self.base_path = config.get('wordclock', 'base_path') # Choose language try: language = ''.join(config.get('wordclock_display', 'language')) except: # For backward compatibility language = ''.join(config.get('plugin_time_default', 'language')) print(' Setting language to ' + language + '.') if language == 'dutch': self.taw = time_dutch.time_dutch() elif language == 'english': self.taw = time_english.time_english() elif language == 'german': self.taw = time_german.time_german() elif language == 'german2': self.taw = time_german2.time_german2() elif language == 'swabian': self.taw = time_swabian.time_swabian() elif language == 'swabian2': self.taw = time_swabian2.time_swabian2() elif language == 'bavarian': self.taw = time_bavarian.time_bavarian() elif language == 'swiss_german': self.taw = time_swiss_german.time_swiss_german() elif language == 'swiss_german2': self.taw = time_swiss_german2.time_swiss_german2() else: print('Could not detect language: ' + language + '.') print('Choosing default: german') self.taw = time_german.time_german() def setPixelColor(self, pixel, color): """ Sets the color for a pixel, while considering the brightness, set within the config file """ self.strip.setPixelColor(pixel, color) def getBrightness(self): """ Sets the color for a pixel, while considering the brightness, set within the config file """ return self.strip.getBrightness() def setBrightness(self, brightness): """ Sets the color for a pixel, while considering the brightness, set within the config file """ self.strip.setBrightness(brightness) self.show() def setColorBy1DCoordinates(self, *args, **kwargs): """ Sets a pixel at given 1D coordinates """ return self.wcl.setColorBy1DCoordinates(*args, **kwargs) def setColorBy2DCoordinates(self, *args, **kwargs): """ Sets a pixel at given 2D coordinates """ return self.wcl.setColorBy2DCoordinates(*args, **kwargs) def get_wca_height(self): """ Returns the height of the WCA """ return self.wcl.WCA_HEIGHT def get_wca_width(self): """ Returns the height of the WCA """ return self.wcl.WCA_WIDTH def get_led_count(self): """ Returns the overall number of LEDs """ return self.wcl.LED_COUNT def dispRes(self): """ Returns the resolution of the wordclock array as string E.g. to choose the correct resolution of animations and icons """ return str(self.wcl.WCA_WIDTH) + 'x' + str(self.wcl.WCA_HEIGHT) def setColorToAll(self, color, includeMinutes=True): """ Sets a given color to all leds If includeMinutes is set to True, color will also be applied to the minute-leds. """ if includeMinutes: for i in range(self.wcl.LED_COUNT): self.setPixelColor(i, color) else: for i in self.wcl.getWcaIndices(): self.setPixelColor(i, color) def setColorTemperatureToAll(self, temperature, includeMinutes=True): """ Sets a color to all leds based on the provided temperature in Kelvin If includeMinutes is set to True, color will also be applied to the minute-leds. """ self.setColorToAll(wcc.color_temperature_to_rgb(temperature), includeMinutes) def resetDisplay(self): """ Reset display """ self.setColorToAll(wcc.BLACK, True) def showIcon(self, plugin, iconName): """ Dispays an icon with a specified name. The icon needs to be provided within the graphics/icons folder. """ self.setImage( self.base_path + '/wordclock_plugins/' + plugin + '/icons/' + self.dispRes() + '/' + iconName + '.png') def setImage(self, absPathToImage): """ Set image (provided as absolute path) to current display """ img = Image.open(absPathToImage) width, height = img.size for x in range(0, width): for y in range(0, height): rgb_img = img.convert('RGB') r, g, b = rgb_img.getpixel((x, y)) self.wcl.setColorBy2DCoordinates(self.strip, x, y, wcc.Color(r, g, b)) self.show() def animate(self, plugin, animationName, fps=10, count=1, invert=False): """ Runs an animation plugin: Plugin-name num_of_frames: Number of frames to be displayed count: Number of runs fps: frames per second invert: Invert order of animation """ animation_dir = self.base_path + '/wordclock_plugins/' + plugin + '/animations/' + self.dispRes() + '/' + animationName + '/' num_of_frames = len([file_count for file_count in os.listdir(animation_dir)]) if invert: animation_range = range(num_of_frames - 1, -1, -1) else: animation_range = range(0, num_of_frames) for _ in range(count): for i in animation_range: self.setImage(animation_dir + str(i).zfill(3) + '.png') if self.wci.waitForExit(1.0 / fps): return def showText(self, text, font=None, fg_color=None, bg_color=None, fps=10, count=1): """ Display text on display """ if font is None: font = self.default_font if fg_color is None: fg_color = self.default_fg_color if bg_color is None: bg_color = self.default_bg_color text = ' ' + text + ' ' fnt = fontdemo.Font(font, self.wcl.WCA_HEIGHT) text_width, text_height, text_max_descent = fnt.text_dimensions(text) text_as_pixel = fnt.render_text(text) # Display text count times for i in range(count): # Erase previous content self.setColorToAll(bg_color, includeMinutes=True) # Assure here correct rendering, if the text does not fill the whole display render_range = self.wcl.WCA_WIDTH if self.wcl.WCA_WIDTH < text_width else text_width for y in range(text_height): for x in range(render_range): self.wcl.setColorBy2DCoordinates(self.strip, x, y, fg_color if text_as_pixel.pixels[y * text_width + x] else bg_color) # Show first frame for 0.5 seconds self.show() if self.wci.waitForExit(0.5): return # Shift text from left to right to show all. for cur_offset in range(text_width - self.wcl.WCA_WIDTH + 1): for y in range(text_height): for x in range(self.wcl.WCA_WIDTH): self.wcl.setColorBy2DCoordinates(self.strip, x, y, fg_color if text_as_pixel.pixels[ y * text_width + x + cur_offset] else bg_color) self.show() if self.wci.waitForExit(1.0 / fps): return def setMinutes(self, time, color): if time.minute % 5 != 0: for i in range(1, time.minute % 5 + 1): self.setPixelColor(self.wcl.mapMinutes(i), color) def show(self): """ This function provides the current color settings to the LEDs """ self.strip.show()