inky-lovelace/inky-lovelace.py

210 lines
5.8 KiB
Python

# inky-lovelace.py: Fetch a lovelace dashboard and display it on an inky-frame.
# Copyright (C) 2023 Paco Hope <github@filter.paco.to>
#
# 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 ntptime
import inky_frame
import pngdec
import gc
import sdcard
import os
import CONFIG
import inky_frame
import uasyncio
global graphics
def display_image(j):
# Open the PNG file
j.open_file(CONFIG.FILENAME)
graphics.set_pen(0)
graphics.set_font('sans')
# Decode the PNG
try:
j.decode()
except Exception as e:
# if that fails, try to recover enough to show
# an error. This seems to fail sometimes and I
# don't know why.
del j
gc.collect()
graphics.text( e, 10, 200, scale=1 )
# write the date/time at the top
tz_seconds = (CONFIG.TZ_OFFSET * 3600)
year, month, day, hour, minute, second, dow, _ = time.gmtime(time.time() + tz_seconds)
# 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():
""" 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()
tz_seconds = (CONFIG.TZ_OFFSET * 3600)
year, month, day, hour, minute, second, dow, _ = time.gmtime(time.time() + tz_seconds)
print(f"{hour:02}:{minute:02} {year}-{month}-{day}")
count = 0
if year < 2023:
# blink d when we are setting the time
inky_frame.button_d.led_on()
ntptime.host = '0.us.pool.ntp.org'
while count < 5:
try:
inky_frame.set_time() # set time from network
except OSError as e:
print( "x", end="" )
count += 1
inky_frame.button_d.led_off()
if count >= 5:
# turn on E to indicate error. gets turned off later.
inky_frame.button_e.led_on()
year, month, day, hour, minute, second, dow, _ = time.gmtime(time.time() + tz_seconds)
# don't bother updating before 06:00 or after 23:00
if hour < 5 or hour > 22:
print(f"hour {hour:02}, no need to fetch")
return False
del year, month, day, hour, minute, second, tz_seconds, count
print( "fetching " + CONFIG.IMG_URL )
inky_frame.button_c.led_on()
try:
# Grab the image
socket = urequest.urlopen(CONFIG.IMG_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():
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)
# 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)