# inky-lovelace.py: Fetch a lovelace dashboard and display it on an inky-frame. # Copyright (C) 2023 Paco Hope # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # See LICENSE.md for details. """ Copy a PNG into the root of the pico from a known URL Display it. Sleep """ from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7" from urllib import urequest from machine import Pin, SPI from network_manager import NetworkManager import time import inky_frame import pngdec import gc import sdcard import os import CONFIG import inky_frame import uasyncio global graphics def display_image(j, CONFIG.FILENAME): # Open the PNG file j.open_file(CONFIG.FILENAME) # Decode the PNG j.decode() # write the date/time at the top year, month, day, hour, minute, second, dow, _ = time.localtime(time.time()) graphics.set_pen(0) graphics.set_font('sans') # display.text(text, x, y, wordwrap, scale, angle, spacing) graphics.text(f"{hour:02}:{minute:02} {year}-{month}-{day}", 230, 10, scale=0.5) gc.collect() def show_error(text,y): WIDTH = 600 HEIGHT = 0 + 20 * y graphics.set_pen(4) graphics.rectangle(0, HEIGHT, WIDTH, 35) graphics.set_pen(1) graphics.text(text, 5, 16 + HEIGHT, 400, 2) gc.collect() def fetch(url): """ Fetch the image, store it onboard. Returns False at first error. Returns True only if everything succeeds. """ inky_frame.button_b.led_on() network_manager = NetworkManager(CONFIG.COUNTRY, status_handler=status_handler, client_timeout=60) try: uasyncio.get_event_loop().run_until_complete(network_manager.client(CONFIG.SSID, CONFIG.PSK)) except RuntimeError: pass except Exception as e: print("join network failed") print(e) inky_frame.button_b.led_off() return False inky_frame.button_b.led_off() year, month, day, hour, minute, second, dow, _ = time.localtime(time.time()) print(f"{hour:02}:{minute:02} {year}-{month}-{day}") if year < 2023: # blink b when we are setting the time inky_frame.button_b.led_on() inky_frame.set_time() # set time from network inky_frame.button_b.led_off() tz_seconds = (CONFIG.TZ_OFFSET * 3600) year, month, day, hour, minute, second, dow, _ = time.localtime(time.time() + tz_seconds) # don't bother updating before 06:00 or after 23:00 if hour < 5 or hour > 22: print("no need to fetch") return False del year, month, day, hour, minute, second, tz_seconds print( "fetching " + url ) inky_frame.button_c.led_on() try: # Grab the image socket = urequest.urlopen(url) gc.collect() except OSError as e: print("Unable open URL. OSErr: ") print(e) except Exception as e: print("other exception") print(e) return False os.remove(CONFIG.FILENAME) inky_frame.button_c.led_off() inky_frame.button_d.led_on() try: data = bytearray(1024) with open(CONFIG.FILENAME, "wb+") as f: while True: if socket.readinto(data) == 0: break else: print(".", end="" ) f.write(data) socket.close() del data except Exception as e: print("Unable to write file") print(e) return False del socket inky_frame.button_d.led_off() gc.collect() return True def status_handler(mode, status, ip): print(mode, status, ip) def mount_sd(): # set up the SD card try: sd_spi = SPI(0, sck=Pin(18, Pin.OUT), mosi=Pin(19, Pin.OUT), miso=Pin(16, Pin.OUT)) sd = sdcard.SDCard(sd_spi, Pin(22)) os.mount(sd, "/sd") except Exception as e: print("mounting SD card") print(e) files = os.listdir("/sd") if len(files) == 0: show_error("SD didn't mount?", 5) del files while True: graphics = None gc.collect() inky_frame.led_busy.off() inky_frame.button_a.led_off() inky_frame.button_b.led_off() inky_frame.button_c.led_off() inky_frame.button_d.led_off() inky_frame.button_e.led_off() inky_frame.led_busy.on() inky_frame.button_a.led_on() mount_sd() gc.collect() inky_frame.button_a.led_off() if fetch(CONFIG.IMG_URL): gc.collect() inky_frame.button_e.led_on() # set up the display graphics = PicoGraphics(DISPLAY) # Create a new PNG decoder for our PicoGraphics j = pngdec.PNG(graphics) display_image(j, CONFIG.FILENAME) # Display the result graphics.update() inky_frame.button_e.led_off() inky_frame.led_busy.off() # Go to sleep if on battery power inky_frame.button_a.led_off() inky_frame.button_b.led_off() inky_frame.button_c.led_off() inky_frame.button_d.led_off() inky_frame.button_e.led_off() inky_frame.sleep_for(CONFIG.UPDATE_INTERVAL)