From 9346fcf750ad7612f4f045fe2b4c1f6e639afaef Mon Sep 17 00:00:00 2001 From: Ace Date: Sat, 11 May 2024 23:25:40 +0200 Subject: [PATCH 01/23] code quality improvements --- inkycal/__init__.py | 2 +- inkycal/display/display.py | 6 +----- inkycal/display/drivers/epdconfig.py | 3 --- inkycal/modules/inkycal_calendar.py | 6 +++--- tests/test_inkycal_agenda.py | 2 +- tests/test_inkycal_webshot.py | 10 ++++++++-- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/inkycal/__init__.py b/inkycal/__init__.py index 0c6a244..6c721f5 100644 --- a/inkycal/__init__.py +++ b/inkycal/__init__.py @@ -14,7 +14,7 @@ import inkycal.modules.inkycal_stocks import inkycal.modules.inkycal_webshot import inkycal.modules.inkycal_xkcd import inkycal.modules.inkycal_fullweather +import inkycal.modules.inkycal_mawaqit # Main file from inkycal.main import Inkycal -import inkycal.modules.inkycal_stocks diff --git a/inkycal/display/display.py b/inkycal/display/display.py index 89fdf4c..179f71f 100644 --- a/inkycal/display/display.py +++ b/inkycal/display/display.py @@ -2,13 +2,11 @@ Inkycal ePaper driving functions Copyright by aceisace """ -import os from importlib import import_module import PIL from PIL import Image -from inkycal.custom import top_level from inkycal.display.supported_models import supported_models @@ -199,9 +197,7 @@ class Display: >>> Display.get_display_names() """ - driver_files = top_level + '/inkycal/display/drivers/' - drivers = [i for i in os.listdir(driver_files) if i.endswith(".py") and not i.startswith("__") and "_" in i] - return drivers + return supported_models.keys() if __name__ == '__main__': diff --git a/inkycal/display/drivers/epdconfig.py b/inkycal/display/drivers/epdconfig.py index 2eaddf4..2a5e819 100644 --- a/inkycal/display/drivers/epdconfig.py +++ b/inkycal/display/drivers/epdconfig.py @@ -28,8 +28,6 @@ THE SOFTWARE. """ import logging -import os -import subprocess import sys import time @@ -128,4 +126,3 @@ implementation = RaspberryPi() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) - diff --git a/inkycal/modules/inkycal_calendar.py b/inkycal/modules/inkycal_calendar.py index 9c85984..8d0cd50 100755 --- a/inkycal/modules/inkycal_calendar.py +++ b/inkycal/modules/inkycal_calendar.py @@ -109,7 +109,7 @@ class Calendar(inkycal_module): # Allocate space for month-names, weekdays etc. month_name_height = int(im_height * 0.10) text_bbox_height = self.font.getbbox("hg") - weekdays_height = int((text_bbox_height[3] - text_bbox_height[1])* 1.25) + weekdays_height = int((text_bbox_height[3] - text_bbox_height[1]) * 1.25) logger.debug(f"month_name_height: {month_name_height}") logger.debug(f"weekdays_height: {weekdays_height}") @@ -265,7 +265,7 @@ class Calendar(inkycal_module): # find out how many lines can fit at max in the event section line_spacing = 2 text_bbox_height = self.font.getbbox("hg") - line_height = text_bbox_height[3] + line_spacing + line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing max_event_lines = events_height // (line_height + line_spacing) # generate list of coordinates for each line @@ -326,7 +326,7 @@ class Calendar(inkycal_module): (icon_width, icon_height), radius=6, thickness=1, - shrinkage=(0.4, 0.2), + shrinkage=(0, 0), ) # Filter upcoming events until 4 weeks in the future diff --git a/tests/test_inkycal_agenda.py b/tests/test_inkycal_agenda.py index af002ec..642f654 100755 --- a/tests/test_inkycal_agenda.py +++ b/tests/test_inkycal_agenda.py @@ -28,7 +28,7 @@ tests = [ "padding_x": 10, "padding_y": 10, "fontsize": 12, - "language": "en" + "language": "de" } }, { diff --git a/tests/test_inkycal_webshot.py b/tests/test_inkycal_webshot.py index 7105a12..b86c965 100755 --- a/tests/test_inkycal_webshot.py +++ b/tests/test_inkycal_webshot.py @@ -6,10 +6,15 @@ import logging import unittest from inkycal.modules import Webshot +from inkycal.modules.inky_image import Inkyimage +from tests import Config logger = logging.getLogger(__name__) logging.basicConfig(level=logging.DEBUG) +preview = Inkyimage.preview +merge = Inkyimage.merge + tests = [ { "position": 1, @@ -60,6 +65,7 @@ class TestWebshot(unittest.TestCase): for test in tests: logger.info(f'test {tests.index(test) + 1} generating image..') module = Webshot(test) - module.generate_image() + im_black, im_colour = module.generate_image() + if Config.USE_PREVIEW: + preview(merge(im_black, im_colour)) logger.info('OK') - From 610f246c0276e4145218133d7b02f169213a1709 Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 12 May 2024 02:00:26 +0200 Subject: [PATCH 02/23] checkpoint --- {icons => fonts}/ui-icons/home_temp.png | Bin {icons => fonts}/ui-icons/humidity.bmp | Bin .../outline_thermostat_white_48dp.bmp | Bin {icons => fonts}/ui-icons/rain-chance.bmp | Bin {icons => fonts}/ui-icons/uv.bmp | Bin {icons => fonts}/ui-icons/wind.bmp | Bin inkycal/__init__.py | 13 +- inkycal/custom/functions.py | 26 +-- inkycal/display/drivers/10_in_3.py | 14 +- inkycal/display/drivers/7_in_8.py | 14 +- inkycal/display/drivers/9_in_7.py | 12 +- inkycal/loggers.py | 35 +++ inkycal/main.py | 206 ++++++++---------- inkycal/modules/inkycal_fullweather.py | 8 +- inkycal/modules/inkycal_slideshow.py | 25 ++- inkycal/modules/inkycal_xkcd.py | 4 +- inkycal/settings.py | 20 ++ inkycal/utils/json_cache.py | 28 +++ 18 files changed, 225 insertions(+), 180 deletions(-) rename {icons => fonts}/ui-icons/home_temp.png (100%) rename {icons => fonts}/ui-icons/humidity.bmp (100%) rename {icons => fonts}/ui-icons/outline_thermostat_white_48dp.bmp (100%) rename {icons => fonts}/ui-icons/rain-chance.bmp (100%) rename {icons => fonts}/ui-icons/uv.bmp (100%) rename {icons => fonts}/ui-icons/wind.bmp (100%) create mode 100644 inkycal/loggers.py create mode 100644 inkycal/settings.py create mode 100644 inkycal/utils/json_cache.py diff --git a/icons/ui-icons/home_temp.png b/fonts/ui-icons/home_temp.png similarity index 100% rename from icons/ui-icons/home_temp.png rename to fonts/ui-icons/home_temp.png diff --git a/icons/ui-icons/humidity.bmp b/fonts/ui-icons/humidity.bmp similarity index 100% rename from icons/ui-icons/humidity.bmp rename to fonts/ui-icons/humidity.bmp diff --git a/icons/ui-icons/outline_thermostat_white_48dp.bmp b/fonts/ui-icons/outline_thermostat_white_48dp.bmp similarity index 100% rename from icons/ui-icons/outline_thermostat_white_48dp.bmp rename to fonts/ui-icons/outline_thermostat_white_48dp.bmp diff --git a/icons/ui-icons/rain-chance.bmp b/fonts/ui-icons/rain-chance.bmp similarity index 100% rename from icons/ui-icons/rain-chance.bmp rename to fonts/ui-icons/rain-chance.bmp diff --git a/icons/ui-icons/uv.bmp b/fonts/ui-icons/uv.bmp similarity index 100% rename from icons/ui-icons/uv.bmp rename to fonts/ui-icons/uv.bmp diff --git a/icons/ui-icons/wind.bmp b/fonts/ui-icons/wind.bmp similarity index 100% rename from icons/ui-icons/wind.bmp rename to fonts/ui-icons/wind.bmp diff --git a/inkycal/__init__.py b/inkycal/__init__.py index 6c721f5..edb3926 100644 --- a/inkycal/__init__.py +++ b/inkycal/__init__.py @@ -1,20 +1,15 @@ -# Display class (for driving E-Paper displays) -from inkycal.display import Display - # Default modules import inkycal.modules.inkycal_agenda import inkycal.modules.inkycal_calendar -import inkycal.modules.inkycal_weather import inkycal.modules.inkycal_feeds -import inkycal.modules.inkycal_todoist +import inkycal.modules.inkycal_fullweather import inkycal.modules.inkycal_image import inkycal.modules.inkycal_jokes import inkycal.modules.inkycal_slideshow import inkycal.modules.inkycal_stocks +import inkycal.modules.inkycal_todoist +import inkycal.modules.inkycal_weather import inkycal.modules.inkycal_webshot import inkycal.modules.inkycal_xkcd -import inkycal.modules.inkycal_fullweather -import inkycal.modules.inkycal_mawaqit - -# Main file +from inkycal.display import Display from inkycal.main import Inkycal diff --git a/inkycal/custom/functions.py b/inkycal/custom/functions.py index 327095c..206a2ec 100644 --- a/inkycal/custom/functions.py +++ b/inkycal/custom/functions.py @@ -17,20 +17,16 @@ from PIL import Image from PIL import ImageDraw from PIL import ImageFont -logs = logging.getLogger(__name__) -logs.setLevel(level=logging.INFO) +from inkycal.settings import Settings -# Get the path to the Inkycal folder -top_level = "/".join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))).split("/")[:-1]) +logger = logging.getLogger(__name__) -# Get path of 'fonts' and 'images' folders within Inkycal folder -fonts_location = os.path.join(top_level, "fonts/") -image_folder = os.path.join(top_level, "image_folder/") +settings = Settings() # Get available fonts within fonts folder fonts = {} -for path, dirs, files in os.walk(fonts_location): +for path, dirs, files in os.walk(settings.FONT_PATH): for _ in files: if _.endswith(".otf"): name = _.split(".otf")[0] @@ -39,7 +35,7 @@ for path, dirs, files in os.walk(fonts_location): if _.endswith(".ttf"): name = _.split(".ttf")[0] fonts[name] = os.path.join(path, _) -logs.debug(f"Found fonts: {json.dumps(fonts, indent=4, sort_keys=True)}") +logger.debug(f"Found fonts: {json.dumps(fonts, indent=4, sort_keys=True)}") available_fonts = [key for key, values in fonts.items()] @@ -81,12 +77,12 @@ def get_system_tz() -> str: """ try: local_tz = tzlocal.get_localzone().key - logs.debug(f"Local system timezone is {local_tz}.") + logger.debug(f"Local system timezone is {local_tz}.") except: - logs.error("System timezone could not be parsed!") - logs.error("Please set timezone manually!. Falling back to UTC...") + logger.error("System timezone could not be parsed!") + logger.error("Please set timezone manually!. Falling back to UTC...") local_tz = "UTC" - logs.debug(f"The time is {arrow.now(tz=local_tz).format('YYYY-MM-DD HH:mm:ss ZZ')}.") + logger.debug(f"The time is {arrow.now(tz=local_tz).format('YYYY-MM-DD HH:mm:ss ZZ')}.") return local_tz @@ -182,14 +178,14 @@ def write(image, xy, box_size, text, font=None, **kwargs): # Truncate text if text is too long, so it can fit inside the box if (text_width, text_height) > (box_width, box_height): - logs.debug(("truncating {}".format(text))) + logger.debug(("truncating {}".format(text))) while (text_width, text_height) > (box_width, box_height): text = text[0:-1] text_bbox = font.getbbox(text) text_width = text_bbox[2] - text_bbox[0] text_bbox_height = font.getbbox("hg") text_height = text_bbox_height[3] - text_bbox_height[1] - logs.debug(text) + logger.debug(text) # Align text to desired position if alignment == "center" or None: diff --git a/inkycal/display/drivers/10_in_3.py b/inkycal/display/drivers/10_in_3.py index 834e2df..7d7b3fa 100644 --- a/inkycal/display/drivers/10_in_3.py +++ b/inkycal/display/drivers/10_in_3.py @@ -2,22 +2,18 @@ 10.3" driver class Copyright by aceinnolab """ +import os from subprocess import run from PIL import Image -from inkycal.custom import image_folder, top_level +from inkycal.settings import Settings # Display resolution EPD_WIDTH = 1872 EPD_HEIGHT = 1404 -# Please insert VCOM of your display. The Minus sign before is not required -VCOM = "2.0" - -driver_dir = top_level + '/inkycal/display/drivers/parallel_drivers/' - -command = f'sudo {driver_dir}epd -{VCOM} 0 {image_folder + "canvas.bmp"}' +settings = Settings() class EPD: @@ -40,8 +36,8 @@ class EPD: def getbuffer(self, image): """ad-hoc""" image = image.rotate(90, expand=True).transpose(Image.FLIP_LEFT_RIGHT) - image.convert('RGB').save(image_folder + 'canvas.bmp', 'BMP') - command = f'sudo {driver_dir}epd -{VCOM} 0 {image_folder + "canvas.bmp"}' + image.convert('RGB').save(os.path.join(settings.IMAGE_FOLDER, 'canvas.bmp'), 'BMP') + command = f'sudo {settings.PARALLEL_DRIVER_PATH}/epd -{settings.VCOM} 0 {settings.IMAGE_FOLDER + "canvas.bmp"}' print(command) return command diff --git a/inkycal/display/drivers/7_in_8.py b/inkycal/display/drivers/7_in_8.py index fe4d243..5f9cf51 100644 --- a/inkycal/display/drivers/7_in_8.py +++ b/inkycal/display/drivers/7_in_8.py @@ -2,20 +2,16 @@ 7.8" parallel driver class Copyright by aceinnolab """ +import os from subprocess import run -from inkycal.custom import image_folder, top_level +from inkycal.settings import Settings # Display resolution EPD_WIDTH = 1872 EPD_HEIGHT = 1404 -# Please insert VCOM of your display. The Minus sign before is not required -VCOM = "2.0" - -driver_dir = top_level + '/inkycal/display/drivers/parallel_drivers/' - -command = f'sudo {driver_dir}epd -{VCOM} 0 {image_folder + "canvas.bmp"}' +settings = Settings() class EPD: @@ -38,8 +34,8 @@ class EPD: def getbuffer(self, image): """ad-hoc""" image = image.rotate(90, expand=True) - image.convert('RGB').save(image_folder + 'canvas.bmp', 'BMP') - command = f'sudo {driver_dir}epd -{VCOM} 0 {image_folder + "canvas.bmp"}' + image.convert('RGB').save(os.path.join(settings.IMAGE_FOLDER, 'canvas.bmp'), 'BMP') + command = f'sudo {settings.PARALLEL_DRIVER_PATH}/epd -{settings.VCOM} 0 {settings.IMAGE_FOLDER + "canvas.bmp"}' print(command) return command diff --git a/inkycal/display/drivers/9_in_7.py b/inkycal/display/drivers/9_in_7.py index 8c6ec0f..c81feef 100644 --- a/inkycal/display/drivers/9_in_7.py +++ b/inkycal/display/drivers/9_in_7.py @@ -4,18 +4,16 @@ Copyright by aceinnolab """ from subprocess import run -from inkycal.custom import image_folder, top_level +from inkycal.settings import Settings # Display resolution EPD_WIDTH = 1200 EPD_HEIGHT = 825 -# Please insert VCOM of your display. The Minus sign before is not required -VCOM = "2.0" -driver_dir = top_level + '/inkycal/display/drivers/parallel_drivers/' +settings = Settings() -command = f'sudo {driver_dir}epd -{VCOM} 0 {image_folder + "canvas.bmp"}' +command = f'sudo {settings.PARALLEL_DRIVER_PATH}/epd -{settings.VCOM} 0 {settings.IMAGE_FOLDER + "canvas.bmp"}' class EPD: @@ -38,8 +36,8 @@ class EPD: def getbuffer(self, image): """ad-hoc""" image = image.rotate(90, expand=True) - image.convert('RGB').save(image_folder + 'canvas.bmp', 'BMP') - command = f'sudo {driver_dir}epd -{VCOM} 0 {image_folder + "canvas.bmp"}' + image.convert('RGB').save(settings.IMAGE_FOLDER + 'canvas.bmp', 'BMP') + command = f'sudo {settings.PARALLEL_DRIVER_PATH}/epd -{settings.VCOM} 0 {settings.IMAGE_FOLDER + "canvas.bmp"}' print(command) return command diff --git a/inkycal/loggers.py b/inkycal/loggers.py new file mode 100644 index 0000000..97c1c46 --- /dev/null +++ b/inkycal/loggers.py @@ -0,0 +1,35 @@ +"""Logging configuration for Inkycal.""" +import logging +import os +from logging.handlers import RotatingFileHandler + +from inkycal.settings import Settings + +# On the console, set a logger to show only important logs +# (level ERROR or higher) +stream_handler = logging.StreamHandler() +stream_handler.setLevel(logging.ERROR) + +settings = Settings() + +if not os.path.exists(settings.LOG_PATH): + os.mkdir(settings.LOG_PATH) + + +# Save all logs to a file, which contains more detailed output +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s | %(name)s | %(levelname)s: %(message)s', + datefmt='%d-%m-%Y %H:%M:%S', + handlers=[ + stream_handler, # add stream handler from above + RotatingFileHandler( # log to a file too + settings.INKYCAL_LOG_PATH, # file to log + maxBytes=2*1024*1024, # 2MB max filesize + backupCount=5 # create max 5 log files + ) + ] +) + +# Show less logging for PIL module +logging.getLogger("PIL").setLevel(logging.WARNING) \ No newline at end of file diff --git a/inkycal/main.py b/inkycal/main.py index cc4473b..ac3dd88 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -6,44 +6,21 @@ Copyright by aceinnolab import asyncio import glob import hashlib -from logging.handlers import RotatingFileHandler import numpy +from inkycal import loggers # noqa from inkycal.custom import * from inkycal.display import Display from inkycal.modules.inky_image import Inkyimage as Images - -# On the console, set a logger to show only important logs -# (level ERROR or higher) -stream_handler = logging.StreamHandler() -stream_handler.setLevel(logging.ERROR) - -if not os.path.exists(f'{top_level}/logs'): - os.mkdir(f'{top_level}/logs') - -# Save all logs to a file, which contains more detailed output -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s | %(name)s | %(levelname)s: %(message)s', - datefmt='%d-%m-%Y %H:%M:%S', - handlers=[ - stream_handler, # add stream handler from above - RotatingFileHandler( # log to a file too - f'{top_level}/logs/inkycal.log', # file to log - maxBytes=2097152, # 2MB max filesize - backupCount=5 # create max 5 log files - ) - ] -) - -# Show less logging for PIL module -logging.getLogger("PIL").setLevel(logging.WARNING) +from inkycal.utils.json_cache import JSONCache logger = logging.getLogger(__name__) +settings = Settings() + +CACHE_NAME = "inkycal_main" -# TODO: autostart -> supervisor? class Inkycal: """Inkycal main class @@ -62,13 +39,7 @@ class Inkycal: def __init__(self, settings_path: str or None = None, render: bool = True): """Initialise Inkycal""" - - # Get the release version from setup.py - with open(f'{top_level}/setup.py') as setup_file: - for line in setup_file: - if line.startswith('__version__'): - self._release = line.split("=")[-1].replace("'", "").replace('"', "").replace(" ", "") - break + self._release = "2.0.3" self.render = render self.info = None @@ -77,8 +48,7 @@ class Inkycal: if settings_path: try: with open(settings_path) as settings_file: - settings = json.load(settings_file) - self.settings = settings + self.settings = json.load(settings_file) except FileNotFoundError: raise FileNotFoundError( @@ -86,17 +56,19 @@ class Inkycal: else: try: - with open('/boot/settings.json') as settings_file: - settings = json.load(settings_file) - self.settings = settings + with open('/boot/settings.json', mode="r") as settings_file: + self.settings = json.load(settings_file) except FileNotFoundError: raise SettingsFileNotFoundError self.disable_calibration = self.settings.get('disable_calibration', False) - if not os.path.exists(image_folder): - os.mkdir(image_folder) + if not os.path.exists(settings.IMAGE_FOLDER): + os.mkdir(settings.IMAGE_FOLDER) + + if not os.path.exists(settings.CACHE_PATH): + os.mkdir(settings.CACHE_PATH) # Option to use epaper image optimisation, reduces colours self.optimize = True @@ -122,7 +94,7 @@ class Inkycal: # Load and initialise modules specified in the settings file self._module_number = 1 - for module in settings['modules']: + for module in self.settings['modules']: module_name = module['name'] try: loader = f'from inkycal.modules import {module_name}' @@ -131,10 +103,9 @@ class Inkycal: setup = f'self.module_{self._module_number} = {module_name}({module})' # print(setup) exec(setup) - logger.info(('name : {name} size : {width}x{height} px'.format( - name=module_name, - width=module['config']['size'][0], - height=module['config']['size'][1]))) + width = module['config']['size'][0] + height = module['config']['size'][1] + logger.info(f'name : {module_name} size : {width}x{height} px') self._module_number += 1 @@ -147,55 +118,56 @@ class Inkycal: logger.exception(f"Exception: {traceback.format_exc()}.") # Path to store images - self.image_folder = image_folder + self.image_folder = settings.IMAGE_FOLDER # Remove old hashes self._remove_hashes(self.image_folder) + # set up cache + if not os.path.exists(os.path.join(settings.CACHE_PATH, CACHE_NAME)): + if not os.path.exists(settings.CACHE_PATH): + os.mkdir(settings.CACHE_PATH) + self.cache = JSONCache(CACHE_NAME) + self.cache_data = self.cache.read() + # Give an OK message print('loaded inkycal') - def countdown(self, interval_mins: int or None = None) -> int: - """Returns the remaining time in seconds until next display update. + def countdown(self, interval_mins: int = None) -> int: + """Returns the remaining time in seconds until the next display update based on the interval. Args: - - interval_mins = int -> the interval in minutes for the update - if no interval is given, the value from the settings file is used. + interval_mins (int): The interval in minutes for the update. If none is given, the value + from the settings file is used. Returns: - - int -> the remaining time in seconds until next update + int: The remaining time in seconds until the next update. """ - - # Check if empty, if empty, use value from settings file + # Default to settings if no interval is provided if interval_mins is None: interval_mins = self.settings["update_interval"] - # Find out at which minutes the update should happen + # Get the current time now = arrow.now() - if interval_mins <= 60: - update_timings = [(60 - interval_mins * updates) for updates in range(60 // interval_mins)][::-1] - # Calculate time in minutes until next update - minutes = [_ for _ in update_timings if _ >= now.minute][0] - now.minute + # Calculate the next update time + # Finding the total minutes from the start of the day + minutes_since_midnight = now.hour * 60 + now.minute - # Print the remaining time in minutes until next update - print(f'{minutes} minutes left until next refresh') + # Finding the next interval point + minutes_to_next_interval = ( + minutes_since_midnight // interval_mins + 1) * interval_mins - minutes_since_midnight + seconds_to_next_interval = minutes_to_next_interval * 60 - now.second - # Calculate time in seconds until next update - remaining_time = minutes * 60 + (60 - now.second) - - # Return seconds until next update - return remaining_time + # Logging the remaining time in appropriate units + hours_to_next_interval = minutes_to_next_interval // 60 + remaining_minutes = minutes_to_next_interval % 60 + if hours_to_next_interval > 0: + print(f'{hours_to_next_interval} hours and {remaining_minutes} minutes left until next refresh') else: - # Calculate time in minutes until next update using the range of 24 hours in steps of every full hour - update_timings = [(60 * 24 - interval_mins * updates) for updates in range(60 * 24 // interval_mins)][::-1] - minutes = [_ for _ in update_timings if _ >= now.minute][0] - now.minute - remaining_time = minutes * 60 + (60 - now.second) + print(f'{remaining_minutes} minutes left until next refresh') - print(f'{round(minutes / 60, 1)} hours left until next refresh') - - # Return seconds until next update - return remaining_time + return seconds_to_next_interval def test(self): """Tests if Inkycal can run without issues. @@ -218,20 +190,13 @@ class Inkycal: for number in range(1, self._module_number): name = eval(f"self.module_{number}.name") - module = eval(f'self.module_{number}') print(f'generating image(s) for {name}...', end="") - try: - black, colour = module.generate_image() - if self.show_border: - draw_border_2(im=black, xy=(1, 1), size=(black.width - 2, black.height - 2), radius=5) - black.save(f"{self.image_folder}module{number}_black.png", "PNG") - colour.save(f"{self.image_folder}module{number}_colour.png", "PNG") + success = self.process_module(number) + if success: print("OK!") - except Exception: + else: errors.append(number) self.info += f"module {number}: Error! " - logger.exception("Error!") - logger.exception(f"Exception: {traceback.format_exc()}.") if errors: logger.error('Error/s in modules:', *errors) @@ -277,14 +242,17 @@ class Inkycal: print("Refresh needed: {a}".format(a=res)) return res - async def run(self): - """Runs main program in nonstop mode. + async def run(self, run_once=False): + """Runs main program in nonstop mode or a single iteration based on the run_once flag. - Uses an infinity loop to run Inkycal nonstop. Inkycal generates the image - from all modules, assembles them in one image, refreshed the E-Paper and - then sleeps until the next scheduled update. + Args: + run_once (bool): If True, runs the updating process once and stops. If False, + runs indefinitely. + + Uses an infinity loop to run Inkycal nonstop or a single time based on run_once. + Inkycal generates the image from all modules, assembles them in one image, + refreshes the E-Paper and then sleeps until the next scheduled update or exits. """ - # Get the time of initial run runtime = arrow.now() @@ -303,31 +271,19 @@ class Inkycal: f"Time: {current_time.format('HH:mm')}") print('Generating images for all modules...', end='') - errors = [] # store module numbers in here + errors = [] # Store module numbers in here - # short info for info-section + # Short info for info-section if not self.settings.get('image_hash', False): self.info = f"{current_time.format('D MMM @ HH:mm')} " else: self.info = "" for number in range(1, self._module_number): - - # name = eval(f"self.module_{number}.name") - module = eval(f'self.module_{number}') - - try: - black, colour = module.generate_image() - if self.show_border: - draw_border_2(im=black, xy=(1, 1), size=(black.width - 2, black.height - 2), radius=5) - black.save(f"{self.image_folder}module{number}_black.png", "PNG") - colour.save(f"{self.image_folder}module{number}_colour.png", "PNG") - self.info += f"module {number}: OK " - except Exception as e: + success = self.process_module(number) + if not success: errors.append(number) self.info += f"module {number}: Error! " - logger.exception("Error!") - logger.exception(f"Exception: {traceback.format_exc()}.") if errors: logger.error("Error/s in modules:", *errors) @@ -343,10 +299,9 @@ class Inkycal: # Check if image should be rendered if self.render: display = self.Display - self._calibration_check() if self._calibration_state: - # after calibration, we have to forcefully rewrite the screen + # After calibration, we have to forcefully rewrite the screen self._remove_hashes(self.image_folder) if self.supports_colour: @@ -358,17 +313,15 @@ class Inkycal: im_black = upside_down(im_black) im_colour = upside_down(im_colour) - # render the image on the display + # Render the image on the display if not self.settings.get('image_hash', False) or self._needs_image_update([ (f"{self.image_folder}/canvas.png.hash", im_black), (f"{self.image_folder}/canvas_colour.png.hash", im_colour) ]): - # render the image on the display display.render(im_black, im_colour) # Part for black-white ePapers elif not self.supports_colour: - im_black = self._merge_bands() # Flip the image by 180° if required @@ -376,13 +329,15 @@ class Inkycal: im_black = upside_down(im_black) if not self.settings.get('image_hash', False) or self._needs_image_update([ - (f"{self.image_folder}/canvas.png.hash", im_black), - ]): + (f"{self.image_folder}/canvas.png.hash", im_black),]): display.render(im_black) print(f'\nNo errors since {counter} display updates \n' f'program started {runtime.humanize()}') + if run_once: + break # Exit the loop after one full cycle if run_once is True + sleep_time = self.countdown() await asyncio.sleep(sleep_time) @@ -392,7 +347,8 @@ class Inkycal: returns the merged image """ - im1_path, im2_path = image_folder + 'canvas.png', image_folder + 'canvas_colour.png' + im1_path = os.path.join(settings.image_folder, "canvas.png") + im2_path = os.path.join(settings.image_folder, "canvas_colour.png") # If there is an image for black and colour, merge them if os.path.exists(im1_path) and os.path.exists(im2_path): @@ -531,7 +487,7 @@ class Inkycal: im_colour = black_to_colour(im_colour) im_colour.paste(im_black, (0, 0), im_black) - im_colour.save(image_folder + 'full-screen.png', 'PNG') + im_colour.save(os.path.join(settings.IMAGE_FOLDER, 'full-screen.png'), 'PNG') @staticmethod def _optimize_im(image, threshold=220): @@ -574,13 +530,29 @@ class Inkycal: @staticmethod def cleanup(): # clean up old images in image_folder - for _file in glob.glob(f"{image_folder}*.png"): + if len(glob.glob(settings.IMAGE_FOLDER)) <= 1: + return + for _file in glob.glob(settings.IMAGE_FOLDER): try: os.remove(_file) except: logger.error(f"could not remove file: {_file}") pass + def process_module(self, number) -> bool or Exception: + """Process individual module to generate images and handle exceptions.""" + module = eval(f'self.module_{number}') + try: + black, colour = module.generate_image() + if self.show_border: + draw_border_2(im=black, xy=(1, 1), size=(black.width - 2, black.height - 2), radius=5) + black.save(f"{self.image_folder}module{number}_black.png", "PNG") + colour.save(f"{self.image_folder}module{number}_colour.png", "PNG") + return True + except Exception: + logger.exception(f"Error in module {number}!") + return False + if __name__ == '__main__': print(f'running inkycal main in standalone/debug mode') diff --git a/inkycal/modules/inkycal_fullweather.py b/inkycal/modules/inkycal_fullweather.py index 55ce044..e48f17f 100644 --- a/inkycal/modules/inkycal_fullweather.py +++ b/inkycal/modules/inkycal_fullweather.py @@ -23,16 +23,18 @@ from icons.weather_icons.weather_icons import get_weather_icon from inkycal.custom.functions import fonts from inkycal.custom.functions import get_system_tz from inkycal.custom.functions import internet_available -from inkycal.custom.functions import top_level from inkycal.custom.inkycal_exceptions import NetworkNotReachableError from inkycal.custom.openweathermap_wrapper import OpenWeatherMap from inkycal.modules.inky_image import image_to_palette from inkycal.modules.template import inkycal_module +from inkycal.settings import Settings logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -icons_dir = os.path.join(top_level, "icons", "ui-icons") +settings = Settings() + +icons_dir = os.path.join(settings.FONT_PATH, "ui-icons") def outline(image: Image, size: int, color: tuple) -> Image: @@ -139,7 +141,7 @@ class Fullweather(inkycal_module): # Check if all required parameters are present for param in self.requires: - if not param in config: + if param not in config: raise Exception(f"config is missing {param}") # required parameters diff --git a/inkycal/modules/inkycal_slideshow.py b/inkycal/modules/inkycal_slideshow.py index c7481fa..9f098df 100755 --- a/inkycal/modules/inkycal_slideshow.py +++ b/inkycal/modules/inkycal_slideshow.py @@ -8,13 +8,13 @@ from inkycal.custom import * # PIL has a class named Image, use alias for Inkyimage -> Images from inkycal.modules.inky_image import Inkyimage as Images, image_to_palette from inkycal.modules.template import inkycal_module +from inkycal.utils.json_cache import JSONCache logger = logging.getLogger(__name__) class Slideshow(inkycal_module): - """Cycles through images in a local image folder - """ + """Cycles through images in a local image folder""" name = "Slideshow - cycle through images from a local folder" requires = { @@ -53,7 +53,7 @@ class Slideshow(inkycal_module): # required parameters for param in self.requires: - if not param in config: + if param not in config: raise Exception(f'config is missing {param}') # optional parameters @@ -64,14 +64,15 @@ class Slideshow(inkycal_module): # Get the full path of all png/jpg/jpeg images in the given folder all_files = glob.glob(f'{self.path}/*') - self.images = [i for i in all_files - if i.split('.')[-1].lower() in ('jpg', 'jpeg', 'png')] + self.images = [i for i in all_files if i.split('.')[-1].lower() in ('jpg', 'jpeg', 'png')] if not self.images: - logger.error('No images found in the given folder, please ' - 'double check your path!') + logger.error('No images found in the given folder, please double check your path!') raise Exception('No images found in the given folder path :/') + self.cache = JSONCache('inkycal_slideshow') + self.cache_data = self.cache.read() + # set a 'first run' signal self._first_run = True @@ -89,14 +90,16 @@ class Slideshow(inkycal_module): logger.info(f'Image size: {im_size}') # rotates list items by 1 index - def rotate(somelist): - return somelist[1:] + somelist[:1] + def rotate(list: list): + return list[1:] + list[:1] # Switch to the next image if this is not the first run if self._first_run: self._first_run = False + self.cache_data["current_index"] = 0 else: self.images = rotate(self.images) + self.cache_data["current_index"] = (self.cache_data["current_index"] + 1) % len(self.images) # initialize custom image class im = Images() @@ -110,7 +113,7 @@ class Slideshow(inkycal_module): # Remove background if present im.remove_alpha() - # if autoflip was enabled, flip the image + # if auto-flip was enabled, flip the image if self.autoflip: im.autoflip(self.orientation) @@ -123,6 +126,8 @@ class Slideshow(inkycal_module): # with the images now send, clear the current image im.clear() + self.cache.write(self.cache_data) + # return images return im_black, im_colour diff --git a/inkycal/modules/inkycal_xkcd.py b/inkycal/modules/inkycal_xkcd.py index b2ce25e..63f7140 100644 --- a/inkycal/modules/inkycal_xkcd.py +++ b/inkycal/modules/inkycal_xkcd.py @@ -11,6 +11,8 @@ from inkycal.modules.template import inkycal_module logger = logging.getLogger(__name__) +settings = Settings() + class Xkcd(inkycal_module): name = "xkcd - Displays comics from xkcd.com by Randall Munroe" @@ -57,7 +59,7 @@ class Xkcd(inkycal_module): """Generate image for this module""" # Create tmp path - tmpPath = f"{top_level}/temp" + tmpPath = settings.TEMPORARY_FOLDER if not os.path.exists(tmpPath): os.mkdir(tmpPath) diff --git a/inkycal/settings.py b/inkycal/settings.py new file mode 100644 index 0000000..8f40352 --- /dev/null +++ b/inkycal/settings.py @@ -0,0 +1,20 @@ +"""Settings class +Used to initialize the settings for the application. +""" +import os + +basedir = os.path.abspath(os.path.dirname(__file__)) + + +class Settings: + """Settings class to initialize the settings for the application. + + """ + CACHE_PATH = os.path.join(basedir, "cache") + LOG_PATH = os.path.join(basedir, "logs") + INKYCAL_LOG_PATH = os.path.join(LOG_PATH, "inkycal.log") + FONT_PATH = os.path.join(basedir, "../fonts") + IMAGE_FOLDER = os.path.join(basedir, "../image_folder") + PARALLEL_DRIVER_PATH = os.path.join(basedir, "inkycal", "display", "drivers", "parallel_drivers") + TEMPORARY_FOLDER = os.path.join(basedir, "tmp") + VCOM = "2.0" diff --git a/inkycal/utils/json_cache.py b/inkycal/utils/json_cache.py new file mode 100644 index 0000000..76e8e19 --- /dev/null +++ b/inkycal/utils/json_cache.py @@ -0,0 +1,28 @@ +"""JSON Cache +Can be used to cache JSON data to disk. This is useful for caching data to survive reboots. +""" +import json +import os + +from inkycal.settings import Settings + +settings = Settings() + + +class JSONCache: + def __init__(self, name: str, create_if_not_exists: bool = True): + self.path = os.path.join(settings.CACHE_PATH,f"{name}.json") + if create_if_not_exists and not os.path.exists(self.path): + with open(self.path, "w", encoding="utf-8") as file: + json.dump({}, file) + + def read(self): + try: + with open(self.path, "r", encoding="utf-8") as file: + return json.load(file) + except FileNotFoundError: + return {} + + def write(self, data: dict): + with open(self.path, "w", encoding="utf-8") as file: + json.dump(data, file, indent=4, sort_keys=True) From 309c6ca5bc09776e7bd6d9858ad1969ec151ebe2 Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 23 Jun 2024 20:13:58 +0200 Subject: [PATCH 03/23] test pisugar support --- inky_run.py | 7 ++- inkycal/main.py | 2 +- inkycal/modules/inkycal_slideshow.py | 2 +- inkycal/utils/__init__.py | 2 + inkycal/utils/pisugar.py | 78 ++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 inkycal/utils/__init__.py create mode 100644 inkycal/utils/pisugar.py diff --git a/inky_run.py b/inky_run.py index e2fff6a..2fb9718 100644 --- a/inky_run.py +++ b/inky_run.py @@ -1,7 +1,12 @@ +"""Basic Inkycal run script. + +Assumes that the settings.json file is in the /boot directory. +set render=True to render the display, set render=False to only run the modules. +""" import asyncio from inkycal import Inkycal inky = Inkycal(render=True) # Initialise Inkycal # If your settings.json file is not in /boot, use the full path: inky = Inkycal('path/to/settings.json', render=True) -inky.test() # test if Inkycal can be run correctly, running this will show a bit of info for each module +inky.run(run_once=True) # test if Inkycal can be run correctly, running this will show a bit of info for each module asyncio.run(inky.run()) # If there were no issues, you can run Inkycal nonstop diff --git a/inkycal/main.py b/inkycal/main.py index ac3dd88..c1dd0f1 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -13,7 +13,7 @@ from inkycal import loggers # noqa from inkycal.custom import * from inkycal.display import Display from inkycal.modules.inky_image import Inkyimage as Images -from inkycal.utils.json_cache import JSONCache +from inkycal.utils import JSONCache logger = logging.getLogger(__name__) diff --git a/inkycal/modules/inkycal_slideshow.py b/inkycal/modules/inkycal_slideshow.py index 9f098df..253397d 100755 --- a/inkycal/modules/inkycal_slideshow.py +++ b/inkycal/modules/inkycal_slideshow.py @@ -8,7 +8,7 @@ from inkycal.custom import * # PIL has a class named Image, use alias for Inkyimage -> Images from inkycal.modules.inky_image import Inkyimage as Images, image_to_palette from inkycal.modules.template import inkycal_module -from inkycal.utils.json_cache import JSONCache +from inkycal.utils import JSONCache logger = logging.getLogger(__name__) diff --git a/inkycal/utils/__init__.py b/inkycal/utils/__init__.py new file mode 100644 index 0000000..e0a85ab --- /dev/null +++ b/inkycal/utils/__init__.py @@ -0,0 +1,2 @@ +from .pisugar import PiSugar +from .json_cache import JSONCache \ No newline at end of file diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py new file mode 100644 index 0000000..7f00cee --- /dev/null +++ b/inkycal/utils/pisugar.py @@ -0,0 +1,78 @@ +"""PiSugar helper class for Inkycal.""" + +import logging +import subprocess + +from inkycal.settings import Settings + +settings = Settings() + +logger = logging.getLogger(__name__) + + +class PiSugar: + + def __init__(self): + # replace "command" with actual command + self.command_template = 'echo "command" | nc -q 0 127.0.0.1 8423' + self.allowed_commands = ["get battery", "get model", "get rtc_time", "get rtc_alarm_enabled", + "get rtc_alarm_time", "get alarm_repeat", "rtc_pi2rtc"] + + def _get_output(self, command): + if command not in self.allowed_commands: + logger.error(f"Command {command} not allowed") + return None + cmd = self.command_template.replace("command", command) + try: + result = subprocess.run(cmd, text=True, capture_output=True) + if result.returncode != 0: + print(f"Command failed with {result.stderr}") + return None + return result + except Exception as e: + logger.error(f"Error executing command: {e}") + return None + + def get_battery(self) -> int or None: + """Get the battery level in percentage. + + Returns: + int or None: The battery level in percentage or None if the command fails. + """ + battery_output = self._get_output("get battery") + if battery_output: + for line in battery_output.splitlines(): + if 'battery:' in line: + return int(line.split(':')[1].strip()) + return None + + def get_model(self): + """Get the PiSugar model.""" + model_output = self._get_output("get model") + if model_output: + for line in model_output.splitlines(): + if 'model:' in line: + return line.split(':')[1].strip() + return None + + def get_rtc_time(self): + """Get the RTC time.""" + return self._get_output("get rtc_time") + + def get_rtc_alarm_enabled(self): + """Get the RTC alarm enabled status.""" + return self._get_output("get rtc_alarm_enabled") + + def get_rtc_alarm_time(self): + """Get the RTC alarm time.""" + return self._get_output("get rtc_alarm_time") + + def get_alarm_repeat(self): + """Get the alarm repeat status.""" + return self._get_output("get alarm_repeat") + + def rtc_pi2rtc(self): + """Sync the Pi time to RTC.""" + return self._get_output("rtc_pi2rtc") + + From 13e03331f807cdbe2dec5ca1edee99b866a9c2e7 Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 23 Jun 2024 21:08:01 +0200 Subject: [PATCH 04/23] fix some minor bugs --- inkycal/utils/pisugar.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index 7f00cee..5c4bf8c 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -24,11 +24,12 @@ class PiSugar: return None cmd = self.command_template.replace("command", command) try: - result = subprocess.run(cmd, text=True, capture_output=True) + result = subprocess.run(cmd, shell=True, text=True, capture_output=True) if result.returncode != 0: print(f"Command failed with {result.stderr}") return None - return result + output = result.stdout.strip() + return output except Exception as e: logger.error(f"Error executing command: {e}") return None From 0ecca08484c5aa621688f1e0d83e03560ae8876d Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 23 Jun 2024 21:24:59 +0200 Subject: [PATCH 05/23] improve pisugar class --- inkycal/utils/pisugar.py | 73 +++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index 5c4bf8c..43fc4e3 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -47,7 +47,7 @@ class PiSugar: return int(line.split(':')[1].strip()) return None - def get_model(self): + def get_model(self) -> str or None: """Get the PiSugar model.""" model_output = self._get_output("get model") if model_output: @@ -56,24 +56,71 @@ class PiSugar: return line.split(':')[1].strip() return None - def get_rtc_time(self): + def get_rtc_time(self) -> str or None: """Get the RTC time.""" - return self._get_output("get rtc_time") + result = self._get_output("get rtc_time") + if result: + second_line = result.splitlines()[1] + return second_line.split('rtc_alarm_time: ')[1].strip() + return None - def get_rtc_alarm_enabled(self): + def get_rtc_alarm_enabled(self) -> str or None: """Get the RTC alarm enabled status.""" - return self._get_output("get rtc_alarm_enabled") + result = self._get_output("get rtc_alarm_enabled") + if result: + second_line = result.splitlines()[1] + return second_line.split('rtc_alarm_enabled: ')[1].strip() + return None - def get_rtc_alarm_time(self): + def get_rtc_alarm_time(self) -> str or None: """Get the RTC alarm time.""" - return self._get_output("get rtc_alarm_time") + result = self._get_output("get rtc_alarm_time") + if result: + second_line = result.splitlines()[1] + return second_line.split('rtc_alarm_time: ')[1].strip() + return None - def get_alarm_repeat(self): - """Get the alarm repeat status.""" - return self._get_output("get alarm_repeat") + def get_alarm_repeat(self) -> dict or None: + """Get the alarm repeat status. - def rtc_pi2rtc(self): - """Sync the Pi time to RTC.""" - return self._get_output("rtc_pi2rtc") + Returns: + dict or None: A dictionary with the alarm repeating days or None if the command fails. + """ + result = self._get_output("get alarm_repeat") + if result: + second_line = result.splitlines()[1] + repeating_days = second_line.split('alarm_repeat: ')[1].strip() + data = {"Monday": False, "Tuesday": False, "Wednesday": False, "Thursday": False, "Friday": False, + "Saturday": False, "Sunday": False} + if repeating_days[0] == "1": + data["Monday"] = True + if repeating_days[1] == "1": + data["Tuesday"] = True + if repeating_days[2] == "1": + data["Wednesday"] = True + if repeating_days[3] == "1": + data["Thursday"] = True + if repeating_days[4] == "1": + data["Friday"] = True + if repeating_days[5] == "1": + data["Saturday"] = True + if repeating_days[6] == "1": + data["Sunday"] = True + return data + return None + + def rtc_pi2rtc(self) -> bool: + """Sync the Pi time to RTC. + + Returns: + bool: True if the sync was successful, False otherwise. + """ + result = self._get_output("rtc_pi2rtc") + if result: + second_line = result.splitlines()[1] + status = second_line.split('rtc_pi2rtc: ')[1].strip() + if status == "done": + return True + return False From 87dbc0691b2c772d99403855bcadd21be664e41d Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 23 Jun 2024 21:28:40 +0200 Subject: [PATCH 06/23] fix minor issue --- inkycal/utils/pisugar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index 43fc4e3..e794f38 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -89,7 +89,7 @@ class PiSugar: result = self._get_output("get alarm_repeat") if result: second_line = result.splitlines()[1] - repeating_days = second_line.split('alarm_repeat: ')[1].strip() + repeating_days = f"{second_line.split('alarm_repeat: ')[1].strip():8b}".strip() data = {"Monday": False, "Tuesday": False, "Wednesday": False, "Thursday": False, "Friday": False, "Saturday": False, "Sunday": False} if repeating_days[0] == "1": From 4112df0771894c37e8d45a91307e7850fd491d1d Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 23 Jun 2024 21:30:30 +0200 Subject: [PATCH 07/23] fix minor issue --- inkycal/utils/pisugar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index e794f38..ab98abf 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -89,7 +89,7 @@ class PiSugar: result = self._get_output("get alarm_repeat") if result: second_line = result.splitlines()[1] - repeating_days = f"{second_line.split('alarm_repeat: ')[1].strip():8b}".strip() + repeating_days = f"{int(second_line.split('alarm_repeat: ')[1].strip()):8b}".strip() data = {"Monday": False, "Tuesday": False, "Wednesday": False, "Thursday": False, "Friday": False, "Saturday": False, "Sunday": False} if repeating_days[0] == "1": From 862886be24c238c6aff974e4ed4763d07bae197d Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 23 Jun 2024 21:35:38 +0200 Subject: [PATCH 08/23] fix small bug --- inkycal/utils/pisugar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index ab98abf..b0f0a82 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -61,7 +61,7 @@ class PiSugar: result = self._get_output("get rtc_time") if result: second_line = result.splitlines()[1] - return second_line.split('rtc_alarm_time: ')[1].strip() + return second_line.split('rtc_time: ')[1].strip() return None def get_rtc_alarm_enabled(self) -> str or None: From 8681e81e0016b22936f5351624917a6717b9916d Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 23 Jun 2024 21:38:25 +0200 Subject: [PATCH 09/23] fix small bug --- inkycal/utils/pisugar.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index b0f0a82..a1b1463 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -69,7 +69,8 @@ class PiSugar: result = self._get_output("get rtc_alarm_enabled") if result: second_line = result.splitlines()[1] - return second_line.split('rtc_alarm_enabled: ')[1].strip() + output = second_line.split('rtc_alarm_enabled: ')[1].strip() + return True if output == "true" else False return None def get_rtc_alarm_time(self) -> str or None: From 17c13e6d8e4030847666411c87312a19cf9bb7f1 Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 23 Jun 2024 22:25:11 +0200 Subject: [PATCH 10/23] fix issue with yfinance --- inkycal/display/display.py | 2 +- inkycal/main.py | 14 +++++++++----- inkycal/modules/inkycal_stocks.py | 2 +- requirements.txt | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/inkycal/display/display.py b/inkycal/display/display.py index 179f71f..4eb47f3 100644 --- a/inkycal/display/display.py +++ b/inkycal/display/display.py @@ -197,7 +197,7 @@ class Display: >>> Display.get_display_names() """ - return supported_models.keys() + return list(supported_models.keys()) if __name__ == '__main__': diff --git a/inkycal/main.py b/inkycal/main.py index c1dd0f1..150e865 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -130,6 +130,8 @@ class Inkycal: self.cache = JSONCache(CACHE_NAME) self.cache_data = self.cache.read() + self.counter = 0 if "counter" not in self.cache_data else int(self.cache_data["counter"]) + # Give an OK message print('loaded inkycal') @@ -259,8 +261,6 @@ class Inkycal: # Function to flip images upside down upside_down = lambda image: image.rotate(180, expand=True) - # Count the number of times without any errors - counter = 0 print(f'Inkycal version: v{self._release}') print(f'Selected E-paper display: {self.settings["model"]}') @@ -287,9 +287,9 @@ class Inkycal: if errors: logger.error("Error/s in modules:", *errors) - counter = 0 + self.counter = 0 else: - counter += 1 + self.counter += 1 logger.info("successful") del errors @@ -332,9 +332,13 @@ class Inkycal: (f"{self.image_folder}/canvas.png.hash", im_black),]): display.render(im_black) - print(f'\nNo errors since {counter} display updates \n' + print(f'\nNo errors since {self.counter} display updates \n' f'program started {runtime.humanize()}') + # store the cache data + self.cache.write(self.cache_data) + + # Exit the loop if run_once is True if run_once: break # Exit the loop after one full cycle if run_once is True diff --git a/inkycal/modules/inkycal_stocks.py b/inkycal/modules/inkycal_stocks.py index cf58a41..47a2b55 100755 --- a/inkycal/modules/inkycal_stocks.py +++ b/inkycal/modules/inkycal_stocks.py @@ -142,7 +142,7 @@ class Stocks(inkycal_module): logger.warning(f"Failed to get '{stockName}' ticker price hint! Using " "default precision of 2 instead.") - stockHistory = yfTicker.history("30d") + stockHistory = yfTicker.history("1mo") stockHistoryLen = len(stockHistory) logger.info(f'fetched {stockHistoryLen} datapoints ...') previousQuote = (stockHistory.tail(2)['Close'].iloc[0]) diff --git a/requirements.txt b/requirements.txt index 3884b42..4554902 100644 --- a/requirements.txt +++ b/requirements.txt @@ -51,4 +51,4 @@ virtualenv==20.25.0 webencodings==0.5.1 x-wr-timezone==0.0.6 xkcd==2.4.2 -yfinance==0.2.36 +yfinance==0.2.40 From 310fefa1aead1e023c5b591eece0832245bd9d51 Mon Sep 17 00:00:00 2001 From: Ace Date: Mon, 24 Jun 2024 01:53:52 +0200 Subject: [PATCH 11/23] small improvements --- inkycal/main.py | 4 ++-- inkycal/utils/pisugar.py | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/inkycal/main.py b/inkycal/main.py index 150e865..b485ac4 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -81,10 +81,10 @@ class Inkycal: if self.render: # Init Display class with model in settings file # from inkycal.display import Display - self.Display = Display(settings["model"]) + self.Display = Display(self.settings["model"]) # check if colours can be rendered - self.supports_colour = True if 'colour' in settings['model'] else False + self.supports_colour = True if 'colour' in self.settings['model'] else False # get calibration hours self._calibration_hours = self.settings['calibration_hours'] diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index a1b1463..eb40da9 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -4,6 +4,7 @@ import logging import subprocess from inkycal.settings import Settings +import arrow settings = Settings() @@ -73,12 +74,13 @@ class PiSugar: return True if output == "true" else False return None - def get_rtc_alarm_time(self) -> str or None: + def get_rtc_alarm_time(self) -> arrow.arrow or None: """Get the RTC alarm time.""" result = self._get_output("get rtc_alarm_time") if result: second_line = result.splitlines()[1] - return second_line.split('rtc_alarm_time: ')[1].strip() + alarm_time = second_line.split('rtc_alarm_time: ')[1].strip() + return arrow.get(alarm_time) return None def get_alarm_repeat(self) -> dict or None: @@ -124,4 +126,22 @@ class PiSugar: return True return False + def rtc_alarm_set(self, time: arrow.arrow) -> bool: + """Set the RTC alarm time. + + Args: + time (arrow.arrow): The alarm time in ISO 8601 format. + + Returns: + bool: True if the alarm was set successfully, False otherwise. + """ + iso_format = time.isoformat() + result = self._get_output(f"rtc_alarm_set {iso_format}") + if result: + second_line = result.splitlines()[1] + status = second_line.split('rtc_alarm_set: ')[1].strip() + if status == "done": + return True + return False + From 36b5906534b4e7e209518814a9379933ee0061a2 Mon Sep 17 00:00:00 2001 From: Ace Date: Mon, 24 Jun 2024 02:16:08 +0200 Subject: [PATCH 12/23] this and that --- inkycal/utils/pisugar.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index eb40da9..851bebd 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -35,7 +35,7 @@ class PiSugar: logger.error(f"Error executing command: {e}") return None - def get_battery(self) -> int or None: + def get_battery(self) -> float or None: """Get the battery level in percentage. Returns: @@ -45,7 +45,7 @@ class PiSugar: if battery_output: for line in battery_output.splitlines(): if 'battery:' in line: - return int(line.split(':')[1].strip()) + return float(line.split(':')[1].strip()) return None def get_model(self) -> str or None: @@ -57,12 +57,12 @@ class PiSugar: return line.split(':')[1].strip() return None - def get_rtc_time(self) -> str or None: + def get_rtc_time(self) -> arrow.arrow or None: """Get the RTC time.""" result = self._get_output("get rtc_time") if result: - second_line = result.splitlines()[1] - return second_line.split('rtc_time: ')[1].strip() + rtc_time = result.split("rtc_time: ")[1].strip() + return arrow.get(rtc_time) return None def get_rtc_alarm_enabled(self) -> str or None: @@ -78,8 +78,7 @@ class PiSugar: """Get the RTC alarm time.""" result = self._get_output("get rtc_alarm_time") if result: - second_line = result.splitlines()[1] - alarm_time = second_line.split('rtc_alarm_time: ')[1].strip() + alarm_time = result.split('rtc_alarm_time: ')[1].strip() return arrow.get(alarm_time) return None From ca8990cd0172ff68f7b79be7902410c14a7c2ceb Mon Sep 17 00:00:00 2001 From: Ace Date: Mon, 24 Jun 2024 02:22:07 +0200 Subject: [PATCH 13/23] this and that --- inkycal/utils/pisugar.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index 851bebd..681a9c8 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -17,7 +17,7 @@ class PiSugar: # replace "command" with actual command self.command_template = 'echo "command" | nc -q 0 127.0.0.1 8423' self.allowed_commands = ["get battery", "get model", "get rtc_time", "get rtc_alarm_enabled", - "get rtc_alarm_time", "get alarm_repeat", "rtc_pi2rtc"] + "get rtc_alarm_time", "get alarm_repeat", "rtc_pi2rtc", "rtc_alarm_set"] def _get_output(self, command): if command not in self.allowed_commands: @@ -90,8 +90,7 @@ class PiSugar: """ result = self._get_output("get alarm_repeat") if result: - second_line = result.splitlines()[1] - repeating_days = f"{int(second_line.split('alarm_repeat: ')[1].strip()):8b}".strip() + repeating_days = f"{int(result.split('alarm_repeat: ')[1].strip()):8b}".strip() data = {"Monday": False, "Tuesday": False, "Wednesday": False, "Thursday": False, "Friday": False, "Saturday": False, "Sunday": False} if repeating_days[0] == "1": From b6edc1ff0f11ac294b2b68b6a024fe8fc48e76ae Mon Sep 17 00:00:00 2001 From: Ace Date: Mon, 24 Jun 2024 02:27:50 +0200 Subject: [PATCH 14/23] allow adding param --- inkycal/utils/pisugar.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index 681a9c8..5ac0746 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -19,11 +19,14 @@ class PiSugar: self.allowed_commands = ["get battery", "get model", "get rtc_time", "get rtc_alarm_enabled", "get rtc_alarm_time", "get alarm_repeat", "rtc_pi2rtc", "rtc_alarm_set"] - def _get_output(self, command): + def _get_output(self, command, param=None): if command not in self.allowed_commands: logger.error(f"Command {command} not allowed") return None - cmd = self.command_template.replace("command", command) + if param: + cmd = self.command_template.replace("command", f"{command} {param}") + else: + cmd = self.command_template.replace("command", command) try: result = subprocess.run(cmd, shell=True, text=True, capture_output=True) if result.returncode != 0: @@ -134,7 +137,7 @@ class PiSugar: bool: True if the alarm was set successfully, False otherwise. """ iso_format = time.isoformat() - result = self._get_output(f"rtc_alarm_set {iso_format}") + result = self._get_output("rtc_alarm_set", iso_format) if result: second_line = result.splitlines()[1] status = second_line.split('rtc_alarm_set: ')[1].strip() From 617484bd807914d5bfcc789e10be611401f32e4a Mon Sep 17 00:00:00 2001 From: Ace Date: Mon, 24 Jun 2024 02:41:52 +0200 Subject: [PATCH 15/23] fix minor bug --- inkycal/utils/pisugar.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index 5ac0746..5062643 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -127,17 +127,18 @@ class PiSugar: return True return False - def rtc_alarm_set(self, time: arrow.arrow) -> bool: + def rtc_alarm_set(self, time: arrow.arrow, repeat:int=127) -> bool: """Set the RTC alarm time. Args: time (arrow.arrow): The alarm time in ISO 8601 format. + repeat: int representing 7-bit binary number of repeating days. e.g. 127 = 1111111 = repeat every day Returns: bool: True if the alarm was set successfully, False otherwise. """ iso_format = time.isoformat() - result = self._get_output("rtc_alarm_set", iso_format) + result = self._get_output("rtc_alarm_set", f"{iso_format } {repeat}") if result: second_line = result.splitlines()[1] status = second_line.split('rtc_alarm_set: ')[1].strip() From bbe9498d9d9be53822c4dbf5d02478de3cd3c198 Mon Sep 17 00:00:00 2001 From: Ace Date: Mon, 24 Jun 2024 02:47:21 +0200 Subject: [PATCH 16/23] fix output of rtc_alarm_set --- inkycal/utils/pisugar.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index 5062643..48cdf8d 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -140,8 +140,7 @@ class PiSugar: iso_format = time.isoformat() result = self._get_output("rtc_alarm_set", f"{iso_format } {repeat}") if result: - second_line = result.splitlines()[1] - status = second_line.split('rtc_alarm_set: ')[1].strip() + status = result.split('rtc_alarm_set: ')[1].strip() if status == "done": return True return False From 98e3efa14bac87eb6ace694acff630cca747b0fd Mon Sep 17 00:00:00 2001 From: Ace Date: Mon, 24 Jun 2024 02:59:48 +0200 Subject: [PATCH 17/23] ad-hoc implementation for pisugar in main class --- inkycal/main.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/inkycal/main.py b/inkycal/main.py index b485ac4..e289d5e 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -132,6 +132,14 @@ class Inkycal: self.counter = 0 if "counter" not in self.cache_data else int(self.cache_data["counter"]) + self.use_pi_sugar = True + print("Please remove hardcoded value of pisugar!") + + if self.use_pi_sugar: + from inkycal.utils import PiSugar + self.pisugar = PiSugar() + print(f"Using PiSigar model: {self.pisugar.get_model()}. Current PiSugar time: {self.pisugar.get_rtc_time()}") + # Give an OK message print('loaded inkycal') @@ -343,6 +351,16 @@ class Inkycal: break # Exit the loop after one full cycle if run_once is True sleep_time = self.countdown() + + if self.use_pi_sugar: + # todo make this timezone aware! + sleep_time = arrow.now(tz=get_system_tz()).shift(seconds=sleep_time) + result = self.pisugar.rtc_alarm_set(sleep_time, 127) + if result: + print(f"Alarm set for {sleep_time.format('HH:mm:ss')}") + else: + print(f"Failed to set alarm for {sleep_time.format('HH:mm:ss')}") + await asyncio.sleep(sleep_time) @staticmethod From 92ef8476183ef676415a8823c4a04a7d728e12be Mon Sep 17 00:00:00 2001 From: Ace Date: Mon, 24 Jun 2024 03:21:40 +0200 Subject: [PATCH 18/23] fix minor issue --- inkycal/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inkycal/main.py b/inkycal/main.py index e289d5e..453a4a4 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -354,12 +354,12 @@ class Inkycal: if self.use_pi_sugar: # todo make this timezone aware! - sleep_time = arrow.now(tz=get_system_tz()).shift(seconds=sleep_time) - result = self.pisugar.rtc_alarm_set(sleep_time, 127) + sleep_time_rtc = arrow.now(tz=get_system_tz()).shift(seconds=sleep_time) + result = self.pisugar.rtc_alarm_set(sleep_time_rtc, 127) if result: - print(f"Alarm set for {sleep_time.format('HH:mm:ss')}") + print(f"Alarm set for {sleep_time_rtc.format('HH:mm:ss')}") else: - print(f"Failed to set alarm for {sleep_time.format('HH:mm:ss')}") + print(f"Failed to set alarm for {sleep_time_rtc.format('HH:mm:ss')}") await asyncio.sleep(sleep_time) From fdd5591456b922896bda1862640dc240ed0639c6 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 25 Jun 2024 13:17:00 +0200 Subject: [PATCH 19/23] add some nice-to-have features, e.g. battery capacity checking of PiSugar --- inkycal/main.py | 58 +++++++++++++++++++++++++++++++--------- inkycal/utils/pisugar.py | 3 +-- tests/test_main.py | 4 +-- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/inkycal/main.py b/inkycal/main.py index 453a4a4..64eecad 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -37,15 +37,19 @@ class Inkycal: to improve rendering on E-Papers. Set this to False for 9.7" E-Paper. """ - def __init__(self, settings_path: str or None = None, render: bool = True): + def __init__(self, settings_path: str or None = None, render: bool = True, use_pi_sugar: bool = False): """Initialise Inkycal""" self._release = "2.0.3" + logger.info(f"Inkycal v{self._release} booting up...") + self.render = render self.info = None + logger.info("Checking if a settings file is present...") # load settings file - throw an error if file could not be found if settings_path: + logger.info(f"Custom location for settings.json file specified: {settings_path}") try: with open(settings_path) as settings_file: self.settings = json.load(settings_file) @@ -55,6 +59,7 @@ class Inkycal: f"No settings.json file could be found in the specified location: {settings_path}") else: + logger.info("Looking for settings.json file in /boot folder...") try: with open('/boot/settings.json', mode="r") as settings_file: self.settings = json.load(settings_file) @@ -63,6 +68,8 @@ class Inkycal: raise SettingsFileNotFoundError self.disable_calibration = self.settings.get('disable_calibration', False) + if self.disable_calibration: + logger.info("Calibration disabled. Please proceed with caution to prevent ghosting.") if not os.path.exists(settings.IMAGE_FOLDER): os.mkdir(settings.IMAGE_FOLDER) @@ -132,16 +139,31 @@ class Inkycal: self.counter = 0 if "counter" not in self.cache_data else int(self.cache_data["counter"]) - self.use_pi_sugar = True - print("Please remove hardcoded value of pisugar!") + self.use_pi_sugar = use_pi_sugar + self.battery_capacity = 100 if self.use_pi_sugar: + logger.info("PiSugar support enabled.") from inkycal.utils import PiSugar self.pisugar = PiSugar() - print(f"Using PiSigar model: {self.pisugar.get_model()}. Current PiSugar time: {self.pisugar.get_rtc_time()}") + + self.battery_capacity = self.pisugar.get_battery() + logger.info(f"PiSugar battery capacity: {self.battery_capacity}%") + + if self.battery_capacity < 20: + logger.warning("Battery capacity is below 20%!") + + logger.info("Setting system time to PiSugar time...") + if self.pisugar.rtc_pi2rtc(): + logger.info("RTC time updates successfully") + else: + logger.warning("RTC time could not be set!") + + print( + f"Using PiSigar model: {self.pisugar.get_model()}. Current PiSugar time: {self.pisugar.get_rtc_time()}") # Give an OK message - print('loaded inkycal') + logger.info('Inkycal initialised successfully!') def countdown(self, interval_mins: int = None) -> int: """Returns the remaining time in seconds until the next display update based on the interval. @@ -179,7 +201,7 @@ class Inkycal: return seconds_to_next_interval - def test(self): + def dry_run(self): """Tests if Inkycal can run without issues. Attempts to import module names from settings file. Loads the config @@ -269,7 +291,6 @@ class Inkycal: # Function to flip images upside down upside_down = lambda image: image.rotate(180, expand=True) - print(f'Inkycal version: v{self._release}') print(f'Selected E-paper display: {self.settings["model"]}') @@ -291,7 +312,7 @@ class Inkycal: success = self.process_module(number) if not success: errors.append(number) - self.info += f"module {number}: Error! " + self.info += f"im {number}: X " if errors: logger.error("Error/s in modules:", *errors) @@ -301,6 +322,9 @@ class Inkycal: logger.info("successful") del errors + if self.battery_capacity < 20: + self.info += "Low battery! " + # Assemble image from each module - add info section if specified self._assemble() @@ -337,7 +361,7 @@ class Inkycal: im_black = upside_down(im_black) if not self.settings.get('image_hash', False) or self._needs_image_update([ - (f"{self.image_folder}/canvas.png.hash", im_black),]): + (f"{self.image_folder}/canvas.png.hash", im_black), ]): display.render(im_black) print(f'\nNo errors since {self.counter} display updates \n' @@ -353,13 +377,12 @@ class Inkycal: sleep_time = self.countdown() if self.use_pi_sugar: - # todo make this timezone aware! sleep_time_rtc = arrow.now(tz=get_system_tz()).shift(seconds=sleep_time) result = self.pisugar.rtc_alarm_set(sleep_time_rtc, 127) if result: - print(f"Alarm set for {sleep_time_rtc.format('HH:mm:ss')}") + logger.info(f"Alarm set for {sleep_time_rtc.format('HH:mm:ss')}") else: - print(f"Failed to set alarm for {sleep_time_rtc.format('HH:mm:ss')}") + logger.warning(f"Failed to set alarm for {sleep_time_rtc.format('HH:mm:ss')}") await asyncio.sleep(sleep_time) @@ -575,6 +598,17 @@ class Inkycal: logger.exception(f"Error in module {number}!") return False + def _shutdown_system(self): + """Shutdown the system""" + import subprocess + from time import sleep + try: + logger.info("Shutting down OS in 5 seconds...") + sleep(5) + subprocess.run(["sudo", "shutdown", "-h", "now"], check=True) + except subprocess.CalledProcessError: + logger.warning("Failed to execute shutdown command.") + if __name__ == '__main__': print(f'running inkycal main in standalone/debug mode') diff --git a/inkycal/utils/pisugar.py b/inkycal/utils/pisugar.py index 48cdf8d..0c89911 100644 --- a/inkycal/utils/pisugar.py +++ b/inkycal/utils/pisugar.py @@ -121,8 +121,7 @@ class PiSugar: """ result = self._get_output("rtc_pi2rtc") if result: - second_line = result.splitlines()[1] - status = second_line.split('rtc_pi2rtc: ')[1].strip() + status = result.split('rtc_pi2rtc: ')[1].strip() if status == "done": return True return False diff --git a/tests/test_main.py b/tests/test_main.py index ceb834c..e8c30af 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -21,9 +21,9 @@ class TestMain(unittest.TestCase): assert inkycal.settings["info_section_height"] == 70 assert inkycal.settings["border_around_modules"] is True - def test_run(self): + def test_dry_run(self): inkycal = Inkycal(self.settings_path, render=False) - inkycal.test() + inkycal.dry_run() def test_countdown(self): inkycal = Inkycal(self.settings_path, render=False) From 427c55ef63b9ed8822b043011d6098075fe3e853 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 25 Jun 2024 13:39:33 +0200 Subject: [PATCH 20/23] async and logging improvements, minor code cleanup --- clear_display.py | 12 ----------- inky_run.py | 33 +++++++++++++++++++++++++++---- inkycal/loggers.py | 2 +- inkycal/modules/inkycal_agenda.py | 4 +--- 4 files changed, 31 insertions(+), 20 deletions(-) delete mode 100644 clear_display.py diff --git a/clear_display.py b/clear_display.py deleted file mode 100644 index be726f3..0000000 --- a/clear_display.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -Clears the display of any content. -""" -from inkycal import Inkycal - -print("loading Inkycal and display driver...") -inky = Inkycal(render=True) # Initialise Inkycal -print("clearing display...") -inky.calibrate(cycles=1) # Calibrate the display -print("clear complete...") - -print("finished!") diff --git a/inky_run.py b/inky_run.py index 2fb9718..9f52494 100644 --- a/inky_run.py +++ b/inky_run.py @@ -4,9 +4,34 @@ Assumes that the settings.json file is in the /boot directory. set render=True to render the display, set render=False to only run the modules. """ import asyncio + from inkycal import Inkycal -inky = Inkycal(render=True) # Initialise Inkycal -# If your settings.json file is not in /boot, use the full path: inky = Inkycal('path/to/settings.json', render=True) -inky.run(run_once=True) # test if Inkycal can be run correctly, running this will show a bit of info for each module -asyncio.run(inky.run()) # If there were no issues, you can run Inkycal nonstop + +async def dry_run(): + # create an instance of Inkycal + # If your settings.json file is not in /boot, use the full path: + # inky = Inkycal('path/to/settings.json', render=True) + inky = Inkycal(render=False) + await inky.run(run_once=True) # dry-run without rendering anything on the display + + +async def clear_display(): + print("loading Inkycal and display driver...") + inky = Inkycal(render=True) # Initialise Inkycal + print("clearing display...") + inky.calibrate(cycles=1) # Calibrate the display + print("clear complete...") + print("finished!") + + +async def run(): + # create an instance of Inkycal + # If your settings.json file is not in /boot, use the full path: + # inky = Inkycal('path/to/settings.json', render=True) + inky = Inkycal(render=True) + await inky.run() # If there were no issues, you can run Inkycal nonstop + + +if __name__ == "__main__": + asyncio.run(run()) diff --git a/inkycal/loggers.py b/inkycal/loggers.py index 97c1c46..3a59c33 100644 --- a/inkycal/loggers.py +++ b/inkycal/loggers.py @@ -8,7 +8,7 @@ from inkycal.settings import Settings # On the console, set a logger to show only important logs # (level ERROR or higher) stream_handler = logging.StreamHandler() -stream_handler.setLevel(logging.ERROR) +stream_handler.setLevel(logging.INFO) settings = Settings() diff --git a/inkycal/modules/inkycal_agenda.py b/inkycal/modules/inkycal_agenda.py index 5b7f5d0..813a3db 100755 --- a/inkycal/modules/inkycal_agenda.py +++ b/inkycal/modules/inkycal_agenda.py @@ -2,9 +2,7 @@ Inkycal Agenda Module Copyright by aceinnolab """ - -import arrow - +import arrow # noqa from inkycal.custom import * from inkycal.modules.ical_parser import iCalendar from inkycal.modules.template import inkycal_module From 762538d4da427ae98371db8196b1de4fe46bf01b Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 25 Jun 2024 14:22:38 +0200 Subject: [PATCH 21/23] minor logging improvements --- inky_run.py | 3 +++ inkycal/main.py | 2 +- inkycal/modules/inky_image.py | 2 +- inkycal/modules/inkycal_agenda.py | 2 +- inkycal/modules/inkycal_feeds.py | 5 +++-- inkycal/modules/inkycal_image.py | 2 +- inkycal/modules/inkycal_jokes.py | 5 +++-- inkycal/modules/inkycal_slideshow.py | 2 +- inkycal/modules/inkycal_stocks.py | 2 +- inkycal/modules/inkycal_textfile_to_display.py | 2 +- inkycal/modules/inkycal_tindie.py | 3 ++- inkycal/modules/inkycal_todoist.py | 3 ++- inkycal/modules/inkycal_weather.py | 5 +++-- inkycal/modules/inkycal_webshot.py | 3 ++- inkycal/modules/inkycal_xkcd.py | 3 ++- 15 files changed, 27 insertions(+), 17 deletions(-) diff --git a/inky_run.py b/inky_run.py index 9f52494..9f4d5f7 100644 --- a/inky_run.py +++ b/inky_run.py @@ -29,6 +29,9 @@ async def run(): # create an instance of Inkycal # If your settings.json file is not in /boot, use the full path: # inky = Inkycal('path/to/settings.json', render=True) + + # when using experimental PiSugar support: + # inky = Inkycal(render=True, use_pi_sugar=True) inky = Inkycal(render=True) await inky.run() # If there were no issues, you can run Inkycal nonstop diff --git a/inkycal/main.py b/inkycal/main.py index 64eecad..8b4a26f 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -319,7 +319,7 @@ class Inkycal: self.counter = 0 else: self.counter += 1 - logger.info("successful") + logger.info("All images generated successfully!") del errors if self.battery_capacity < 20: diff --git a/inkycal/modules/inky_image.py b/inkycal/modules/inky_image.py index 67f4a14..eaefe28 100755 --- a/inkycal/modules/inky_image.py +++ b/inkycal/modules/inky_image.py @@ -59,7 +59,7 @@ class Inkyimage: logger.error("Invalid Image file provided", exc_info=True) raise Exception("Please check if the path points to an image file.") - logger.info(f"width: {image.width}, height: {image.height}") + logger.debug(f"width: {image.width}, height: {image.height}") image.convert(mode="RGBA") # convert to a more suitable format self.image = image diff --git a/inkycal/modules/inkycal_agenda.py b/inkycal/modules/inkycal_agenda.py index 813a3db..5b27d00 100755 --- a/inkycal/modules/inkycal_agenda.py +++ b/inkycal/modules/inkycal_agenda.py @@ -86,7 +86,7 @@ class Agenda(inkycal_module): im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'Image size: {im_size}') + logger.debug(f'Image size: {im_size}') # Create an image for black pixels and one for coloured pixels im_black = Image.new('RGB', size=im_size, color='white') diff --git a/inkycal/modules/inkycal_feeds.py b/inkycal/modules/inkycal_feeds.py index d7bdde3..a51eba6 100644 --- a/inkycal/modules/inkycal_feeds.py +++ b/inkycal/modules/inkycal_feeds.py @@ -75,7 +75,7 @@ class Feeds(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'Image size: {im_size}') + logger.debug(f'Image size: {im_size}') # Create an image for black pixels and one for coloured pixels im_black = Image.new('RGB', size=im_size, color='white') @@ -83,8 +83,9 @@ class Feeds(inkycal_module): # Check if internet is available if internet_available(): - logger.info('Connection test passed') + logger.debug('Connection test passed') else: + logger.error("Network not reachable. Please check your connection.") raise NetworkNotReachableError # Set some parameters for formatting feeds diff --git a/inkycal/modules/inkycal_image.py b/inkycal/modules/inkycal_image.py index 5387b9b..bb7558b 100755 --- a/inkycal/modules/inkycal_image.py +++ b/inkycal/modules/inkycal_image.py @@ -71,7 +71,7 @@ class Inkyimage(inkycal_module): # Remove background if present im.remove_alpha() - # if autoflip was enabled, flip the image + # if auto-flip was enabled, flip the image if self.autoflip: im.autoflip(self.orientation) diff --git a/inkycal/modules/inkycal_jokes.py b/inkycal/modules/inkycal_jokes.py index 5f0085e..004037b 100755 --- a/inkycal/modules/inkycal_jokes.py +++ b/inkycal/modules/inkycal_jokes.py @@ -39,7 +39,7 @@ class Jokes(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'image size: {im_width} x {im_height} px') + logger.debug(f'image size: {im_width} x {im_height} px') # Create an image for black pixels and one for coloured pixels im_black = Image.new('RGB', size=im_size, color='white') @@ -47,8 +47,9 @@ class Jokes(inkycal_module): # Check if internet is available if internet_available(): - logger.info('Connection test passed') + logger.debug('Connection test passed') else: + logger.error("Network not reachable. Please check your connection.") raise NetworkNotReachableError # Set some parameters for formatting feeds diff --git a/inkycal/modules/inkycal_slideshow.py b/inkycal/modules/inkycal_slideshow.py index 253397d..8ac612e 100755 --- a/inkycal/modules/inkycal_slideshow.py +++ b/inkycal/modules/inkycal_slideshow.py @@ -87,7 +87,7 @@ class Slideshow(inkycal_module): im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'Image size: {im_size}') + logger.debug(f'Image size: {im_size}') # rotates list items by 1 index def rotate(list: list): diff --git a/inkycal/modules/inkycal_stocks.py b/inkycal/modules/inkycal_stocks.py index 47a2b55..c82506f 100755 --- a/inkycal/modules/inkycal_stocks.py +++ b/inkycal/modules/inkycal_stocks.py @@ -63,7 +63,7 @@ class Stocks(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'image size: {im_width} x {im_height} px') + logger.debug(f'image size: {im_width} x {im_height} px') # Create an image for black pixels and one for coloured pixels (required) im_black = Image.new('RGB', size=im_size, color='white') diff --git a/inkycal/modules/inkycal_textfile_to_display.py b/inkycal/modules/inkycal_textfile_to_display.py index 7dc4987..1f5b76e 100644 --- a/inkycal/modules/inkycal_textfile_to_display.py +++ b/inkycal/modules/inkycal_textfile_to_display.py @@ -45,7 +45,7 @@ class TextToDisplay(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'Image size: {im_size}') + logger.debug(f'Image size: {im_size}') # Create an image for black pixels and one for coloured pixels im_black = Image.new('RGB', size=im_size, color='white') diff --git a/inkycal/modules/inkycal_tindie.py b/inkycal/modules/inkycal_tindie.py index 24b51cb..c07efa0 100755 --- a/inkycal/modules/inkycal_tindie.py +++ b/inkycal/modules/inkycal_tindie.py @@ -40,7 +40,7 @@ class Tindie(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'image size: {im_width} x {im_height} px') + logger.debug(f'image size: {im_width} x {im_height} px') # Create an image for black pixels and one for coloured pixels im_black = Image.new('RGB', size=im_size, color='white') @@ -50,6 +50,7 @@ class Tindie(inkycal_module): if internet_available(): logger.info('Connection test passed') else: + logger.error("Network not reachable. Please check your connection.") raise NetworkNotReachableError # Set some parameters for formatting feeds diff --git a/inkycal/modules/inkycal_todoist.py b/inkycal/modules/inkycal_todoist.py index 55e725e..3d41619 100644 --- a/inkycal/modules/inkycal_todoist.py +++ b/inkycal/modules/inkycal_todoist.py @@ -70,7 +70,7 @@ class Todoist(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'Image size: {im_size}') + logger.debug(f'Image size: {im_size}') # Create an image for black pixels and one for coloured pixels im_black = Image.new('RGB', size=im_size, color='white') @@ -80,6 +80,7 @@ class Todoist(inkycal_module): if internet_available(): logger.info('Connection test passed') else: + logger.error("Network not reachable. Please check your connection.") raise NetworkNotReachableError # Set some parameters for formatting todos diff --git a/inkycal/modules/inkycal_weather.py b/inkycal/modules/inkycal_weather.py index 7aeb845..e6b747b 100644 --- a/inkycal/modules/inkycal_weather.py +++ b/inkycal/modules/inkycal_weather.py @@ -154,7 +154,7 @@ class Weather(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info(f'Image size: {im_size}') + logger.debug(f'Image size: {im_size}') # Create an image for black pixels and one for coloured pixels im_black = Image.new('RGB', size=im_size, color='white') @@ -162,8 +162,9 @@ class Weather(inkycal_module): # Check if internet is available if internet_available(): - logger.info('Connection test passed') + logger.debug('Connection test passed') else: + logger.error("Network not reachable. Please check your connection.") raise NetworkNotReachableError def get_moon_phase(): diff --git a/inkycal/modules/inkycal_webshot.py b/inkycal/modules/inkycal_webshot.py index 8d08cc6..f4945f6 100644 --- a/inkycal/modules/inkycal_webshot.py +++ b/inkycal/modules/inkycal_webshot.py @@ -90,7 +90,7 @@ class Webshot(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info('image size: {} x {} px'.format(im_width, im_height)) + logger.debug('image size: {} x {} px'.format(im_width, im_height)) # Create an image for black pixels and one for coloured pixels (required) im_black = Image.new('RGB', size=im_size, color='white') @@ -100,6 +100,7 @@ class Webshot(inkycal_module): if internet_available(): logger.info('Connection test passed') else: + logger.error("Network not reachable. Please check your connection.") raise Exception('Network could not be reached :/') logger.info( diff --git a/inkycal/modules/inkycal_xkcd.py b/inkycal/modules/inkycal_xkcd.py index 63f7140..f3325d8 100644 --- a/inkycal/modules/inkycal_xkcd.py +++ b/inkycal/modules/inkycal_xkcd.py @@ -68,7 +68,7 @@ class Xkcd(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height - logger.info('image size: {} x {} px'.format(im_width, im_height)) + logger.debug('image size: {} x {} px'.format(im_width, im_height)) # Create an image for black pixels and one for coloured pixels (required) im_black = Image.new('RGB', size=im_size, color='white') @@ -78,6 +78,7 @@ class Xkcd(inkycal_module): if internet_available(): logger.info('Connection test passed') else: + logger.error("Network not reachable. Please check your connection.") raise Exception('Network could not be reached :/') # Set some parameters for formatting feeds From d365090c3b597fc01db37e42b5ed5a827304af47 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 25 Jun 2024 14:27:54 +0200 Subject: [PATCH 22/23] use logging in favour of print --- inkycal/modules/dev_module.py | 2 +- inkycal/modules/inky_image.py | 2 +- inkycal/modules/inkycal_agenda.py | 2 +- inkycal/modules/inkycal_calendar.py | 4 ++-- inkycal/modules/inkycal_feeds.py | 2 +- inkycal/modules/inkycal_fullweather.py | 2 +- inkycal/modules/inkycal_image.py | 2 +- inkycal/modules/inkycal_jokes.py | 2 +- inkycal/modules/inkycal_server.py | 2 +- inkycal/modules/inkycal_slideshow.py | 2 +- inkycal/modules/inkycal_stocks.py | 2 +- inkycal/modules/inkycal_textfile_to_display.py | 2 +- inkycal/modules/inkycal_tindie.py | 2 +- inkycal/modules/inkycal_todoist.py | 2 +- inkycal/modules/inkycal_weather.py | 2 +- inkycal/modules/inkycal_webshot.py | 2 +- inkycal/modules/inkycal_xkcd.py | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/inkycal/modules/dev_module.py b/inkycal/modules/dev_module.py index 13d6275..53d8db3 100755 --- a/inkycal/modules/dev_module.py +++ b/inkycal/modules/dev_module.py @@ -156,7 +156,7 @@ class Simple(inkycal_module): # -----------------------------------------------------------------------# # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') ############################################################################# # Validation of module specific parameters (optional) # diff --git a/inkycal/modules/inky_image.py b/inkycal/modules/inky_image.py index eaefe28..5795f0e 100755 --- a/inkycal/modules/inky_image.py +++ b/inkycal/modules/inky_image.py @@ -27,7 +27,7 @@ class Inkyimage: self.image = image # give an OK message - logger.info(f"{__name__} loaded") + logger.debug(f"{__name__} loaded") def load(self, path: str) -> None: """loads an image from a URL or filepath. diff --git a/inkycal/modules/inkycal_agenda.py b/inkycal/modules/inkycal_agenda.py index 5b27d00..a91f93b 100755 --- a/inkycal/modules/inkycal_agenda.py +++ b/inkycal/modules/inkycal_agenda.py @@ -76,7 +76,7 @@ class Agenda(inkycal_module): self.timezone = get_system_tz() # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def generate_image(self): """Generate image for this module""" diff --git a/inkycal/modules/inkycal_calendar.py b/inkycal/modules/inkycal_calendar.py index 8d0cd50..0f947cd 100755 --- a/inkycal/modules/inkycal_calendar.py +++ b/inkycal/modules/inkycal_calendar.py @@ -84,7 +84,7 @@ class Calendar(inkycal_module): ) # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') @staticmethod def flatten(values): @@ -100,7 +100,7 @@ class Calendar(inkycal_module): im_size = im_width, im_height events_height = 0 - logger.info(f'Image size: {im_size}') + logger.debug(f'Image size: {im_size}') # Create an image for black pixels and one for coloured pixels im_black = Image.new('RGB', size=im_size, color='white') diff --git a/inkycal/modules/inkycal_feeds.py b/inkycal/modules/inkycal_feeds.py index a51eba6..38a6294 100644 --- a/inkycal/modules/inkycal_feeds.py +++ b/inkycal/modules/inkycal_feeds.py @@ -60,7 +60,7 @@ class Feeds(inkycal_module): self.shuffle_feeds = config["shuffle_feeds"] # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def _validate(self): """Validate module-specific parameters""" diff --git a/inkycal/modules/inkycal_fullweather.py b/inkycal/modules/inkycal_fullweather.py index e48f17f..5dccc70 100644 --- a/inkycal/modules/inkycal_fullweather.py +++ b/inkycal/modules/inkycal_fullweather.py @@ -239,7 +239,7 @@ class Fullweather(inkycal_module): self.left_section_width = int(self.width / 4) # give an OK message - print(f"{__name__} loaded") + logger.debug(f"{__name__} loaded") def createBaseImage(self): """ diff --git a/inkycal/modules/inkycal_image.py b/inkycal/modules/inkycal_image.py index bb7558b..bdf94bd 100755 --- a/inkycal/modules/inkycal_image.py +++ b/inkycal/modules/inkycal_image.py @@ -50,7 +50,7 @@ class Inkyimage(inkycal_module): self.dither = False # give an OK message - print(f"{__name__} loaded") + logger.debug(f"{__name__} loaded") def generate_image(self): """Generate image for this module""" diff --git a/inkycal/modules/inkycal_jokes.py b/inkycal/modules/inkycal_jokes.py index 004037b..e35fd5e 100755 --- a/inkycal/modules/inkycal_jokes.py +++ b/inkycal/modules/inkycal_jokes.py @@ -30,7 +30,7 @@ class Jokes(inkycal_module): config = config['config'] # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def generate_image(self): """Generate image for this module""" diff --git a/inkycal/modules/inkycal_server.py b/inkycal/modules/inkycal_server.py index 619c146..4cade83 100755 --- a/inkycal/modules/inkycal_server.py +++ b/inkycal/modules/inkycal_server.py @@ -67,7 +67,7 @@ class Inkyserver(inkycal_module): self.path_body = config['path_body'] # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def generate_image(self): """Generate image for this module""" diff --git a/inkycal/modules/inkycal_slideshow.py b/inkycal/modules/inkycal_slideshow.py index 8ac612e..926d72b 100755 --- a/inkycal/modules/inkycal_slideshow.py +++ b/inkycal/modules/inkycal_slideshow.py @@ -77,7 +77,7 @@ class Slideshow(inkycal_module): self._first_run = True # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def generate_image(self): """Generate image for this module""" diff --git a/inkycal/modules/inkycal_stocks.py b/inkycal/modules/inkycal_stocks.py index c82506f..4e7538f 100755 --- a/inkycal/modules/inkycal_stocks.py +++ b/inkycal/modules/inkycal_stocks.py @@ -54,7 +54,7 @@ class Stocks(inkycal_module): self.tickers = config['tickers'] # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def generate_image(self): """Generate image for this module""" diff --git a/inkycal/modules/inkycal_textfile_to_display.py b/inkycal/modules/inkycal_textfile_to_display.py index 1f5b76e..dc36ad1 100644 --- a/inkycal/modules/inkycal_textfile_to_display.py +++ b/inkycal/modules/inkycal_textfile_to_display.py @@ -31,7 +31,7 @@ class TextToDisplay(inkycal_module): self.make_request = True if self.filepath.startswith("https://") else False # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def _validate(self): """Validate module-specific parameters""" diff --git a/inkycal/modules/inkycal_tindie.py b/inkycal/modules/inkycal_tindie.py index c07efa0..bf2b92b 100755 --- a/inkycal/modules/inkycal_tindie.py +++ b/inkycal/modules/inkycal_tindie.py @@ -32,7 +32,7 @@ class Tindie(inkycal_module): # self.mode = config['mode'] # unshipped_orders, shipped_orders, all_orders # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def generate_image(self): """Generate image for this module""" diff --git a/inkycal/modules/inkycal_todoist.py b/inkycal/modules/inkycal_todoist.py index 3d41619..0e95585 100644 --- a/inkycal/modules/inkycal_todoist.py +++ b/inkycal/modules/inkycal_todoist.py @@ -56,7 +56,7 @@ class Todoist(inkycal_module): self._api = TodoistAPI(config['api_key']) # give an OK message - print(f'{__name__} loaded') + logger.debug(f'{__name__} loaded') def _validate(self): """Validate module-specific parameters""" diff --git a/inkycal/modules/inkycal_weather.py b/inkycal/modules/inkycal_weather.py index e6b747b..8bfe2c2 100644 --- a/inkycal/modules/inkycal_weather.py +++ b/inkycal/modules/inkycal_weather.py @@ -143,7 +143,7 @@ class Weather(inkycal_module): self.tempDispUnit = "°" # give an OK message - print(f"{__name__} loaded") + logger.debug(f"{__name__} loaded") diff --git a/inkycal/modules/inkycal_webshot.py b/inkycal/modules/inkycal_webshot.py index f4945f6..ae49dee 100644 --- a/inkycal/modules/inkycal_webshot.py +++ b/inkycal/modules/inkycal_webshot.py @@ -74,7 +74,7 @@ class Webshot(inkycal_module): self.crop_y = 0 # give an OK message - print(f'Inkycal webshot loaded') + logger.debug(f'Inkycal webshot loaded') def generate_image(self): """Generate image for this module""" diff --git a/inkycal/modules/inkycal_xkcd.py b/inkycal/modules/inkycal_xkcd.py index f3325d8..864e15c 100644 --- a/inkycal/modules/inkycal_xkcd.py +++ b/inkycal/modules/inkycal_xkcd.py @@ -53,7 +53,7 @@ class Xkcd(inkycal_module): self.scale_filter = config['filter'] # give an OK message - print(f'Inkycal XKCD loaded') + logger.debug(f'Inkycal XKCD loaded') def generate_image(self): """Generate image for this module""" From 79a9b88091354bf429faabd2d6583efe62bc2591 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 25 Jun 2024 14:40:12 +0200 Subject: [PATCH 23/23] logging improvements --- inkycal/main.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/inkycal/main.py b/inkycal/main.py index 8b4a26f..476f683 100644 --- a/inkycal/main.py +++ b/inkycal/main.py @@ -210,8 +210,6 @@ class Inkycal: Generated images can be found in the /images folder of Inkycal. """ - - logger.info(f"Inkycal version: v{self._release}") logger.info(f'Selected E-paper display: {self.settings["model"]}') # store module numbers in here @@ -222,11 +220,11 @@ class Inkycal: for number in range(1, self._module_number): name = eval(f"self.module_{number}.name") - print(f'generating image(s) for {name}...', end="") success = self.process_module(number) if success: - print("OK!") + logger.debug(f'Image of module {name} generated successfully') else: + logger.warning(f'Generating image of module {name} failed!') errors.append(number) self.info += f"module {number}: Error! " @@ -291,14 +289,14 @@ class Inkycal: # Function to flip images upside down upside_down = lambda image: image.rotate(180, expand=True) - print(f'Inkycal version: v{self._release}') - print(f'Selected E-paper display: {self.settings["model"]}') + logger.info(f'Inkycal version: v{self._release}') + logger.info(f'Selected E-paper display: {self.settings["model"]}') while True: + logger.info("Starting new cycle...") current_time = arrow.now(tz=get_system_tz()) - print(f"Date: {current_time.format('D MMM YY')} | " - f"Time: {current_time.format('HH:mm')}") - print('Generating images for all modules...', end='') + logger.info(f"Timestamp: {current_time.format('HH:mm:ss DD.MM.YYYY')}") + self.cache_data["counter"] = self.counter errors = [] # Store module numbers in here @@ -317,8 +315,10 @@ class Inkycal: if errors: logger.error("Error/s in modules:", *errors) self.counter = 0 + self.cache_data["counter"] = 0 else: self.counter += 1 + self.cache_data["counter"] += 1 logger.info("All images generated successfully!") del errors @@ -330,6 +330,7 @@ class Inkycal: # Check if image should be rendered if self.render: + logger.info("Attempting to render image on display...") display = self.Display self._calibration_check() if self._calibration_state: @@ -353,7 +354,7 @@ class Inkycal: display.render(im_black, im_colour) # Part for black-white ePapers - elif not self.supports_colour: + else: im_black = self._merge_bands() # Flip the image by 180° if required @@ -364,8 +365,8 @@ class Inkycal: (f"{self.image_folder}/canvas.png.hash", im_black), ]): display.render(im_black) - print(f'\nNo errors since {self.counter} display updates \n' - f'program started {runtime.humanize()}') + logger.info(f'\nNo errors since {self.counter} display updates') + logger.info(f'program started {runtime.humanize()}') # store the cache data self.cache.write(self.cache_data)