python 3.11 & code quality improvements
This commit is contained in:
parent
4f2dacc35a
commit
93c968da53
@ -1,2 +1,3 @@
|
||||
from .functions import *
|
||||
from .inkycal_exceptions import *
|
||||
from .openweathermap_wrapper import OpenWeatherMap
|
@ -6,8 +6,10 @@ Inkycal custom-functions for ease-of-use
|
||||
Copyright by aceinnolab
|
||||
"""
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageColor
|
||||
from urllib.request import urlopen
|
||||
import requests
|
||||
import os
|
||||
import time
|
||||
|
||||
@ -98,11 +100,13 @@ def auto_fontsize(font, max_height):
|
||||
Returns:
|
||||
A PIL font object with modified height.
|
||||
"""
|
||||
|
||||
fontsize = font.getsize('hg')[1]
|
||||
while font.getsize('hg')[1] <= (max_height * 0.80):
|
||||
text_bbox = font.getbbox("hg")
|
||||
text_height = text_bbox[3] - text_bbox[1]
|
||||
fontsize = text_height
|
||||
while text_height <= (max_height * 0.80):
|
||||
fontsize += 1
|
||||
font = ImageFont.truetype(font.path, fontsize)
|
||||
text_height = text_bbox[3] - text_bbox[1]
|
||||
return font
|
||||
|
||||
|
||||
@ -154,21 +158,34 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
if autofit or (fill_width != 1.0) or (fill_height != 0.8):
|
||||
size = 8
|
||||
font = ImageFont.truetype(font.path, size)
|
||||
text_width, text_height = font.getsize(text)[0], font.getsize('hg')[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]
|
||||
|
||||
while (text_width < int(box_width * fill_width) and
|
||||
text_height < int(box_height * fill_height)):
|
||||
size += 1
|
||||
font = ImageFont.truetype(font.path, size)
|
||||
text_width, text_height = font.getsize(text)[0], font.getsize('hg')[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]
|
||||
|
||||
text_width, text_height = font.getsize(text)[0], font.getsize('hg')[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]
|
||||
|
||||
# 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)))
|
||||
while (text_width, text_height) > (box_width, box_height):
|
||||
text = text[0:-1]
|
||||
text_width, text_height = font.getsize(text)[0], font.getsize('hg')[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)
|
||||
|
||||
# Align text to desired position
|
||||
@ -215,14 +232,17 @@ def text_wrap(text, font=None, max_width=None):
|
||||
A list containing chunked strings of the full text.
|
||||
"""
|
||||
lines = []
|
||||
if font.getsize(text)[0] < max_width:
|
||||
|
||||
text_width = font.getlength(text)
|
||||
|
||||
if text_width < max_width:
|
||||
lines.append(text)
|
||||
else:
|
||||
words = text.split(' ')
|
||||
i = 0
|
||||
while i < len(words):
|
||||
line = ''
|
||||
while i < len(words) and font.getsize(line + words[i])[0] <= max_width:
|
||||
while i < len(words) and font.getlength(line + words[i]) <= max_width:
|
||||
line = line + words[i] + " "
|
||||
i += 1
|
||||
if not line:
|
||||
@ -249,9 +269,10 @@ def internet_available():
|
||||
"""
|
||||
|
||||
try:
|
||||
urlopen('https://google.com', timeout=5)
|
||||
requests.get('https://google.com', timeout=5)
|
||||
return True
|
||||
except:
|
||||
print(f"Network could not be reached: {traceback.print_exc()}")
|
||||
return False
|
||||
|
||||
|
||||
|
43
inkycal/custom/openweathermap_wrapper.py
Normal file
43
inkycal/custom/openweathermap_wrapper.py
Normal file
@ -0,0 +1,43 @@
|
||||
import logging
|
||||
from enum import Enum
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class WEATHER_OPTIONS(Enum):
|
||||
CURRENT_WEATHER = "weather"
|
||||
|
||||
class FORECAST_INTERVAL(Enum):
|
||||
THREE_HOURS = "3h"
|
||||
FIVE_DAYS = "5d"
|
||||
|
||||
|
||||
|
||||
class OpenWeatherMap:
|
||||
def __init__(self, api_key:str, city_id:int, units:str) -> None:
|
||||
self.api_key = api_key
|
||||
self.city_id = city_id
|
||||
assert (units in ["metric", "imperial"] )
|
||||
self.units = units
|
||||
self._api_version = "2.5"
|
||||
self._base_url = f"https://api.openweathermap.org/data/{self._api_version}"
|
||||
|
||||
|
||||
def get_current_weather(self) -> dict:
|
||||
current_weather_url = f"{self._base_url}/weather?id={self.city_id}&appid={self.api_key}&units={self.units}"
|
||||
response = requests.get(current_weather_url)
|
||||
if not response.ok:
|
||||
raise AssertionError(f"Failure getting the current weather: code {response.status_code}. Reason: {response.text}")
|
||||
data = json.loads(response.text)
|
||||
return data
|
||||
|
||||
def get_weather_forecast(self) -> dict:
|
||||
forecast_url = f"{self._base_url}/forecast?id={self.city_id}&appid={self.api_key}&units={self.units}"
|
||||
response = requests.get(forecast_url)
|
||||
if not response.ok:
|
||||
raise AssertionError(f"Failure getting the current weather: code {response.status_code}. Reason: {response.text}")
|
||||
data = json.loads(response.text)["list"]
|
||||
return data
|
||||
|
@ -98,7 +98,9 @@ class Agenda(inkycal_module):
|
||||
|
||||
# Calculate the max number of lines that can fit on the image
|
||||
line_spacing = 1
|
||||
line_height = int(self.font.getsize('hg')[1]) + line_spacing
|
||||
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing
|
||||
line_width = im_width
|
||||
max_lines = im_height // line_height
|
||||
logger.debug(f'max lines: {max_lines}')
|
||||
@ -133,8 +135,8 @@ class Agenda(inkycal_module):
|
||||
# parser.show_events()
|
||||
|
||||
# Set the width for date, time and event titles
|
||||
date_width = int(max([self.font.getsize(
|
||||
dates['begin'].format(self.date_format, locale=self.language))[0]
|
||||
date_width = int(max([self.font.getlength(
|
||||
dates['begin'].format(self.date_format, locale=self.language))
|
||||
for dates in agenda_events]) * 1.2)
|
||||
logger.debug(f'date_width: {date_width}')
|
||||
|
||||
@ -147,8 +149,9 @@ class Agenda(inkycal_module):
|
||||
logger.info('Managed to parse events from urls')
|
||||
|
||||
# Find out how much space the event times take
|
||||
time_width = int(max([self.font.getsize(
|
||||
events['begin'].format(self.time_format, locale=self.language))[0]
|
||||
|
||||
time_width = int(max([self.font.getlength(
|
||||
events['begin'].format(self.time_format, locale=self.language))
|
||||
for events in upcoming_events]) * 1.2)
|
||||
logger.debug(f'time_width: {time_width}')
|
||||
|
||||
|
@ -110,7 +110,8 @@ class Calendar(inkycal_module):
|
||||
|
||||
# Allocate space for month-names, weekdays etc.
|
||||
month_name_height = int(im_height * 0.10)
|
||||
weekdays_height = int(self.font.getsize('hg')[1] * 1.25)
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
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}")
|
||||
|
||||
@ -182,15 +183,15 @@ class Calendar(inkycal_module):
|
||||
]
|
||||
logger.debug(f'weekday names: {weekday_names}')
|
||||
|
||||
for idx, weekday in enumerate(weekday_pos):
|
||||
for index, weekday in enumerate(weekday_pos):
|
||||
write(
|
||||
im_black,
|
||||
weekday,
|
||||
(icon_width, weekdays_height),
|
||||
weekday_names[idx],
|
||||
weekday_names[index],
|
||||
font=self.font,
|
||||
autofit=True,
|
||||
fill_height=1.0,
|
||||
fill_height=0.9,
|
||||
)
|
||||
|
||||
# Create a calendar template and flatten (remove nestings)
|
||||
@ -207,6 +208,10 @@ class Calendar(inkycal_module):
|
||||
# remove zeros from calendar since they are not required
|
||||
calendar_flat = [num for num in calendar_flat if num != 0]
|
||||
|
||||
# ensure all numbers have the same size
|
||||
fontsize_numbers = int(min(icon_width, icon_height) * 0.5)
|
||||
number_font = ImageFont.truetype(self.font.path, fontsize_numbers)
|
||||
|
||||
# Add the numbers on the correct positions
|
||||
for number in calendar_flat:
|
||||
if number != int(now.day):
|
||||
@ -215,9 +220,7 @@ class Calendar(inkycal_module):
|
||||
grid[number],
|
||||
(icon_width, icon_height),
|
||||
str(number),
|
||||
font=self.num_font,
|
||||
fill_height=0.5,
|
||||
fill_width=0.5,
|
||||
font=number_font,
|
||||
)
|
||||
|
||||
# Draw a red/black circle with the current day of month in white
|
||||
@ -262,10 +265,10 @@ class Calendar(inkycal_module):
|
||||
from inkycal.modules.ical_parser import iCalendar
|
||||
|
||||
# find out how many lines can fit at max in the event section
|
||||
line_spacing = 0
|
||||
max_event_lines = events_height // (
|
||||
self.font.getsize('hg')[1] + line_spacing
|
||||
)
|
||||
line_spacing = 2
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
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
|
||||
events_offset = im_height - events_height
|
||||
@ -329,31 +332,18 @@ class Calendar(inkycal_module):
|
||||
# Find out how much space (width) the date format requires
|
||||
lang = self.language
|
||||
|
||||
date_width = int(
|
||||
max(
|
||||
(
|
||||
self.font.getsize(
|
||||
events['begin'].format(self.date_format, locale=lang)
|
||||
)[0]
|
||||
for events in upcoming_events
|
||||
)
|
||||
)
|
||||
* 1.1
|
||||
date_width = int(max((
|
||||
self.font.getlength(events['begin'].format(self.date_format, locale=lang))
|
||||
for events in upcoming_events))* 1.1
|
||||
)
|
||||
|
||||
time_width = int(
|
||||
max(
|
||||
(
|
||||
self.font.getsize(
|
||||
events['begin'].format(self.time_format, locale=lang)
|
||||
)[0]
|
||||
for events in upcoming_events
|
||||
)
|
||||
)
|
||||
* 1.1
|
||||
time_width = int(max((
|
||||
self.font.getlength(events['begin'].format(self.time_format, locale=lang))
|
||||
for events in upcoming_events))* 1.1
|
||||
)
|
||||
|
||||
line_height = self.font.getsize('hg')[1] + line_spacing
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing
|
||||
|
||||
event_width_s = im_width - date_width - time_width
|
||||
event_width_l = im_width - date_width
|
||||
@ -411,12 +401,13 @@ class Calendar(inkycal_module):
|
||||
cursor += 1
|
||||
else:
|
||||
symbol = '- '
|
||||
while self.font.getsize(symbol)[0] < im_width * 0.9:
|
||||
|
||||
while self.font.getlength(symbol) < im_width * 0.9:
|
||||
symbol += ' -'
|
||||
write(
|
||||
im_black,
|
||||
event_lines[0],
|
||||
(im_width, self.font.getsize(symbol)[1]),
|
||||
(im_width, line_height),
|
||||
symbol,
|
||||
font=self.font,
|
||||
)
|
||||
|
@ -91,9 +91,11 @@ class Feeds(inkycal_module):
|
||||
|
||||
# Set some parameters for formatting feeds
|
||||
line_spacing = 1
|
||||
line_height = self.font.getsize('hg')[1] + line_spacing
|
||||
|
||||
line_width = im_width
|
||||
max_lines = (im_height // (self.font.getsize('hg')[1] + line_spacing))
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing
|
||||
max_lines = (im_height // (line_height + line_spacing))
|
||||
|
||||
# Calculate padding from top so the lines look centralised
|
||||
spacing_top = int(im_height % line_height / 2)
|
||||
|
@ -54,10 +54,11 @@ class Jokes(inkycal_module):
|
||||
raise NetworkNotReachableError
|
||||
|
||||
# Set some parameters for formatting feeds
|
||||
line_spacing = 1
|
||||
line_height = self.font.getsize('hg')[1] + line_spacing
|
||||
line_spacing = 5
|
||||
text_bbox = self.font.getbbox("hg")
|
||||
line_height = text_bbox[3] - text_bbox[1] + line_spacing
|
||||
line_width = im_width
|
||||
max_lines = (im_height // (self.font.getsize('hg')[1] + line_spacing))
|
||||
max_lines = (im_height // (line_height + line_spacing))
|
||||
|
||||
logger.debug(f"max_lines: {max_lines}")
|
||||
|
||||
|
@ -96,9 +96,10 @@ class Stocks(inkycal_module):
|
||||
|
||||
# Set some parameters for formatting feeds
|
||||
line_spacing = 1
|
||||
line_height = self.font.getsize('hg')[1] + line_spacing
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing
|
||||
line_width = im_width
|
||||
max_lines = (im_height // (self.font.getsize('hg')[1] + line_spacing))
|
||||
max_lines = (im_height // line_height)
|
||||
|
||||
logger.debug(f"max_lines: {max_lines}")
|
||||
|
||||
|
@ -73,10 +73,11 @@ class TextToDisplay(inkycal_module):
|
||||
raise NetworkNotReachableError
|
||||
|
||||
# Set some parameters for formatting feeds
|
||||
line_spacing = 1
|
||||
line_height = self.font.getsize('hg')[1] + line_spacing
|
||||
line_spacing = 4
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing
|
||||
line_width = im_width
|
||||
max_lines = (im_height // (self.font.getsize('hg')[1] + line_spacing))
|
||||
max_lines = im_height // line_height
|
||||
|
||||
# Calculate padding from top so the lines look centralised
|
||||
spacing_top = int(im_height % line_height / 2)
|
||||
|
@ -86,9 +86,10 @@ class Todoist(inkycal_module):
|
||||
|
||||
# Set some parameters for formatting todos
|
||||
line_spacing = 1
|
||||
line_height = self.font.getsize('hg')[1] + line_spacing
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
line_height = text_bbox_height[3] - text_bbox_height[1] + line_spacing
|
||||
line_width = im_width
|
||||
max_lines = (im_height // (self.font.getsize('hg')[1] + line_spacing))
|
||||
max_lines = im_height // line_height
|
||||
|
||||
# Calculate padding from top so the lines look centralised
|
||||
spacing_top = int(im_height % line_height / 2)
|
||||
|
@ -12,7 +12,7 @@ import math
|
||||
import decimal
|
||||
import arrow
|
||||
|
||||
from pyowm.owm import OWM
|
||||
from inkycal.custom import OpenWeatherMap
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -95,7 +95,7 @@ class Weather(inkycal_module):
|
||||
self.use_beaufort = config['use_beaufort']
|
||||
|
||||
# additional configuration
|
||||
self.owm = OWM(self.api_key).weather_manager()
|
||||
self.owm = OpenWeatherMap(api_key=self.api_key, city_id=self.location, units=config['units'])
|
||||
self.timezone = get_system_tz()
|
||||
self.locale = config['language']
|
||||
self.weatherfont = ImageFont.truetype(
|
||||
@ -104,6 +104,42 @@ class Weather(inkycal_module):
|
||||
# give an OK message
|
||||
print(f"{__name__} loaded")
|
||||
|
||||
|
||||
@staticmethod
|
||||
def mps_to_beaufort(meters_per_second:float) -> int:
|
||||
"""Map meters per second to the beaufort scale.
|
||||
|
||||
Args:
|
||||
meters_per_second:
|
||||
float representing meters per seconds
|
||||
|
||||
Returns:
|
||||
an integer of the beaufort scale mapping the input
|
||||
"""
|
||||
thresholds = [0.3, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.7, 24.5, 28.4]
|
||||
return next((i for i, threshold in enumerate(thresholds) if meters_per_second < threshold), 11)
|
||||
|
||||
@staticmethod
|
||||
def mps_to_mph(meters_per_second:float) -> float:
|
||||
"""Map meters per second to miles per hour, rounded to one decimal place.
|
||||
|
||||
Args:
|
||||
meters_per_second:
|
||||
float representing meters per seconds.
|
||||
|
||||
Returns:
|
||||
float representing the input value in miles per hour.
|
||||
"""
|
||||
# 1 m/s is approximately equal to 2.23694 mph
|
||||
miles_per_hour = meters_per_second * 2.23694
|
||||
return round(miles_per_hour, 1)
|
||||
|
||||
@staticmethod
|
||||
def celsius_to_fahrenheit(celsius:int or float):
|
||||
"""Converts the given temperate from degrees Celsius to Fahrenheit."""
|
||||
fahrenheit = (celsius * 9 / 5) + 32
|
||||
return fahrenheit
|
||||
|
||||
def generate_image(self):
|
||||
"""Generate image for this module"""
|
||||
|
||||
@ -124,7 +160,11 @@ class Weather(inkycal_module):
|
||||
raise NetworkNotReachableError
|
||||
|
||||
def get_moon_phase():
|
||||
"""Calculate the current (approximate) moon phase"""
|
||||
"""Calculate the current (approximate) moon phase
|
||||
|
||||
Returns:
|
||||
The corresponding moonphase-icon.
|
||||
"""
|
||||
|
||||
dec = decimal.Decimal
|
||||
diff = now - arrow.get(2001, 1, 1)
|
||||
@ -154,7 +194,7 @@ class Weather(inkycal_module):
|
||||
return answer
|
||||
|
||||
# Lookup-table for weather icons and weather codes
|
||||
weathericons = {
|
||||
weather_icons = {
|
||||
'01d': '\uf00d',
|
||||
'02d': '\uf002',
|
||||
'03d': '\uf013',
|
||||
@ -227,26 +267,26 @@ class Weather(inkycal_module):
|
||||
# Increase fontsize to fit specified height and width of text box
|
||||
size = 8
|
||||
font = ImageFont.truetype(font.path, size)
|
||||
text_width, text_height = font.getsize(text)
|
||||
text_width, text_height = font.getbbox(text)[2:]
|
||||
|
||||
while (text_width < int(box_width * 0.9) and
|
||||
text_height < int(box_height * 0.9)):
|
||||
size += 1
|
||||
font = ImageFont.truetype(font.path, size)
|
||||
text_width, text_height = font.getsize(text)
|
||||
text_width, text_height = font.getbbox(text)[2:]
|
||||
|
||||
text_width, text_height = font.getsize(text)
|
||||
text_width, text_height = font.getbbox(text)[2:]
|
||||
|
||||
# Align text to desired position
|
||||
x = int((box_width / 2) - (text_width / 2))
|
||||
y = int((box_height / 2) - (text_height / 2) - (icon_size_correction[icon] * size) / 2)
|
||||
y = int((box_height / 2) - (text_height / 2))
|
||||
|
||||
# Draw the text in the text-box
|
||||
draw = ImageDraw.Draw(image)
|
||||
space = Image.new('RGBA', (box_width, box_height))
|
||||
ImageDraw.Draw(space).text((x, y), text, fill='black', font=font)
|
||||
|
||||
if rotation != None:
|
||||
if rotation:
|
||||
space.rotate(rotation, expand=True)
|
||||
|
||||
# Update only region with text (add text with transparent background)
|
||||
@ -350,14 +390,9 @@ class Weather(inkycal_module):
|
||||
temp_fc4 = (col7, row3)
|
||||
|
||||
# Create current-weather and weather-forecast objects
|
||||
if self.location.isdigit():
|
||||
logging.debug('looking up location by ID')
|
||||
weather = self.owm.weather_at_id(int(self.location)).weather
|
||||
forecast = self.owm.forecast_at_id(int(self.location), '3h')
|
||||
else:
|
||||
logging.debug('looking up location by string')
|
||||
weather = self.owm.weather_at_place(self.location).weather
|
||||
forecast = self.owm.forecast_at_place(self.location, '3h')
|
||||
logging.debug('looking up location by ID')
|
||||
weather = self.owm.get_current_weather()
|
||||
forecast = self.owm.get_weather_forecast()
|
||||
|
||||
# Set decimals
|
||||
dec_temp = None if self.round_temperature == True else 1
|
||||
@ -369,12 +404,14 @@ class Weather(inkycal_module):
|
||||
elif self.units == 'imperial':
|
||||
temp_unit = 'fahrenheit'
|
||||
|
||||
logging.debug(f'temperature unit: {temp_unit}')
|
||||
logging.debug(f'temperature unit: {self.units}')
|
||||
logging.debug(f'decimals temperature: {dec_temp} | decimals wind: {dec_wind}')
|
||||
|
||||
# Get current time
|
||||
now = arrow.utcnow()
|
||||
|
||||
fc_data = {}
|
||||
|
||||
if self.forecast_interval == 'hourly':
|
||||
|
||||
logger.debug("getting hourly forecasts")
|
||||
@ -386,21 +423,22 @@ class Weather(inkycal_module):
|
||||
else:
|
||||
hour_gap = 3
|
||||
|
||||
# Create timings for hourly forcasts
|
||||
# Create timings for hourly forecasts
|
||||
forecast_timings = [now.shift(hours=+ hour_gap + _).floor('hour')
|
||||
for _ in range(0, 12, 3)]
|
||||
|
||||
# Create forecast objects for given timings
|
||||
forecasts = [forecast.get_weather_at(forecast_time.datetime) for
|
||||
forecast_time in forecast_timings]
|
||||
forecasts = [_ for _ in forecast if arrow.get(_["dt"]) in forecast_timings]
|
||||
|
||||
# Add forecast-data to fc_data dictionary
|
||||
fc_data = {}
|
||||
for forecast in forecasts:
|
||||
temp = '{}°'.format(round(
|
||||
forecast.temperature(unit=temp_unit)['temp'], ndigits=dec_temp))
|
||||
if self.units == "metric":
|
||||
temp = f"{round(weather['main']['temp'], ndigits=dec_temp)}°C"
|
||||
else:
|
||||
temp = f"{round(self.celsius_to_fahrenheit(weather['weather']['main']['temp']), ndigits=dec_temp)}°F"
|
||||
|
||||
icon = forecast.weather_icon_name
|
||||
icon = forecast["weather"][0]["icon"]
|
||||
fc_data['fc' + str(forecasts.index(forecast) + 1)] = {
|
||||
'temp': temp,
|
||||
'icon': icon,
|
||||
@ -412,38 +450,35 @@ class Weather(inkycal_module):
|
||||
|
||||
logger.debug("getting daily forecasts")
|
||||
|
||||
def calculate_forecast(days_from_today):
|
||||
def calculate_forecast(days_from_today) -> dict:
|
||||
"""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
|
||||
"""
|
||||
|
||||
# Create a list containing time-objects for every 3rd hour of the day
|
||||
time_range = list(arrow.Arrow.range('hour',
|
||||
now.shift(days=days_from_today).floor('day'),
|
||||
now.shift(days=days_from_today).ceil('day')
|
||||
))[::3]
|
||||
time_range = list(
|
||||
arrow.Arrow.range('hour',
|
||||
now.shift(days=days_from_today).floor('day'),now.shift(days=days_from_today).ceil('day')
|
||||
))[::3]
|
||||
|
||||
# Get forecasts for each time-object
|
||||
forecasts = [forecast.get_weather_at(_.datetime) for _ in time_range]
|
||||
forecasts = [_ for _ in forecast if arrow.get(_["dt"]) in time_range]
|
||||
|
||||
# Get all temperatures for this day
|
||||
daily_temp = [round(_.temperature(unit=temp_unit)['temp'],
|
||||
ndigits=dec_temp) for _ in forecasts]
|
||||
daily_temp = [round(_["main"]["temp"]) for _ in forecasts]
|
||||
# Calculate min. and max. temp for this day
|
||||
temp_range = f'{max(daily_temp)}°/{min(daily_temp)}°'
|
||||
temp_range = f'{min(daily_temp)}°/{max(daily_temp)}°'
|
||||
|
||||
# Get all weather icon codes for this day
|
||||
daily_icons = [_.weather_icon_name for _ in forecasts]
|
||||
daily_icons = [_["weather"][0]["icon"] for _ in forecasts]
|
||||
# Find most common element from all weather icon codes
|
||||
status = max(set(daily_icons), key=daily_icons.count)
|
||||
|
||||
weekday = now.shift(days=days_from_today).format('ddd', locale=
|
||||
self.locale)
|
||||
weekday = now.shift(days=days_from_today).format('ddd', locale=self.locale)
|
||||
return {'temp': temp_range, 'icon': status, 'stamp': weekday}
|
||||
|
||||
forecasts = [calculate_forecast(days) for days in range(1, 5)]
|
||||
|
||||
fc_data = {}
|
||||
for forecast in forecasts:
|
||||
fc_data['fc' + str(forecasts.index(forecast) + 1)] = {
|
||||
'temp': forecast['temp'],
|
||||
@ -455,13 +490,15 @@ class Weather(inkycal_module):
|
||||
logger.debug((key, val))
|
||||
|
||||
# Get some current weather details
|
||||
temperature = '{}°'.format(round(
|
||||
weather.temperature(unit=temp_unit)['temp'], ndigits=dec_temp))
|
||||
if dec_temp != 0:
|
||||
temperature = f"{round(weather['main']['temp'])}°"
|
||||
else:
|
||||
temperature = f"{round(weather['main']['temp'],ndigits=dec_temp)}°"
|
||||
|
||||
weather_icon = weather.weather_icon_name
|
||||
humidity = str(weather.humidity)
|
||||
sunrise_raw = arrow.get(weather.sunrise_time()).to(self.timezone)
|
||||
sunset_raw = arrow.get(weather.sunset_time()).to(self.timezone)
|
||||
weather_icon = weather["weather"][0]["icon"]
|
||||
humidity = str(weather["main"]["humidity"])
|
||||
sunrise_raw = arrow.get(weather["sys"]["sunrise"]).to(self.timezone)
|
||||
sunset_raw = arrow.get(weather["sys"]["sunset"]).to(self.timezone)
|
||||
|
||||
logger.debug(f'weather_icon: {weather_icon}')
|
||||
|
||||
@ -469,33 +506,29 @@ class Weather(inkycal_module):
|
||||
logger.debug('using 12 hour format for sunrise/sunset')
|
||||
sunrise = sunrise_raw.format('h:mm a')
|
||||
sunset = sunset_raw.format('h:mm a')
|
||||
|
||||
elif self.hour_format == 24:
|
||||
else:
|
||||
# 24 hours format
|
||||
logger.debug('using 24 hour format for sunrise/sunset')
|
||||
sunrise = sunrise_raw.format('H:mm')
|
||||
sunset = sunset_raw.format('H:mm')
|
||||
|
||||
# Format the windspeed to user preference
|
||||
# Format the wind-speed to user preference
|
||||
if self.use_beaufort:
|
||||
logger.debug("using beaufort for wind")
|
||||
wind = str(weather.wind(unit='beaufort')['speed'])
|
||||
|
||||
wind = str(self.mps_to_beaufort(weather["wind"]["speed"]))
|
||||
else:
|
||||
|
||||
if self.units == 'metric':
|
||||
logging.debug('getting windspeed in metric unit')
|
||||
wind = str(weather.wind(unit='meters_sec')['speed']) + 'm/s'
|
||||
logging.debug('getting wind speed in meters per second')
|
||||
wind = f"{weather['wind']['speed']} m/s"
|
||||
else:
|
||||
logging.debug('getting wind speed in imperial unit')
|
||||
wind = f"{self.mps_to_mph(weather['wind']['speed'])} miles/h"
|
||||
|
||||
elif self.units == 'imperial':
|
||||
logging.debug('getting windspeed in imperial unit')
|
||||
wind = str(weather.wind(unit='miles_hour')['speed']) + 'miles/h'
|
||||
|
||||
dec = decimal.Decimal
|
||||
moonphase = get_moon_phase()
|
||||
moon_phase = get_moon_phase()
|
||||
|
||||
# Fill weather details in col 1 (current weather icon)
|
||||
draw_icon(im_colour, weather_icon_pos, (col_width, im_height),
|
||||
weathericons[weather_icon])
|
||||
weather_icons[weather_icon])
|
||||
|
||||
# Fill weather details in col 2 (temp, humidity, wind)
|
||||
draw_icon(im_colour, temperature_icon_pos, (icon_small, row_height),
|
||||
@ -521,7 +554,7 @@ class Weather(inkycal_module):
|
||||
wind, font=self.font)
|
||||
|
||||
# Fill weather details in col 3 (moonphase, sunrise, sunset)
|
||||
draw_icon(im_colour, moonphase_pos, (col_width, row_height), moonphase)
|
||||
draw_icon(im_colour, moonphase_pos, (col_width, row_height), moon_phase)
|
||||
|
||||
draw_icon(im_colour, sunrise_icon_pos, (icon_small, icon_small), '\uf051')
|
||||
write(im_black, sunrise_time_pos, (col_width - icon_small, row_height),
|
||||
@ -535,7 +568,7 @@ class Weather(inkycal_module):
|
||||
for pos in range(1, len(fc_data) + 1):
|
||||
stamp = fc_data[f'fc{pos}']['stamp']
|
||||
|
||||
icon = weathericons[fc_data[f'fc{pos}']['icon']]
|
||||
icon = weather_icons[fc_data[f'fc{pos}']['icon']]
|
||||
temp = fc_data[f'fc{pos}']['temp']
|
||||
|
||||
write(im_black, eval(f'stamp_fc{pos}'), (col_width, row_height),
|
||||
@ -548,7 +581,7 @@ class Weather(inkycal_module):
|
||||
border_h = row3 + row_height
|
||||
border_w = col_width - 3 # leave 3 pixels gap
|
||||
|
||||
# Add borders around each sub-section
|
||||
# Add borders around each subsection
|
||||
draw_border(im_black, (col1, row1), (col_width * 3 - 3, border_h),
|
||||
shrinkage=(0, 0))
|
||||
|
||||
|
@ -1 +1 @@
|
||||
from config import Config
|
||||
from .config import Config
|
||||
|
@ -57,8 +57,6 @@ class module_test(unittest.TestCase):
|
||||
print('OK')
|
||||
if Config.USE_PREVIEW:
|
||||
preview(merge(im_black, im_colour))
|
||||
im = merge(im_black, im_colour)
|
||||
im.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
@ -112,8 +112,6 @@ class TestTextToDisplay(unittest.TestCase):
|
||||
print('OK')
|
||||
if Config.USE_PREVIEW:
|
||||
preview(merge(im_black, im_colour))
|
||||
im = merge(im_black, im_colour)
|
||||
im.show()
|
||||
|
||||
if delete_file_after_parse:
|
||||
print("cleaning up temp file")
|
||||
|
@ -46,7 +46,6 @@ class module_test(unittest.TestCase):
|
||||
print('OK')
|
||||
if Config.USE_PREVIEW:
|
||||
preview(merge(im_black, im_colour))
|
||||
merge(im_black, im_colour).show()
|
||||
else:
|
||||
print('No api key given, omitting test')
|
||||
|
@ -13,7 +13,7 @@ preview = Inkyimage.preview
|
||||
merge = Inkyimage.merge
|
||||
|
||||
owm_api_key = Config.OPENWEATHERMAP_API_KEY
|
||||
location = 'Stuttgart, DE'
|
||||
location = '2825297'
|
||||
|
||||
tests = [
|
||||
{
|
||||
@ -184,7 +184,8 @@ class module_test(unittest.TestCase):
|
||||
im_black, im_colour = module.generate_image()
|
||||
print('OK')
|
||||
if Config.USE_PREVIEW:
|
||||
preview(merge(im_black, im_colour))
|
||||
merged = merge(im_black, im_colour)
|
||||
preview(merged)
|
||||
|
||||
|
||||
|
@ -1,30 +1,28 @@
|
||||
arrow==1.2.3
|
||||
arrow==1.3.0
|
||||
certifi==2023.7.22
|
||||
cycler==0.11.0
|
||||
cycler==0.12.1
|
||||
feedparser==6.0.10
|
||||
fonttools==4.40.0
|
||||
geojson==2.3.0
|
||||
icalendar==5.0.7
|
||||
kiwisolver==1.4.4
|
||||
lxml==4.9.2
|
||||
matplotlib==3.7.1
|
||||
fonttools==4.44.0
|
||||
icalendar==5.0.11
|
||||
kiwisolver==1.4.5
|
||||
lxml==4.9.3
|
||||
matplotlib==3.8.1
|
||||
multitasking==0.0.11
|
||||
numpy==1.25.0
|
||||
packaging==23.1
|
||||
pandas==2.0.2
|
||||
Pillow==9.5.0
|
||||
pyowm==3.3.0
|
||||
pyparsing==3.1.0
|
||||
numpy==1.26.1
|
||||
packaging==23.2
|
||||
pandas==2.1.2
|
||||
Pillow==10.1.0
|
||||
pyparsing==3.1.1
|
||||
PySocks==1.7.1
|
||||
python-dateutil==2.8.2
|
||||
pytz==2023.3
|
||||
recurring-ical-events==2.0.2
|
||||
pytz==2023.3.post1
|
||||
recurring-ical-events==2.1.0
|
||||
requests==2.31.0
|
||||
sgmllib3k==1.0.0
|
||||
six==1.16.0
|
||||
todoist-api-python==2.0.2
|
||||
typing_extensions==4.6.3
|
||||
todoist-api-python==2.1.3
|
||||
typing_extensions==4.8.0
|
||||
urllib3==2.0.7
|
||||
yfinance==0.2.21
|
||||
yfinance==0.2.31
|
||||
python-dotenv==1.0.0
|
||||
setuptools==68.0.0
|
||||
setuptools==68.2.2
|
||||
|
Loading…
Reference in New Issue
Block a user