Adapting modules for new web-ui (do not use yet)
This commit is contained in:
parent
29788f0313
commit
035ce65f06
@ -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
|
||||||
|
@ -1,2 +1 @@
|
|||||||
from .settings_parser import Settings
|
|
||||||
from .layout import Layout
|
from .layout import Layout
|
||||||
|
@ -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]))
|
||||||
|
|
@ -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]))
|
|
||||||
|
|
@ -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:
|
||||||
|
@ -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()
|
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
@ -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))
|
@ -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))
|
@ -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())
|
|
@ -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()
|
|
@ -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))
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user