예제 #1
0
 def __init__(self):
     """
     Construct the EPDView object.
     """
     self.__epd = epd7in5b_HD.EPD()
     self.__epd.init()
     self.__epd.Clear()
예제 #2
0
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()
예제 #3
0
    # 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)
예제 #4
0
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()
예제 #5
0
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."
                )