Adapting modules for new web-ui (do not use yet)

This commit is contained in:
Ace 2020-11-10 11:53:48 +01:00
parent 29788f0313
commit 035ce65f06
14 changed files with 155 additions and 415 deletions

View File

@ -7,8 +7,8 @@ from inkycal.display import Display
import inkycal.modules.inkycal_agenda import inkycal.modules.inkycal_agenda
import inkycal.modules.inkycal_calendar import inkycal.modules.inkycal_calendar
import inkycal.modules.inkycal_weather import inkycal.modules.inkycal_weather
import inkycal.modules.inkycal_rss import inkycal.modules.inkycal_feeds
#import inkycal.modules.inkycal_image # import inkycal.modules.inkycal_image
# import inkycal.modules.inkycal_server # import inkycal.modules.inkycal_server
# Main file # Main file

View File

@ -1,2 +1 @@
from .settings_parser import Settings
from .layout import Layout from .layout import Layout

View File

@ -124,5 +124,4 @@ class Layout:
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone/debug mode'.format( print('running {0} in standalone/debug mode'.format(
os.path.basename(__file__).split('.py')[0])) os.path.basename(__file__).split('.py')[0]))

View File

@ -1,207 +0,0 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Json settings parser for inkycal project
Copyright by aceisace
"""
from inkycal.config.layout import Layout
import json
import os
import logging
from jsmin import jsmin
logger = logging.getLogger('settings')
logger.setLevel(level=logging.DEBUG)
class Settings:
"""Load and validate settings from the settings file"""
_supported_languages = ['en', 'de', 'ru', 'it', 'es', 'fr', 'el', 'sv', 'nl',
'pl', 'ua', 'nb', 'vi', 'zh_tw', 'zh-cn', 'ja', 'ko']
_supported_units = ['metric', 'imperial']
_supported_hours = [12, 24]
_supported_update_interval = [10, 15, 20, 30, 60]
_supported_display_orientation = ['normal', 'upside_down']
_supported_models = [
'epd_7_in_5_v2_colour', 'epd_7_in_5_v2',
'epd_7_in_5_colour', 'epd_7_in_5',
'epd_5_in_83_colour','epd_5_in_83',
'epd_4_in_2_colour', 'epd_4_in_2',
'9_in_7'
]
def __init__(self, settings_file_path):
"""Load settings from path (folder or settings.json file)
Set show_info_section to False to hide the info section"""
try:
if settings_file_path.endswith('settings.json'):
folder = settings_file_path.split('/settings.json')[0]
else:
folder = settings_file_path
os.chdir(folder)
if os.path.exists('settings.jsonc'):
with open("settings.jsonc") as jsonc_file:
# minify in order to remove comments
minified = jsmin(jsonc_file.read())
# remove known invalid json (comma followed by closing accolades)
minified = minified.replace(",}","}")
settings = json.loads(minified)
self._settings = settings
else:
with open("settings.json") as file:
settings = json.load(file)
self._settings = settings
except FileNotFoundError:
print('No settings file found in specified location')
# Validate the settings
self._validate()
# Get the height-percentages of the modules
if self.info_section == True:
self.Layout = Layout(model=self.model, use_info_section = True)
else:
self.Layout = Layout(model=self.model, use_info_section = False)
all_heights = [_['height'] for _ in self._settings['panels']]
num_modules = len(self.active_modules())
# check if height have (or have not) been provided for all modules
if len(all_heights) == num_modules:
# If no height is provided, use default values
if list(set(all_heights)) == [None]:
self.Layout.create_sections()
# if all heights are specified, use given values
else:
logger.info('Setting section height according to settings file')
to_decimal = lambda percentage: percentage/100
top_height = [to_decimal(_['height']) for _ in
self._settings['panels'] if _['location'] == 'top']
middle_height = [to_decimal(_['height']) for _ in
self._settings['panels'] if _['location'] == 'middle']
bottom_height = [to_decimal(_['height']) for _ in
self._settings['panels'] if _['location'] == 'bottom']
self.Layout.create_sections(
top_section = top_height[0] if top_height else 0,
middle_section = middle_height[0] if middle_height else 0,
bottom_section = bottom_height[0] if bottom_height else 0)
# If only some heights were defined, raise an error
else:
print("Section height is not defined for all sections.")
print("Please leave height empty for all modules")
print("OR specify the height for all sections")
raise Exception('Module height is not specified in all modules!')
def _validate(self):
"""Validate the basic config"""
settings = self._settings
required = ['language', 'units', 'hours', 'model', 'calibration_hours',
'display_orientation', 'info_section']
# Check if all required settings exist
for param in required:
if not param in settings:
raise Exception (
'required parameter: `{0}` not found in settings file!'.format(param))
# Attempt to parse the parameters
self.language = settings['language']
self.units = settings['units']
self.hours = settings['hours']
self.model = settings['model']
self.update_interval = settings['update_interval']
self.calibration_hours = settings['calibration_hours']
self.display_orientation = settings['display_orientation']
self.info_section = settings['info_section']
# Validate the parameters
if (not isinstance(self.language, str) or self.language not in
self._supported_languages):
print('Language not supported, switching to fallback, en')
self.language = 'en'
if (not isinstance(self.units, str) or self.units not in
self._supported_units):
print('units not supported, switching to fallback, metric')
self.units = 'metric'
if (not isinstance(self.hours, int) or self.hours not in
self._supported_hours):
print('hour-format not supported, switching to fallback, 24')
self.hours = 24
if (not isinstance(self.model, str) or self.model not in
self._supported_models):
print('model not supported, switching to fallback, epd_7_in_5')
self.model = 'epd_7_in_5'
if (not isinstance(self.update_interval, int) or self.update_interval
not in self._supported_update_interval):
print('update-interval not supported, switching to fallback, 60')
self.update_interval = 60
if (not isinstance(self.calibration_hours, list)):
print('calibration_hours not supported, switching to fallback, [0,12,18]')
self.calibration_hours = [0,12,18]
if (not isinstance(self.display_orientation, str) or self.display_orientation not in
self._supported_display_orientation):
print('display orientation not supported, switching to fallback, normal')
self.display_orientation = 'normal'
if (not isinstance(self.info_section, bool)):
print('info_section must be True/False. Switching to fallback: False')
self.info_section = False
print('Settings file OK!')
def active_modules(self):
modules = [section['type'] for section in self._settings['panels']]
return modules
def common_config(self):
common_config = {
'language' : self.language, 'units' : self.units, 'hours' : self.hours
}
return common_config
def get_config(self, module_name):
"""Ge the config of this module (size, config)"""
if module_name not in self.active_modules():
print('No config is available for this module')
else:
for section in self._settings['panels']:
if section['type'] == module_name:
config = section['config']
size = self.Layout.get_size(self.get_position(module_name))
return {'size':size, 'config': {**config, **self.common_config()}}
def get_position(self, module_name):
"""Get the position of this module's image on the display"""
if module_name not in self.active_modules():
print('No position is available for this module')
else:
for section in self._settings['panels']:
if section['type'] == module_name:
position = section['location']
return position
if __name__ == '__main__':
print('running {0} in standalone/debug mode'.format(
os.path.basename(__file__).split('.py')[0]))

