diff --git a/blocklists.py b/blocklists.py new file mode 100644 index 0000000..7b449e2 --- /dev/null +++ b/blocklists.py @@ -0,0 +1,57 @@ +import atexit +import logging +import os +import pathlib +import time + +import requests + +import config +import screen + +log = logging.getLogger("blocklists") +running = True + +blocklist_data_src = "https://github.com/hagezi/dns-blocklists/archive/refs/heads/main.zip" +blocklist_data_src_path = "dnsmasq" +blocklist_lib_path = "/var/lib/starcontrol" + +log.debug("preparing cache directories") +pth_cache_root = pathlib.Path(config.cparse["internal"]["cache_root"]) +pth_cache_root.mkdir(exist_ok=True, parents=True) +pth_blocklist_lib = pathlib.Path(blocklist_lib_path) +pth_blocklist_lib.mkdir(exist_ok=True, parents=True) + +def blocklist_check(): + log.debug("checking blocklist mtimes and updating if needed") + try: + ts_payload = os.path.getmtime(pth_blocklist_lib / "dns-blocklists-main.zip") + ts_now = time.time() + if (ts_now - ts_payload) >= 604800: + update_blocklists() + except OSError: + update_blocklists() + +def update_blocklists(): + log.info("blocklist update in progress") + screen.download_state = 1 + + fd_listpayload = open(pth_blocklist_lib / "dns-blocklists-main.zip", "wb") + hnd_download = requests.get(blocklist_data_src, stream=True) + + for chunk in hnd_download.iter_content(chunk_size=256): + fd_listpayload.write(chunk) + + hnd_download.close() + fd_listpayload.close() + screen.download_state = 0 + +def blocklist_thread(): + while running: + blocklist_check() + time.sleep(3600) # 1 hour + +@atexit.register +def stop_blocklist(): + global running + running = False \ No newline at end of file diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..d6df357 --- /dev/null +++ b/config.ini @@ -0,0 +1,2 @@ +[internal] +cache_root=/run/starcontrol \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..257ceac --- /dev/null +++ b/config.py @@ -0,0 +1,8 @@ +import configparser +import logging + +log = logging.getLogger("main") + +log.debug("reading configuration file") +cparse = configparser.ConfigParser() +cparse.read("config.ini") \ No newline at end of file diff --git a/mainloop.py b/mainloop.py new file mode 100755 index 0000000..89c7ad7 --- /dev/null +++ b/mainloop.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +import logging +import os +import math +import pathlib +import time +import threading + +from PIL import Image, ImageDraw + +import blocklists +import screen + +log = logging.getLogger("main") + +if __name__ == "__main__": + screen.screen_init() + screen.scr_light.value = True + + img_canvas = Image.new("RGB", (135, 240)) + hnd_canvas = ImageDraw.Draw(img_canvas) + hnd_canvas.rectangle((0,0,135,240), (0,0,0)) + img_canvas.paste(Image.open("res/image/hourglass.png"),(math.floor(135/2) - math.ceil(49/2),math.floor(240/2) - math.ceil(90/2))) + screen.scr.image(img_canvas) + + thr_blocklists = threading.Thread(target=blocklists.blocklist_thread) + thr_screen = threading.Thread(target=screen.screen_thread) + thr_blocklists.start() + thr_screen.start() + + ok = True + while ok: + try: + thr_blocklists.join(5.0) + thr_screen.join(5.0) + ok = thr_blocklists.is_alive() and thr_screen.is_alive() + except KeyboardInterrupt: + ok = False + print("KEYBOARD INTERRUPT") + except: + ok = False + + screen.running = False + blocklists.running = False + thr_screen.join() + thr_blocklists.join() \ No newline at end of file diff --git a/pitft-screen-test.py b/pitft-screen-test.py new file mode 100755 index 0000000..e0c80bf --- /dev/null +++ b/pitft-screen-test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import digitalio +import board + +from adafruit_rgb_display.rgb import color565 +from adafruit_rgb_display import st7789 + +# Configuration for CS and DC pins for Raspberry Pi +cs_pin = digitalio.DigitalInOut(board.CE0) +dc_pin = digitalio.DigitalInOut(board.D25) +reset_pin = None +BAUDRATE = 64000000 # The pi can be very fast! +# Create the ST7789 display: +display = st7789.ST7789( + board.SPI(), + cs=cs_pin, + dc=dc_pin, + rst=reset_pin, + baudrate=BAUDRATE, + width=135, + height=240, + x_offset=53, + y_offset=40, +) + +backlight = digitalio.DigitalInOut(board.D22) +backlight.switch_to_output() +backlight.value = True +buttonA = digitalio.DigitalInOut(board.D23) +buttonB = digitalio.DigitalInOut(board.D24) +buttonA.switch_to_input() +buttonB.switch_to_input() + +buttonAToggle = False +buttonBToggle = False + +# Main loop: +while True: + if buttonA.value and buttonB.value: + backlight.value = False # turn off backlight + else: + backlight.value = True # turn on backlight + if buttonB.value and not buttonA.value: # just button A pressed + display.fill(color565(255, 0, 0)) # red + if buttonA.value and not buttonB.value: # just button B pressed + display.fill(color565(0, 0, 255)) # blue + if not buttonA.value and not buttonB.value: # none pressed + display.fill(color565(0, 255, 0)) # green + + if buttonA.value != buttonAToggle: + print("buttonA toggle state") + buttonAToggle = buttonA.value + if buttonB.value != buttonBToggle: + print("buttonB toggle state") + buttonBToggle = buttonB.value \ No newline at end of file diff --git a/res/image/dns.png b/res/image/dns.png new file mode 100644 index 0000000..f212ace Binary files /dev/null and b/res/image/dns.png differ diff --git a/res/image/download.png b/res/image/download.png new file mode 100644 index 0000000..1917f0d Binary files /dev/null and b/res/image/download.png differ diff --git a/res/image/hourglass.png b/res/image/hourglass.png new file mode 100644 index 0000000..5d599d3 Binary files /dev/null and b/res/image/hourglass.png differ diff --git a/res/image/okay.png b/res/image/okay.png new file mode 100644 index 0000000..9d8584b Binary files /dev/null and b/res/image/okay.png differ diff --git a/res/image/problem.png b/res/image/problem.png new file mode 100644 index 0000000..f73ed2d Binary files /dev/null and b/res/image/problem.png differ diff --git a/res/image/wifi.png b/res/image/wifi.png new file mode 100644 index 0000000..7258405 Binary files /dev/null and b/res/image/wifi.png differ diff --git a/screen.py b/screen.py new file mode 100644 index 0000000..9b89a45 --- /dev/null +++ b/screen.py @@ -0,0 +1,86 @@ +import atexit +import math +import time + +import board +import digitalio +from adafruit_rgb_display import rgb, st7789 +from PIL import Image, ImageDraw, ImageOps + +running = True +scr = None +scr_light = None +btn_upper = None +btn_lower = None +def screen_init(): + global scr + global scr_light + global btn_upper + global btn_lower + + scr_light = digitalio.DigitalInOut(board.D22) + scr_light.switch_to_output() + scr_light.value = True + + # Configuration for CS and DC pins for Raspberry Pi + cs_pin = digitalio.DigitalInOut(board.CE0) + dc_pin = digitalio.DigitalInOut(board.D25) + reset_pin = None + BAUDRATE = 64000000 # The pi can be very fast! + # Create the ST7789 display: + scr = st7789.ST7789( + board.SPI(), + cs=cs_pin, + dc=dc_pin, + rst=reset_pin, + baudrate=BAUDRATE, + width=135, + height=240, + x_offset=53, + y_offset=40, + ) + + scr.fill(rgb.color565(0,0,0)) + btn_upper = digitalio.DigitalInOut(board.D23) + btn_lower = digitalio.DigitalInOut(board.D24) + btn_upper.switch_to_input() + btn_lower.switch_to_input() + +@atexit.register +def screen_deinit(): + global running + running = False # stops update thread + + scr.fill(rgb.color565(0,0,0)) + scr_light.value = False + +current_icon = None +wifi_state = 0 # -1 error, 0 disconnected, 1 connected +dns_state = 0 # -1 error, 0 off, 1 running +download_state = 0 # yes/no +screen_on = True + +def screen_update(): + global current_icon + + if wifi_state < 1 or dns_state < 1: + current_icon = Image.open("res/image/problem.png") # ⚠️ + else: + current_icon = Image.open("res/image/okay.png") # ✅ + + #scr.fill(rgb.color565(0,0,0)) # blank + img_canvas = Image.new("RGB", (135, 240)) + hnd_canvas = ImageDraw.Draw(img_canvas) + hnd_canvas.rectangle([(0,0), (135,240)], fill=(0,0,0)) + hnd_canvas.text((math.floor(135/2), 0), time.strftime("%H:%M"), # top center of screen, "16:20" + fill=(50,200,255), anchor="mt", # cyan, middle-top + font_size=16) + + img_canvas.paste(current_icon, (math.floor(135/2) - 32, math.floor(240/2) - 32)) + + scr.image(img_canvas) + +def screen_thread(): + while running: + screen_update() + time.sleep(1) \ No newline at end of file