Update inkycal_weather.py
This commit is contained in:
		| @@ -3,8 +3,10 @@ | |||||||
| """ | """ | ||||||
| Weather module for Inky-Calendar software. In development... | Weather module for Inky-Calendar software. In development... | ||||||
|  |  | ||||||
| To-do: | The lunar phase calculation algorithm was taken from Michael Bishop | ||||||
| - make locations of icons and text dynamic | from Github after being granted permission. Thanks, Michael Bishop for your | ||||||
|  | awesome code! | ||||||
|  |  | ||||||
| Copyright by aceisace | Copyright by aceisace | ||||||
| """ | """ | ||||||
| from __future__ import print_function | from __future__ import print_function | ||||||
| @@ -13,6 +15,8 @@ from settings import * | |||||||
| from configuration import * | from configuration import * | ||||||
| from PIL import Image, ImageDraw, ImageFont | from PIL import Image, ImageDraw, ImageFont | ||||||
| import arrow | import arrow | ||||||
|  | import math, decimal | ||||||
|  | dec = decimal.Decimal | ||||||
|  |  | ||||||
| print('Initialising weather...', end=' ') | print('Initialising weather...', end=' ') | ||||||
| owm = pyowm.OWM(api_key, language=language) | owm = pyowm.OWM(api_key, language=language) | ||||||
| @@ -29,131 +33,259 @@ weathericons = { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| """Split top_section into to 2 rows""" | """Add a border to increase readability""" | ||||||
| section_height = top_section_height // 2 | border_top = int(top_section_height * 0.05) | ||||||
| section_width = (top_section_width - top_section_height) // 3 | border_left = int(top_section_width * 0.02) | ||||||
|  |  | ||||||
| """Allocate icon sizes""" | """Calculate size for each weather sub-section""" | ||||||
| icon_small = section_height | row_height = (top_section_height-(border_top*2)) // 3 | ||||||
| icon_large = top_section_height | coloumn_width = (top_section_width-(border_left*2)) // 7 | ||||||
|  |  | ||||||
| """Split top section into 4 coloums""" | """Calculate paddings""" | ||||||
| section1 = 0 | x_padding = int( (top_section_width % coloumn_width) / 2 ) | ||||||
| section2 = icon_large + (top_section_width - icon_large) // 3 * 0 | y_padding = int( (top_section_height % row_height) / 2 ) | ||||||
| section3 = icon_large + (top_section_width - icon_large) // 3 * 1 |  | ||||||
| section4 = icon_large + (top_section_width - icon_large) // 3 * 2 |  | ||||||
|  |  | ||||||
| """Allocate positions for icons""" | """Allocate sizes for weather icons""" | ||||||
| weather_icon_pos = (section1, 0) | icon_small = row_height | ||||||
| wind_icon_pos = (section2, 0) | icon_medium = row_height * 2 | ||||||
| sun_icon_pos = (section3, 0) |  | ||||||
| temperature_icon_pos = (section4, 0) |  | ||||||
| weather_description_pos = (section2, section_height) |  | ||||||
| humidity_icon_pos = (section4, section_height) |  | ||||||
|  |  | ||||||
| """Allocate positions for text""" | """Calculate the x-axis position of each coloumn""" | ||||||
| next_to = lambda x: (x[0]+ icon_small, x[1]) | coloumn1 = x_padding | ||||||
| icon_offset = section_width - icon_small | coloumn2 = coloumn1 + coloumn_width | ||||||
|  | coloumn3 = coloumn2 + coloumn_width | ||||||
|  | coloumn4 = coloumn3 + coloumn_width | ||||||
|  | coloumn5 = coloumn4 + coloumn_width | ||||||
|  | coloumn6 = coloumn5 + coloumn_width | ||||||
|  | coloumn7 = coloumn6 + coloumn_width | ||||||
|  |  | ||||||
| wind_pos = next_to(wind_icon_pos) | """Calculate the y-axis position of each row""" | ||||||
| temperature_pos = next_to(temperature_icon_pos) | row1 = y_padding | ||||||
| sun_pos = next_to(sun_icon_pos) | row2 = row1 + row_height | ||||||
| humidity_pos = next_to(humidity_icon_pos) | row3 = row2 + row_height | ||||||
| weather_pos = (section2, section_height) |  | ||||||
|  |  | ||||||
|  | """Allocate positions for current weather details""" | ||||||
|  | text_now_pos = (coloumn1, row1) | ||||||
|  | weather_icon_now_pos = (coloumn1, row2) | ||||||
|  |  | ||||||
|  | temperature_icon_now_pos = (coloumn2, row1) | ||||||
|  | temperature_now_pos = (coloumn2+icon_small, row1) | ||||||
|  | humidity_icon_now_pos = (coloumn2, row2) | ||||||
|  | humidity_now_pos = (coloumn2+icon_small, row2) | ||||||
|  | windspeed_icon_now_pos = (coloumn2, row3) | ||||||
|  | windspeed_now_pos = (coloumn2+icon_small, row3) | ||||||
|  |  | ||||||
|  | moon_phase_now_pos = (coloumn3, row1) | ||||||
|  | sunrise_icon_now_pos = (coloumn3, row2) | ||||||
|  | sunrise_time_now_pos = (coloumn3+icon_small, row2) | ||||||
|  | sunset_icon_now_pos = (coloumn3, row3) | ||||||
|  | sunset_time_now_pos = (coloumn3+ icon_small, row3) | ||||||
|  |  | ||||||
|  | """Allocate positions for weather forecast after 3 hours""" | ||||||
|  | text_3h_pos = (coloumn4, row1) | ||||||
|  | icon_3h_pos = (coloumn4, row2) | ||||||
|  | temperature_3h_pos = (coloumn4, row3) | ||||||
|  |  | ||||||
|  | """Allocate positions for weather forecast after 6 hours""" | ||||||
|  | text_6h_pos = (coloumn5, row1) | ||||||
|  | icon_6h_pos = (coloumn5, row2) | ||||||
|  | temperature_6h_pos = (coloumn5, row3) | ||||||
|  |  | ||||||
|  | """Allocate positions for weather forecast after 9 hours""" | ||||||
|  | text_9h_pos = (coloumn6, row1) | ||||||
|  | icon_9h_pos = (coloumn6, row2) | ||||||
|  | temperature_9h_pos = (coloumn6, row3) | ||||||
|  |  | ||||||
|  | """Allocate positions for weather forecast after 12 hours""" | ||||||
|  | text_12h_pos = (coloumn7, row1) | ||||||
|  | icon_12h_pos = (coloumn7, row2) | ||||||
|  | temperature_12h_pos = (coloumn7, row3) | ||||||
|  |  | ||||||
|  | """Windspeed (m/s) to beaufort (index of list) conversion""" | ||||||
|  | windspeed_to_beaufort = [0.02, 1.5, 3.3, 5.4, 7.9, 10.7, 13.8, 17.1, 20.7, | ||||||
|  |   24.4, 28.4, 32.6, 100] | ||||||
|  |  | ||||||
|  | """Function to convert tempertures from kelvin to celcius or fahrenheit""" | ||||||
|  | def to_units(kelvin): | ||||||
|  |   if units == 'metric': | ||||||
|  |     conversion =  str(int(kelvin - 273.15)) + '°C' | ||||||
|  |   else: | ||||||
|  |     conversion = str(int((kelvin - 273.15) * 9/5 + 32)) + 'F' | ||||||
|  |   return conversion | ||||||
|  |  | ||||||
|  | """Function to convert time objects to specified format 12/24 hours""" | ||||||
|  | """Simple means just the hour and if 12 hours, am/pm as well""" | ||||||
|  | def to_hours(datetime_object, simple = False): | ||||||
|  |   if hours == '24': | ||||||
|  |     if simple == True: | ||||||
|  |       converted_time = datetime_object.format('H') | ||||||
|  |     else: | ||||||
|  |       converted_time = datetime_object.format('HH:mm') | ||||||
|  |   else: | ||||||
|  |     if simple == True: | ||||||
|  |       converted_time = datetime_object.format('H a') | ||||||
|  |     else: | ||||||
|  |       converted_time = datetime_object.format('hh:mm') | ||||||
|  |   return str(converted_time) | ||||||
|  |  | ||||||
| #def main(): | #def main(): | ||||||
|  | try: | ||||||
|  |   """Connect to Openweathermap API and fetch weather data""" | ||||||
|  |   if top_section == "Weather" and api_key != "" and owm.is_API_online() is True: | ||||||
|  |     #try: | ||||||
|  |     print("Fetching weather data from openweathermap API...",end = ' ') | ||||||
|  |     current_weather_setup = owm.weather_at_place(location) | ||||||
|  |     weather = current_weather_setup.get_weather() | ||||||
|  |  | ||||||
| """Connect to Openweathermap API and fetch weather data""" |     """Set-up and get weather forecast data""" | ||||||
| if top_section == "Weather" and api_key != "" and owm.is_API_online() is True: |     forecast = owm.three_hours_forecast(location) | ||||||
|   try: |  | ||||||
|     print("Fetching weather data from openweathermap...",end = ' ') |  | ||||||
|     observation = owm.weather_at_place(location) |  | ||||||
|     print("Done") |     print("Done") | ||||||
|  |  | ||||||
|     weather = observation.get_weather() |     """Round the hour to the nearest multiple of 3""" | ||||||
|     weathericon = weather.get_weather_icon_name() |  | ||||||
|     Humidity = str(weather.get_humidity()) |  | ||||||
|     cloudstatus = str(weather.get_clouds()) |  | ||||||
|     weather_description = (str(weather.get_detailed_status())) |  | ||||||
|  |  | ||||||
|     """Add the icons at the correct positions""" |  | ||||||
|     print('Adding weather info and icons to the image...', end = ' ') |  | ||||||
|     write_text(icon_small, icon_small, '\uf055', temperature_icon_pos, |  | ||||||
|       font = w_font, adapt_fontsize = True) # Temperature icon |  | ||||||
|  |  | ||||||
|     write_text(icon_large, icon_large, weathericons[weathericon], |  | ||||||
|       weather_icon_pos, font = w_font, adapt_fontsize = True) # Weather icon |  | ||||||
|  |  | ||||||
|     write_text(icon_small, icon_small, '\uf07a', humidity_icon_pos, font = w_font, |  | ||||||
|       adapt_fontsize = True) #Humidity icon |  | ||||||
|  |  | ||||||
|     write_text(icon_small,icon_small, '\uf050', wind_icon_pos, font = w_font, |  | ||||||
|       adapt_fontsize = True) #Wind icon |  | ||||||
|  |  | ||||||
|     """Format and write the temperature and windspeed""" |  | ||||||
|     if units == "metric": |  | ||||||
|       Temperature = str(int(weather.get_temperature(unit='celsius')['temp'])) |  | ||||||
|       windspeed = str(int(weather.get_wind()['speed'])) |  | ||||||
|  |  | ||||||
|       write_text(icon_offset, section_height, Temperature+'°C', temperature_pos) |  | ||||||
|  |  | ||||||
|       write_text(icon_offset,section_height, windspeed+" km/h", wind_pos) |  | ||||||
|  |  | ||||||
|     else: |  | ||||||
|       Temperature = str(int(weather.get_temperature('fahrenheit')['temp'])) |  | ||||||
|       windspeed = str(int(weather.get_wind()['speed']*0.621)) |  | ||||||
|  |  | ||||||
|       write_text(icon_offset, section_height, Temperature+' F', temperature_pos) |  | ||||||
|  |  | ||||||
|       write_text(icon_offset,section_height, windspeed+" mph", wind_pos) |  | ||||||
|  |  | ||||||
|     """write the humidity at the given position""" |  | ||||||
|     write_text(icon_offset, section_height, Humidity+'%', humidity_pos) |  | ||||||
|  |  | ||||||
|     now = arrow.now(tz=get_tz()) |     now = arrow.now(tz=get_tz()) | ||||||
|     sunrise = arrow.get(weather.get_sunrise_time()).to(get_tz()) |     hour_gap = (now.hour % 3) | ||||||
|     sunset = arrow.get(weather.get_sunset_time()).to(get_tz()) |  | ||||||
|  |  | ||||||
|     """Add the sunrise/sunset icon and display the time""" |     """Prepare timings for forecasts""" | ||||||
|     if (now <= sunrise and now <= sunset) or (now >= sunrise and now >= sunset): |     in_3h = now.replace(hours = + hour_gap + 3) | ||||||
|       write_text(icon_small, icon_small, '\uf051', sun_icon_pos, font = w_font, |     in_6h = now.replace(hours = + hour_gap + 6) | ||||||
|                  adapt_fontsize = True) |     in_9h = now.replace(hours = + hour_gap + 9) | ||||||
|       if hours == "24": |     in_12h = now.replace(hours = + hour_gap + 12) | ||||||
|         write_text(icon_offset, section_height, sunrise.format('H:mm'), sun_pos) |  | ||||||
|       else: |     """Prepare forecast objects for the specified timings""" | ||||||
|         write_text(icon_offset, section_height, sunrise.format('h:mm'), sun_pos) |     forecast_3h = forecast.get_weather_at(in_3h.datetime) | ||||||
|     else: |     forecast_6h = forecast.get_weather_at(in_6h.datetime) | ||||||
|       write_text(icon_small, '\uf052', sun_icon_pos, font = w_font, |     forecast_9h = forecast.get_weather_at(in_9h.datetime) | ||||||
|                  adapt_fontsize = True) |     forecast_12h = forecast.get_weather_at(in_12h.datetime) | ||||||
|       if hours == "24": |  | ||||||
|         write_text(icon_offset, section_height, sunset.format('H:mm'), sun_pos) |     """Get the current temperature and forcasts temperatures""" | ||||||
|       else: |     temperature_now = to_units(weather.get_temperature()['temp']) | ||||||
|         write_text(icon_offset, section_height, sunset.format('h:mm'), sun_pos) |     temperature_3h = to_units(forecast_3h.get_temperature()['temp']) | ||||||
|  |     temperature_6h = to_units(forecast_6h.get_temperature()['temp']) | ||||||
|  |     temperature_9h = to_units(forecast_9h.get_temperature()['temp']) | ||||||
|  |     temperature_12h = to_units(forecast_12h.get_temperature()['temp']) | ||||||
|  |  | ||||||
|  |     """Get current and forecast weather icon names""" | ||||||
|  |     weather_icon_now = weather.get_weather_icon_name() | ||||||
|  |     weather_icon_3h = forecast_3h.get_weather_icon_name() | ||||||
|  |     weather_icon_6h = forecast_6h.get_weather_icon_name() | ||||||
|  |     weather_icon_9h = forecast_9h.get_weather_icon_name() | ||||||
|  |     weather_icon_12h = forecast_12h.get_weather_icon_name() | ||||||
|  |  | ||||||
|  |     """Parse current weather details""" | ||||||
|  |     sunrise_time_now = arrow.get(weather.get_sunrise_time()).to(get_tz()) | ||||||
|  |     sunset_time_now = arrow.get(weather.get_sunset_time()).to(get_tz()) | ||||||
|  |     humidity_now = str(weather.get_humidity()) | ||||||
|  |     cloudstatus_now = str(weather.get_clouds()) | ||||||
|  |     weather_description_now = str(weather.get_detailed_status()) | ||||||
|  |     windspeed_now = weather.get_wind(unit='meters_sec')['speed'] | ||||||
|  |  | ||||||
|  |     beaufort = str([windspeed_to_beaufort.index(_) for _ in windspeed_to_beaufort | ||||||
|  |       if windspeed_now < _][0]) | ||||||
|  |  | ||||||
|  |  | ||||||
|     """Add a short weather description""" |     """Calculate the moon phase""" | ||||||
|     write_text(section2+section3-icon_offset, section_height, |     def get_moon_phase(): | ||||||
|                weather_description, weather_pos) |      diff = now - arrow.get(2001, 1, 1) | ||||||
|  |      days = dec(diff.days) + (dec(diff.seconds) / dec(86400)) | ||||||
|  |      lunations = dec("0.20439731") + (days * dec("0.03386319269")) | ||||||
|  |      position = lunations % dec(1) | ||||||
|  |      index = math.floor((position * dec(8)) + dec("0.5")) | ||||||
|  |      return {0: '\uf095',1: '\uf099',2: '\uf09c',3: '\uf0a0', | ||||||
|  |              4: '\uf0a3',5: '\uf0a7',6: '\uf0aa',7: '\uf0ae' }[int(index) & 7] | ||||||
|  |  | ||||||
|  |     moonphase = get_moon_phase() | ||||||
|  |  | ||||||
|  |     print('Adding weather details on the image...', end = ' ') | ||||||
|  |  | ||||||
|  |     """Add weather details in column 1""" | ||||||
|  |     write_text(coloumn_width, row_height, 'now', text_now_pos) | ||||||
|  |     write_text(icon_medium, icon_medium, weathericons[weather_icon_now], | ||||||
|  |       weather_icon_now_pos, font = w_font, adapt_fontsize = True) | ||||||
|  |  | ||||||
|  |     """Add weather details in column 2""" | ||||||
|  |     write_text(icon_small, icon_small, '\uf053', temperature_icon_now_pos, | ||||||
|  |       font = w_font, adapt_fontsize = True) | ||||||
|  |     write_text(icon_small, icon_small, '\uf07a', humidity_icon_now_pos, | ||||||
|  |       font = w_font, adapt_fontsize = True) | ||||||
|  |     write_text(icon_small, icon_small, '\uf050', windspeed_icon_now_pos, | ||||||
|  |       font = w_font, adapt_fontsize = True) | ||||||
|  |  | ||||||
|  |     write_text(coloumn_width-icon_small, row_height, | ||||||
|  |       temperature_now, temperature_now_pos) | ||||||
|  |     write_text(coloumn_width-icon_small, row_height, humidity_now+'%', | ||||||
|  |       humidity_now_pos) | ||||||
|  |     write_text(coloumn_width-icon_small, row_height, beaufort, | ||||||
|  |       windspeed_now_pos) | ||||||
|  |  | ||||||
|  |     """Add weather details in column 3""" | ||||||
|  |     write_text(coloumn_width, row_height, moonphase , moon_phase_now_pos, | ||||||
|  |       font = w_font, adapt_fontsize = True) | ||||||
|  |     write_text(icon_small, icon_small, '\uf051', sunrise_icon_now_pos, | ||||||
|  |       font = w_font, adapt_fontsize = True) | ||||||
|  |     write_text(icon_small, icon_small, '\uf052', sunset_icon_now_pos, | ||||||
|  |       font = w_font, adapt_fontsize = True) | ||||||
|  |  | ||||||
|  |     write_text(coloumn_width-icon_small, row_height, to_hours(sunrise_time_now), | ||||||
|  |       sunrise_time_now_pos) | ||||||
|  |     write_text(coloumn_width-icon_small, row_height, to_hours(sunset_time_now), | ||||||
|  |       sunset_time_now_pos) | ||||||
|  |  | ||||||
|  |     """Add weather details in column 4""" | ||||||
|  |     write_text(coloumn_width, row_height, to_hours(in_3h, simple=True), | ||||||
|  |       text_3h_pos) | ||||||
|  |     write_text(coloumn_width, row_height, weathericons[weather_icon_3h], | ||||||
|  |       icon_3h_pos, font = w_font, adapt_fontsize = True) | ||||||
|  |     write_text(coloumn_width, row_height, temperature_3h, | ||||||
|  |         temperature_3h_pos) | ||||||
|  |  | ||||||
|  |     """Add weather details in column 5""" | ||||||
|  |     write_text(coloumn_width, row_height, to_hours(in_6h, simple=True), | ||||||
|  |       text_6h_pos) | ||||||
|  |     write_text(coloumn_width, row_height, weathericons[weather_icon_6h], | ||||||
|  |       icon_6h_pos, font = w_font, adapt_fontsize = True) | ||||||
|  |     write_text(coloumn_width, row_height, temperature_6h, | ||||||
|  |         temperature_6h_pos) | ||||||
|  |  | ||||||
|  |     """Add weather details in column 6""" | ||||||
|  |     write_text(coloumn_width, row_height, to_hours(in_9h, simple=True), | ||||||
|  |       text_9h_pos) | ||||||
|  |     write_text(coloumn_width, row_height, weathericons[weather_icon_9h], | ||||||
|  |       icon_9h_pos, font = w_font, adapt_fontsize = True) | ||||||
|  |     write_text(coloumn_width, row_height, temperature_9h, | ||||||
|  |         temperature_9h_pos) | ||||||
|  |  | ||||||
|  |     """Add weather details in column 7""" | ||||||
|  |     write_text(coloumn_width, row_height, to_hours(in_12h, simple=True), | ||||||
|  |       text_12h_pos) | ||||||
|  |     write_text(coloumn_width, row_height, weathericons[weather_icon_12h], | ||||||
|  |       icon_12h_pos, font = w_font, adapt_fontsize = True) | ||||||
|  |     write_text(coloumn_width, row_height, temperature_12h, | ||||||
|  |         temperature_12h_pos) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     """Add seperators between section4 and section7""" | ||||||
|  |     draw = ImageDraw.Draw(image) | ||||||
|  |     line_start_y = int(top_section_height*0.1) | ||||||
|  |     line_end_y = int(top_section_height*0.9) | ||||||
|  |  | ||||||
|  |     draw.line((coloumn4, line_start_y, coloumn4, line_end_y), fill='black') | ||||||
|  |     draw.line((coloumn5, line_start_y, coloumn5, line_end_y), fill='black') | ||||||
|  |     draw.line((coloumn6, line_start_y, coloumn6, line_end_y), fill='black') | ||||||
|  |     draw.line((coloumn7, line_start_y, coloumn7, line_end_y), fill='black') | ||||||
|  |     draw.line((0, top_section_height-border_top, top_section_width-border_left, | ||||||
|  |       top_section_height-border_top), fill='red', width=3) | ||||||
|  |  | ||||||
|     print('Done'+'\n') |     print('Done'+'\n') | ||||||
|  |  | ||||||
|     """Show the fetched weather data""" |  | ||||||
|     print("Today's weather report: The current Temperature is {0}°C. The " |  | ||||||
|     "relative humidity is {1} %. The current windspeed is {2} km/h. " |  | ||||||
|     "The sunrise today was at {3}. The sunset is at {4}. The weather can " |  | ||||||
|     "be described with: {5}".format(Temperature, Humidity, windspeed, |  | ||||||
|       sunrise.format('H:mm'), sunset.format('H:mm'), weather_description)) |  | ||||||
|  |  | ||||||
|     image.crop((0,0, top_section_width, top_section_height)).save('weather.png') |     image.crop((0,0, top_section_width, top_section_height)).save('weather.png') | ||||||
|  |  | ||||||
|   except Exception as e: | except Exception as e: | ||||||
|   """If no response was received from the openweathermap |   """If no response was received from the openweathermap | ||||||
|   api server, add the cloud with question mark""" |   api server, add the cloud with question mark""" | ||||||
|   print('__________OWM-ERROR!__________') |   print('__________OWM-ERROR!__________') | ||||||
|   print('Reason: ',e) |   print('Reason: ',e) | ||||||
|     write_text(icon_large, icon_large, '\uf07b', weather_icon_pos, |   write_text(icon_medium, icon_medium, '\uf07b', weather_icon_now_pos, | ||||||
|   font = w_font, adapt_fontsize = True) |   font = w_font, adapt_fontsize = True) | ||||||
|  |   message = 'No internet connectivity or API timeout' | ||||||
|  |   write_text(coloumn_width*6, row_height, message, humidity_icon_now_pos) | ||||||
|   pass |   pass | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user