def __init__(self): """ Construct the EPDView object. """ self.__epd = epd7in5b_HD.EPD() self.__epd.init() self.__epd.Clear()
def display(image): try: epd = epd7in5b_HD.EPD() # TODO: assert that epd.height == 880 and epd.width == 528 logging.info("init and Clear") epd.init() epd.Clear() epd.display(epd.getbuffer(image.black), epd.getbuffer(image.red)) time.sleep(2) #logging.info("Clear...") #epd.init() #epd.Clear() logging.info("Goto Sleep...") epd.sleep() time.sleep(3) epd.Dev_exit() except IOError as e: logging.info(e) except KeyboardInterrupt: logging.info("ctrl + c:") epd7in5b_HD.epdconfig.module_exit() exit()
# add last line to the headline if len(hl) == 0: hl = hll[:] else: hl += '\n' + hll lines += 1 # calculate height, in pixels, of the whole headline height = font.getsize(hl)[1] * lines # send back formatted headline and height as a tuple return (hl, height) epd = epd7in5b_HD.EPD() epd.init() epd.Clear() while True: # constants for positioning content BASE_HEADLINE_Y = 70 COL_1_X = 5 COL_2_X = 298 COL_3_X = 592 NEWS_WIDTH = 280 # fonts for display headlineFont = ImageFont.truetype('/home/pi/WNStation/times.ttf', 22) newsFont = ImageFont.truetype('/home/pi/WNStation/times.ttf', 18)
def draw(draws): # Resources text_font = ImageFont.truetype('fonts/Ubuntu-C.ttf', 48) author_font = ImageFont.truetype('fonts/Ubuntu-L.ttf', 32) total_w, total_h = 880, 528 _, atom_h = author_font.getsize('e') author_w, author_h = total_w, atom_h + 32 text_w, text_h = total_w, total_h - author_h atom_w, atom_h = text_font.getsize('e') cols, lines = text_w // atom_w, text_h // atom_h black_image = Image.new('1', (total_w, total_h), color=255) red_image = Image.new('1', (total_w, total_h), color=255) if PRODUCTION else black_image black_draw = ImageDraw.Draw(black_image) red_draw = ImageDraw.Draw(red_image) if PRODUCTION else black_draw if PRODUCTION: display = epd.EPD() # Main loop thread = threading.current_thread() while thread.running: try: quote = draws.get(timeout=5) except queue.Empty: # This is meant to allow the thread to stop even if the queue is empty continue logging.info(f"Drawing quote {quote['id']}") # Render text text = textwrap.fill(quote['text'], width=cols, max_lines=lines, replace_whitespace=False) w, h = text_font.getsize_multiline(text, spacing=0) pos = (text_w - w) // 2, (text_h - h) // 2 black_draw.text(pos, text=text, font=text_font, fill=0, spacing=0, align='center') # Render author author = quote['author'] + ' ' + quote['context'] w, h = author_font.getsize(author) pos = (author_w - w) // 2, text_h + (author_h - h) // 2 red_draw.text(pos, text=author, font=author_font, fill=0) # Display on screen if PRODUCTION: display.init() display.display(display.getbuffer(black_image), display.getbuffer(red_image)) display.sleep() else: black_image.show() # Clear for next drawing black_draw.rectangle((0, 0, total_w, total_h), fill=255) red_draw.rectangle((0, 0, total_w, total_h), fill=255) logging.info('Drawing done') if PRODUCTION: display.Dev_exit()
def main(): cmdparser = optparse.OptionParser() cmdparser.add_option("--noclear", action="store_true", default=False, help="Do not clear the eink before drawing.") cmdparser.add_option("--noweather", action="store_true", default=False, help="Do not draw the weather forecast.") cmdparser.add_option( "-o", "--output", type="string", default="", help= "Save the output to two files called output-b.png and output-r.png. Will not attempt to draw on eink." ) cmdparser.add_option( "-i", "--input", type="string", default="", help= "Do not render a frame, draw the provided images to the eink. This option is the 'black' image. Use in conjunction with --redinput." ) cmdparser.add_option("-r", "--redinput", type="string", default="", help="The 'red' image file for use with --input.") cmdparser.add_option("-c", "--calendars", type="string", default="primary", help="Calendar IDs to use, delimited by comma.") cmdparser.add_option("-v", "--verbose", action="store_true", default=False, help="Verbose mode.") cmdparser.add_option("--width", type="int", default=880, help="If not drawing to eink, output width of image.") cmdparser.add_option( "--height", type="int", default=528, help="If not drawing to eink, output height of image.") cmdparser.add_option("-d", "--days", type="int", default=7, help="Number of days to look ahead for events.") cmdparser.add_option( "--cache", type="string", help= "Filename of cache file to save fetched events to. Will only redraw if the events have changed." ) cmdparser.add_option( "--cachehours", type="int", default=2, help="Number of hours that the cache remains valid for.") (options, _) = cmdparser.parse_args() if (options.redinput != "" and options.input == "") or (options.redinput == "" and options.input != ""): print("Options --input and --redinput must be used together.") sys.exit(1) if options.output == "": if platform.system() != "Linux": print( "Cannot use the Waveshare libraries on a non-Linux platform. Use the --output option to output to an image file." ) sys.exit(1) try: from waveshare_epd import epd7in5b_HD except ImportError: print( "Could not find the waveshare_epd libraries. Download the Waveshare Python code from https://www.waveshare.com/wiki/7.5inch_HD_e-Paper_HAT_%28B%29 and ensure it is visible to Python." ) sys.exit(1) epd = epd7in5b_HD.EPD() epd.init() options.width = epd.width options.height = epd.height redraw = True if options.input == "": calservice = registerCalendarService() ids = getIDsFromNames(calservice, options.calendars) events = getEvents(calservice, ids) days = eventsToDays(events, options.days) if not options.noweather: if os.path.exists(os.path.join(curdir, 'weather.json')): with open(os.path.join(curdir, 'weather.json')) as f: data = json.load(f) if not "lat" in data and not "lon" in data and not "apikey" in data: print("weather.json is in an incorrect format.") sys.exit() else: weatherjson = '{\n\t"lat": "54.9755153",\n\t"lon": "-1.6222127",\n\t"apikey": "00000000-0000-0000-0000-000000000000"\n}' with open(os.path.join(curdir, 'weather.json'), 'a') as f: f.write(weatherjson) print("weather.json created. Fill it in and retry.") sys.exit() mo = metoffer.MetOffer(data['apikey']) forecast = mo.nearest_loc_forecast(float(data['lat']), float(data['lon']), metoffer.THREE_HOURLY) dailyforecast = mo.nearest_loc_forecast(float(data['lat']), float(data['lon']), metoffer.DAILY) weather = (metoffer.Weather(forecast), metoffer.Weather(dailyforecast)) if options.verbose: print(f"Weather forecast for {weather[0].name} fetched.") else: weather = None if options.cache != None: cachefile = os.path.join(curdir, options.cache) if os.path.exists(cachefile): try: (cacheddays, cachedtime) = pickle.load(open(cachefile, "rb")) if cacheddays == days and datetime.now( ) < cachedtime + timedelta(hours=options.cachehours): #Nothing has changed if options.verbose: print(f"Fetched data is the same as cached data.") redraw = False except: #Any errors handling the cache, just invalitate it print("Error whilst reading cache: ", sys.exc_info()[0]) #Else we will update the cache #We store the days dictionary and the current time in the cache, because the MetOffice objects are incomparable and this #is the easiest way to ensure that we update at least frequently enough to show the new forecast. pickle.dump((days, datetime.now()), open(cachefile, "wb")) if options.verbose: pprint.pprint(days) if redraw: if options.verbose: print("Drawing frame.") (bimage, rimage) = renderFrame(options.width, options.height, days, weather) else: bimage = Image.open(os.path.join(curdir, options.input)) rimage = Image.open(os.path.join(curdir, options.redinput)) if options.output == "": if redraw: if options.noclear == False: if options.verbose: print("Clearing eink.") epd.Clear() if options.verbose: print("eink cleared.") if options.verbose: print("Sending frame to eink.") epd.display(epd.getbuffer(bimage), epd.getbuffer(rimage)) if options.verbose: print("eink done.") epd.sleep() else: if options.verbose: print("eink not updated because fetched data matches cache.") else: if redraw: bimage.save(options.output + "-b.png", format="png") rimage.save(options.output + "-r.png", format="png") if options.verbose: print( f"Rendered frame saved to {options.output}-b.png and {options.output}-r.png." ) else: if options.verbose: print( "Output files not updated because fetched data matches cache." )