View File

@ -11,6 +11,8 @@ from urllib.request import urlopen
import os import os
import time import time
logger = logging.getLogger('inkycal_custom')
logger.setLevel(level=logging.INFO)
# Get the path to the Inkycal folder # Get the path to the Inkycal folder
top_level = os.path.dirname( top_level = os.path.dirname(
@ -107,11 +109,11 @@ def write(image, xy, box_size, text, font=None, **kwargs):
# Truncate text if text is too long so it can fit inside the box # Truncate text if text is too long so it can fit inside the box
if (text_width, text_height) > (box_width, box_height): if (text_width, text_height) > (box_width, box_height):
logging.debug(('truncating {}'.format(text))) logger.debug(('truncating {}'.format(text)))
while (text_width, text_height) > (box_width, box_height): while (text_width, text_height) > (box_width, box_height):
text=text[0:-1] text=text[0:-1]
text_width, text_height = font.getsize(text)[0], font.getsize('hg')[1] text_width, text_height = font.getsize(text)[0], font.getsize('hg')[1]
logging.debug((text)) logger.debug((text))
# Align text to desired position # Align text to desired position
if alignment == "center" or None: if alignment == "center" or None:

View File

@ -1,99 +0,0 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Inky-Calendar epaper functions
Copyright by aceisace
"""
from importlib import import_module
from PIL import Image
class Display:
"""Display class for inkycal
Handles rendering on display"""
def __init__(self, epaper_model):
"""Load the drivers for this epaper model"""
if 'colour' in epaper_model:
self.supports_colour = True
else:
self.supports_colour = False
try:
driver_path = 'inkycal.display.drivers.{}'.format(epaper_model)
driver = import_module(driver_path)
self._epaper = driver.EPD()
self.model_name = epaper_model
except ImportError:
raise Exception('This module is not supported. Check your spellings?')
except FileNotFoundError:
raise Exception('SPI could not be found. Please check if SPI is enabled')
def render(self, im_black, im_colour = None):
"""Render an image on the epaper
im_colour is required for three-colour epapers"""
epaper = self._epaper
if self.supports_colour == False:
print('Initialising..', end = '')
epaper.init()
# For the 9.7" ePaper, the image needs to be flipped by 90 deg first
# The other displays flip the image automatically
if self.model_name == "9_in_7":
im_black.rotate(90, expand=True)
print('Updating display......', end = '')
epaper.display(epaper.getbuffer(im_black))
print('Done')
elif self.supports_colour == True:
if not im_colour:
raise Exception('im_colour is required for coloured epaper displays')
print('Initialising..', end = '')
epaper.init()
print('Updating display......', end = '')
epaper.display(epaper.getbuffer(im_black), epaper.getbuffer(im_colour))
print('Done')
print('Sending E-Paper to deep sleep...', end = '')
epaper.sleep()
print('Done')
def calibrate(self, cycles=3):
"""Flush display with single colour to prevent burn-ins (ghosting)
cycles -> int. How many times should each colour be flushed?
recommended cycles = 3"""
epaper = self._epaper
epaper.init()
white = Image.new('1', (epaper.width, epaper.height), 'white')
black = Image.new('1', (epaper.width, epaper.height), 'black')
print('----------Started calibration of ePaper display----------')
if self.supports_colour == True:
for _ in range(cycles):
print('Calibrating...', end= ' ')
print('black...', end= ' ')
epaper.display(epaper.getbuffer(black), epaper.getbuffer(white))
print('colour...', end = ' ')
epaper.display(epaper.getbuffer(white), epaper.getbuffer(black))
print('white...')
epaper.display(epaper.getbuffer(white), epaper.getbuffer(white))
print('Cycle {0} of {1} complete'.format(_+1, cycles))
if self.supports_colour == False:
for _ in range(cycles):
print('Calibrating...', end= ' ')
print('black...', end = ' ')
epaper.display(epaper.getbuffer(black))
print('white...')
epaper.display(epaper.getbuffer(white)),
print('Cycle {0} of {1} complete'.format(_+1, cycles))
print('-----------Calibration complete----------')
epaper.sleep()

View File

@ -1,5 +1,5 @@
from .inkycal_agenda import Agenda from .inkycal_agenda import Agenda
from .inkycal_calendar import Calendar from .inkycal_calendar import Calendar
from .inkycal_weather import Weather from .inkycal_weather import Weather
from .inkycal_rss import RSS from .inkycal_feeds import RSS
#from .inkycal_image import Image #from .inkycal_image import Image

View File

@ -14,7 +14,7 @@ import arrow
filename = os.path.basename(__file__).split('.py')[0] filename = os.path.basename(__file__).split('.py')[0]
logger = logging.getLogger(filename) logger = logging.getLogger(filename)
logger.setLevel(level=logging.ERROR) logger.setLevel(level=logging.INFO)
class Agenda(inkycal_module): class Agenda(inkycal_module):
"""Agenda class """Agenda class
@ -33,7 +33,6 @@ class Agenda(inkycal_module):
optional = { optional = {
"ical_files" : { "ical_files" : {
"label":"iCalendar filepaths, separated with a comma", "label":"iCalendar filepaths, separated with a comma",
"default":[]
}, },
"date_format":{ "date_format":{
@ -45,27 +44,37 @@ class Agenda(inkycal_module):
"time_format":{ "time_format":{
"label":"Use an arrow-supported token for custom time formatting "+ "label":"Use an arrow-supported token for custom time formatting "+
"see https://arrow.readthedocs.io/en/stable/#supported-tokens, e.g. HH:mm", "see https://arrow.readthedocs.io/en/stable/#supported-tokens, e.g. HH:mm",
"default":"HH:mm",
}, },
} }
def __init__(self, section_size, section_config): def __init__(self, config):
"""Initialize inkycal_agenda module""" """Initialize inkycal_agenda module"""
super().__init__(section_size, section_config) super().__init__(config)
for param in self.equires: config = config['config']
if not param in section_config:
# Check if all required parameters are present
for param in self.requires:
if not param in config:
raise Exception('config is missing {}'.format(param)) raise Exception('config is missing {}'.format(param))
# module specific parameters # module specific parameters
self.date_format = self.config['date_format'] self.date_format = config['date_format']
self.time_format = self.config['time_format'] self.time_format = config['time_format']
self.language = self.config['language'] self.language = config['language']
self.ical_urls = self.config['ical_urls'] self.ical_urls = config['ical_urls'].split(',')
self.ical_files = self.config['ical_files']
# Check if ical_files is an empty string
if config['ical_files'] != "":
self.ical_files = config['ical_files'].split(',')
else:
self.ical_files = []
# Additional config
self.timezone = get_system_tz() self.timezone = get_system_tz()
# give an OK message # give an OK message
@ -83,9 +92,6 @@ class Agenda(inkycal_module):
if not isinstance(self.language, str): if not isinstance(self.language, str):
print('language has to be a string: "en" ') print('language has to be a string: "en" ')
if not isinstance(self.timezone, str):
print('The timezone has bo be a string.')
if not isinstance(self.ical_urls, list): if not isinstance(self.ical_urls, list):
print('ical_urls has to be a list ["url1", "url2"] ') print('ical_urls has to be a list ["url1", "url2"] ')
@ -96,8 +102,8 @@ class Agenda(inkycal_module):
"""Generate image for this module""" """Generate image for this module"""
# Define new image size with respect to padding # Define new image size with respect to padding
im_width = int(self.width - (self.width * 2 * self.margin_x)) im_width = int(self.width - (2 * self.padding_left))
im_height = int(self.height - (self.height * 2 * self.margin_y)) im_height = int(self.height - (2 * self.padding_top))
im_size = im_width, im_height im_size = im_width, im_height
logger.info('Image size: {0}'.format(im_size)) logger.info('Image size: {0}'.format(im_size))
@ -130,6 +136,7 @@ class Agenda(inkycal_module):
if self.ical_urls: if self.ical_urls:
parser.load_url(self.ical_urls) parser.load_url(self.ical_urls)
if self.ical_files: if self.ical_files:
parser.load_from_file(self.ical_files) parser.load_from_file(self.ical_files)
@ -139,7 +146,7 @@ class Agenda(inkycal_module):
# Sort events by beginning time # Sort events by beginning time
parser.sort() parser.sort()
# parser.show_events() #parser.show_events()
# Set the width for date, time and event titles # Set the width for date, time and event titles
date_width = int(max([self.font.getsize( date_width = int(max([self.font.getsize(
@ -147,8 +154,13 @@ class Agenda(inkycal_module):
for dates in agenda_events]) * 1.2) for dates in agenda_events]) * 1.2)
logger.debug(('date_width:', date_width)) logger.debug(('date_width:', date_width))
# Calculate positions for each line
line_pos = [(0, int(line * line_height)) for line in range(max_lines)]
logger.debug(('line_pos:', line_pos))
# Check if any events were filtered # Check if any events were filtered
if upcoming_events: if upcoming_events:
logger.info('Managed to parse events from urls')
# Find out how much space the event times take # Find out how much space the event times take
time_width = int(max([self.font.getsize( time_width = int(max([self.font.getsize(
@ -168,10 +180,6 @@ class Agenda(inkycal_module):
x_event = date_width + time_width x_event = date_width + time_width
logger.debug(('x-event:', x_event)) logger.debug(('x-event:', x_event))
# Calculate positions for each line
line_pos = [(0, int(line * line_height)) for line in range(max_lines)]
logger.debug(('line_pos:', line_pos))
# Merge list of dates and list of events # Merge list of dates and list of events
agenda_events += upcoming_events agenda_events += upcoming_events
@ -216,6 +224,8 @@ class Agenda(inkycal_module):
# If no events were found, write only dates and lines # If no events were found, write only dates and lines
else: else:
logger.info('no events found')
cursor = 0 cursor = 0
for _ in agenda_events: for _ in agenda_events:
title = _['title'] title = _['title']
@ -228,10 +238,9 @@ class Agenda(inkycal_module):
cursor += 1 cursor += 1
logger.info('no events found')
# return the images ready for the display # return the images ready for the display
return im_black, im_colour return im_black, im_colour
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone mode'.format(filename)) print('running {0} in standalone mode'.format(filename))

View File

@ -12,7 +12,7 @@ import arrow
filename = os.path.basename(__file__).split('.py')[0] filename = os.path.basename(__file__).split('.py')[0]
logger = logging.getLogger(filename) logger = logging.getLogger(filename)
logger.setLevel(level=logging.ERROR) logger.setLevel(level=logging.DEBUG)
class Calendar(inkycal_module): class Calendar(inkycal_module):
"""Calendar class """Calendar class
@ -37,12 +37,10 @@ class Calendar(inkycal_module):
"ical_urls" : { "ical_urls" : {
"label":"iCalendar URL/s, separate multiple ones with a comma", "label":"iCalendar URL/s, separate multiple ones with a comma",
"default":[]
}, },
"ical_files" : { "ical_files" : {
"label":"iCalendar filepaths, separated with a comma", "label":"iCalendar filepaths, separated with a comma",
"default":[]
}, },
"date_format":{ "date_format":{
@ -59,24 +57,33 @@ class Calendar(inkycal_module):
} }
def __init__(self, section_size, section_config): def __init__(self, config):
"""Initialize inkycal_calendar module""" """Initialize inkycal_calendar module"""
super().__init__(section_size, section_config) super().__init__(config)
config = config['config']
# optional parameters
self.weekstart = config['week_starts_on']
self.show_events = bool(config['show_events'])
self.date_format = config["date_format"]
self.time_format = config['time_format']
self.language = config['language']
# module specific parameters if config['ical_urls'] != "":
self.ical_urls = config['ical_urls'].split(',')
else:
self.ical_urls = []
if config['ical_files'] != "":
self.ical_files = config['ical_files'].split(',')
else:
self.ical_files = []
# additional configuration
self.timezone = get_system_tz()
self.num_font = ImageFont.truetype( self.num_font = ImageFont.truetype(
fonts['NotoSans-SemiCondensed'], size = self.fontsize) fonts['NotoSans-SemiCondensed'], size = self.fontsize)
self.weekstart = self.config['week_starts_on']
self.show_events = self.config['show_events']
self.date_format = self.config["date_format"]
self.time_format = self.config['time_format']
self.language = self.config['language']
self.timezone = get_system_tz()
self.ical_urls = self.config['ical_urls']
self.ical_files = self.config['ical_files']
# give an OK message # give an OK message
print('{0} loaded'.format(filename)) print('{0} loaded'.format(filename))
@ -85,8 +92,8 @@ class Calendar(inkycal_module):
"""Generate image for this module""" """Generate image for this module"""
# Define new image size with respect to padding # Define new image size with respect to padding
im_width = int(self.width - (2 * self.padding_x)) im_width = int(self.width - (2 * self.padding_left))
im_height = int(self.height - (2 * self.padding_y)) im_height = int(self.height - (2 * self.padding_top))
im_size = im_width, im_height im_size = im_width, im_height
logger.info('Image size: {0}'.format(im_size)) logger.info('Image size: {0}'.format(im_size))
@ -313,4 +320,4 @@ class Calendar(inkycal_module):
return im_black, im_colour return im_black, im_colour
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone mode'.format(filename)) print('running {0} in standalone mode'.format(filename))

View File

@ -18,7 +18,7 @@ except ImportError:
filename = os.path.basename(__file__).split('.py')[0] filename = os.path.basename(__file__).split('.py')[0]
logger = logging.getLogger(filename) logger = logging.getLogger(filename)
logger.setLevel(level=logging.ERROR) logger.setLevel(level=logging.INFO)
class RSS(inkycal_module): class RSS(inkycal_module):
"""RSS class """RSS class
@ -28,7 +28,7 @@ class RSS(inkycal_module):
name = "Inkycal RSS / Atom" name = "Inkycal RSS / Atom"
requires = { requires = {
"rss_urls" : { "feed_urls" : {
"label":"Please enter ATOM or RSS feed URL/s, separated by a comma", "label":"Please enter ATOM or RSS feed URL/s, separated by a comma",
}, },
@ -44,23 +44,24 @@ class RSS(inkycal_module):
} }
def __init__(self, section_size, section_config): def __init__(self, config):
"""Initialize inkycal_rss module""" """Initialize inkycal_feeds module"""
super().__init__(section_size, section_config) super().__init__(config)
# Check if required parameters are available in config config = config['config']
# Check if all required parameters are present
for param in self.requires: for param in self.requires:
if not param in section_config: if not param in config:
raise Exception('config is missing {}'.format(param)) raise Exception('config is missing {}'.format(param))
# parse required config # required parameters
self.rss_urls = self.config["rss_urls"].split(",") self.feed_urls = self.config["feed_urls"].split(",")
# parse optional config # optional parameters
self.shuffle_feeds = self.config["shuffle_feeds"] self.shuffle_feeds = bool(self.config["shuffle_feeds"])
# give an OK message # give an OK message
print('{0} loaded'.format(filename)) print('{0} loaded'.format(filename))
@ -75,8 +76,8 @@ class RSS(inkycal_module):
"""Generate image for this module""" """Generate image for this module"""
# Define new image size with respect to padding # Define new image size with respect to padding
im_width = int(self.width - ( 2 * self.padding_x)) im_width = int(self.width - (2 * self.padding_left))
im_height = int(self.height - (2 * self.padding_y)) im_height = int(self.height - (2 * self.padding_top))
im_size = im_width, im_height im_size = im_width, im_height
logger.info('image size: {} x {} px'.format(im_width, im_height)) logger.info('image size: {} x {} px'.format(im_width, im_height))
@ -90,7 +91,7 @@ class RSS(inkycal_module):
else: else:
raise Exception('Network could not be reached :/') raise Exception('Network could not be reached :/')
# Set some parameters for formatting rss feeds # Set some parameters for formatting feeds
line_spacing = 1 line_spacing = 1
line_height = self.font.getsize('hg')[1] + line_spacing line_height = self.font.getsize('hg')[1] + line_spacing
line_width = im_width line_width = im_width
@ -103,9 +104,9 @@ class RSS(inkycal_module):
line_positions = [ line_positions = [
(0, spacing_top + _ * line_height ) for _ in range(max_lines)] (0, spacing_top + _ * line_height ) for _ in range(max_lines)]
# Create list containing all rss-feeds from all rss-feed urls # Create list containing all feeds from all urls
parsed_feeds = [] parsed_feeds = []
for feeds in self.rss_urls: for feeds in self.feed_urls:
text = feedparser.parse(feeds) text = feedparser.parse(feeds)
for posts in text.entries: for posts in text.entries:
parsed_feeds.append('{0}: {1}'.format(posts.title, posts.summary)) parsed_feeds.append('{0}: {1}'.format(posts.title, posts.summary))
@ -131,23 +132,21 @@ class RSS(inkycal_module):
filtered_feeds = flatten(filtered_feeds) filtered_feeds = flatten(filtered_feeds)
self._filtered_feeds = filtered_feeds self._filtered_feeds = filtered_feeds
logger.debug(f'filtered feeds -> {filtered_feeds}')
# Check if feeds could be parsed and can be displayed # Check if feeds could be parsed and can be displayed
if len(filtered_feeds) == 0 and len(parsed_feeds) > 0: if len(filtered_feeds) == 0 and len(parsed_feeds) > 0:
print('Feeds could be parsed, but the text is too long to be displayed:/') print('Feeds could be parsed, but the text is too long to be displayed:/')
elif len(filtered_feeds) == 0 and len(parsed_feeds) == 0: elif len(filtered_feeds) == 0 and len(parsed_feeds) == 0:
print('No feeds could be parsed :/') print('No feeds could be parsed :/')
else: else:
# Write rss-feeds on image # Write feeds on image
for _ in range(len(filtered_feeds)): for _ in range(len(filtered_feeds)):
write(im_black, line_positions[_], (line_width, line_height), write(im_black, line_positions[_], (line_width, line_height),
filtered_feeds[_], font = self.font, alignment= 'left') filtered_feeds[_], font = self.font, alignment= 'left')
# Cleanup
del filtered_feeds, parsed_feeds, wrapped, counter, text
# Save image of black and colour channel in image-folder # Save image of black and colour channel in image-folder
return im_black, im_colour return im_black, im_colour
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone/debug mode'.format(filename)) print('running {0} in standalone/debug mode'.format(filename))
print(RSS.get_config())

View File

@ -169,8 +169,4 @@ class Todoist(inkycal_module):
return im_black, im_colour return im_black, im_colour
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone/debug mode'.format(filename)) print('running {0} in standalone/debug mode'.format(filename))
config = {'api_key':'4e166367dcafdd60e6a9f4cbed598d578bf2c359'}
size = (480, 100)
a = Todoist(size, config)
b,c = a.generate_image()

View File

@ -81,42 +81,74 @@ class Weather(inkycal_module):
} }
def __init__(self, section_size, section_config): def __init__(self, config):
"""Initialize inkycal_weather module""" """Initialize inkycal_weather module"""
super().__init__(section_size, section_config) super().__init__(config)
# Module specific parameters config = config['config']
# Check if all required parameters are present
for param in self.requires: for param in self.requires:
if not param in section_config: if not param in config:
raise Exception('config is missing {}'.format(param)) raise Exception('config is missing {}'.format(param))
# required parameters # required parameters
self.location = self.config['location'] self.location = config['location']
self.api_key = self.config['api_key'] self.api_key = config['api_key']
# optional parameters # optional parameters
self.round_temperature = self.config['round_temperature'] self.round_temperature = bool(config['round_temperature'])
self.round_windspeed = self.config['round_windspeed'] self.round_windspeed = bool(config['round_windspeed'])
self.forecast_interval = self.config['forecast_interval'] self.forecast_interval = config['forecast_interval']
self.units = self.config['units'] self.units = config['units']
self.hour_format = self.config['hour_format'] self.hour_format = int(config['hour_format'])
self.use_beaufort = self.config['use_beaufort'] self.use_beaufort = bool(config['use_beaufort'])
# additional configuration
self.owm = pyowm.OWM(self.api_key)
self.timezone = get_system_tz() self.timezone = get_system_tz()
self.locale = sys_locale()[0] self.locale = sys_locale()[0]
self.weatherfont = ImageFont.truetype(fonts['weathericons-regular-webfont'], self.weatherfont = ImageFont.truetype(
size = self.fontsize) fonts['weathericons-regular-webfont'], size = self.fontsize)
#self.owm = pyowm.OWM(self.config['api_key'])
# give an OK message # give an OK message
print('{0} loaded'.format(filename)) print('{0} loaded'.format(filename))
def _validate(self):
if not isinstance(self.location, str):
print(f'location should be -> Manchester, USA, not {self.location}')
if not isinstance(self.api_key, str):
print(f'api_key should be a string, not {self.api_key}')
if not isinstance(self.round_temperature, bool):
print(f'round_temperature should be a boolean, not {self.round_temperature}')
if not isinstance(self.round_windspeed, bool):
print(f'round_windspeed should be a boolean, not {self.round_windspeed}')
if not isinstance(self.forecast_interval, int):
print(f'forecast_interval should be a boolean, not {self.forecast_interval}')
if not isinstance(self.units, str):
print(f'units should be a boolean, not {self.units}')
if not isinstance(self.hour_format, int):
print(f'hour_format should be a int, not {self.hour_format}')
if not isinstance(self.use_beaufort, bool):
print(f'use_beaufort should be a int, not {self.use_beaufort}')
def generate_image(self): def generate_image(self):
"""Generate image for this module""" """Generate image for this module"""
# Define new image size with respect to padding # Define new image size with respect to padding
im_width = int(self.width - (2 * self.padding_x)) im_width = int(self.width - (2 * self.padding_left))
im_height = int(self.height - (2 * self.padding_y)) im_height = int(self.height - (2 * self.padding_top))
im_size = im_width, im_height im_size = im_width, im_height
logger.info('image size: {} x {} px'.format(im_width, im_height)) logger.info('image size: {} x {} px'.format(im_width, im_height))
@ -338,6 +370,10 @@ class Weather(inkycal_module):
elif self.forecast_interval == 'daily': elif self.forecast_interval == 'daily':
###
logger.debug("daily")
def calculate_forecast(days_from_today): def calculate_forecast(days_from_today):
"""Get temperature range and most frequent icon code for forecast """Get temperature range and most frequent icon code for forecast
days_from_today should be int from 1-4: e.g. 2 -> 2 days from today days_from_today should be int from 1-4: e.g. 2 -> 2 days from today
@ -352,7 +388,6 @@ class Weather(inkycal_module):
# Get forecasts for each time-object # Get forecasts for each time-object
forecasts = [forecast.get_weather_at(_.datetime) for _ in time_range] forecasts = [forecast.get_weather_at(_.datetime) for _ in time_range]
# Get all temperatures for this day # Get all temperatures for this day
daily_temp = [round(_.get_temperature(unit=temp_unit)['temp'], daily_temp = [round(_.get_temperature(unit=temp_unit)['temp'],
ndigits=dec_temp) for _ in forecasts] ndigits=dec_temp) for _ in forecasts]
@ -418,7 +453,6 @@ class Weather(inkycal_module):
moonphase = get_moon_phase() moonphase = get_moon_phase()
# Fill weather details in col 1 (current weather icon) # Fill weather details in col 1 (current weather icon)
# write(im_black, (col_width, row_height), now_str, text_now_pos, font = font)
draw_icon(im_colour, weather_icon_pos, (icon_large, icon_large), draw_icon(im_colour, weather_icon_pos, (icon_large, icon_large),
weathericons[weather_icon]) weathericons[weather_icon])
@ -481,4 +515,4 @@ class Weather(inkycal_module):
return im_black, im_colour return im_black, im_colour
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone mode'.format(filename)) print('running {0} in standalone mode'.format(filename))

View File

@ -1,6 +1,10 @@
import abc import abc
from inkycal.custom import * from inkycal.custom import *
# Set the root logger to level DEBUG to allow any inkycal module to use the
# logger for any level
logging.basicConfig(level = logging.DEBUG)
class inkycal_module(metaclass=abc.ABCMeta): class inkycal_module(metaclass=abc.ABCMeta):
"""Generic base class for inykcal modules""" """Generic base class for inykcal modules"""
@ -10,16 +14,18 @@ class inkycal_module(metaclass=abc.ABCMeta):
callable(subclass.generate_image) or callable(subclass.generate_image) or
NotImplemented) NotImplemented)
def __init__(self, section_config): def __init__(self, config):
"""Initialize module with given config"""
# Initializes base module # Initializes base module
# sets properties shared amongst all sections # sets properties shared amongst all sections
self.config = section_config self.config = conf = config['config']
self.width, self.height = section_config['size'] self.width, self.height = conf['size']
self.padding_left = self.padding_right = self.config["padding_x"] self.padding_left = self.padding_right = conf["padding_x"]
self.padding_top = self.padding_bottom = self.config["padding_y"] self.padding_top = self.padding_bottom = conf['padding_y']
self.fontsize = self.config["fontsize"] self.fontsize = conf["fontsize"]
self.font = ImageFont.truetype( self.font = ImageFont.truetype(
fonts['NotoSans-SemiCondensed'], size = self.fontsize) fonts['NotoSans-SemiCondensed'], size = self.fontsize)

View File

@ -1,9 +1,7 @@
#!/usr/bin/python3 #!/usr/bin/python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Module template for Inky-Calendar Project Developer module template for Inkycal Project
Create your own module with this template
Copyright by aceisace Copyright by aceisace
""" """
@ -79,9 +77,6 @@ class Simple(inkycal_module):
# Initialise this module via the inkycal_module template (required) # Initialise this module via the inkycal_module template (required)
super().__init__(section_size, section_config) super().__init__(section_size, section_config)
# module name (required)
self.name = self.__class__.__name__
# module specific parameters (optional) # module specific parameters (optional)
self.do_something = True self.do_something = True