fix timezone handling for weather modules
This commit is contained in:
		| @@ -1,3 +1,9 @@ | ||||
| """ | ||||
| Inkycal opwenweather API abstraction | ||||
| - retrieves free weather data from OWM 2.5 API endpoints (given provided API key) | ||||
| - handles unit, language and timezone conversions | ||||
| - provides ready-to-use current weather, hourly and daily forecasts | ||||
| """ | ||||
| import json | ||||
| import logging | ||||
| from datetime import datetime | ||||
| @@ -9,13 +15,11 @@ from typing import Literal | ||||
| import requests | ||||
| from dateutil import tz | ||||
|  | ||||
| from inkycal.custom.functions import get_system_tz | ||||
|  | ||||
| TEMP_UNITS = Literal["celsius", "fahrenheit"] | ||||
| WIND_UNITS = Literal["meters_sec", "km_hour", "miles_hour", "knots", "beaufort"] | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
| logger.setLevel("DEBUG") | ||||
| logger.setLevel(level=logging.INFO) | ||||
|  | ||||
|  | ||||
| def is_timestamp_within_range(timestamp: datetime, start_time: datetime, end_time: datetime) -> bool: | ||||
| @@ -31,6 +35,7 @@ class OpenWeatherMap: | ||||
|         temp_unit: TEMP_UNITS = "celsius", | ||||
|         wind_unit: WIND_UNITS = "meters_sec", | ||||
|         language: str = "en", | ||||
|         tz_name: str = "UTC" | ||||
|     ) -> None: | ||||
|         self.api_key = api_key | ||||
|         self.city_id = city_id | ||||
| @@ -39,7 +44,8 @@ class OpenWeatherMap: | ||||
|         self.language = language | ||||
|         self._api_version = "2.5" | ||||
|         self._base_url = f"https://api.openweathermap.org/data/{self._api_version}" | ||||
|         self.tz_zone = tz.gettz(get_system_tz()) | ||||
|         self.tz_zone = tz.gettz(tz_name) | ||||
|         logger.info(f"OWM wrapper initialized for city id {self.city_id}, language {self.language} and timezone {tz_name}.") | ||||
|  | ||||
|     def get_current_weather(self) -> Dict: | ||||
|         """ | ||||
| @@ -57,7 +63,7 @@ class OpenWeatherMap: | ||||
|                 f"Failure getting the current weather: code {response.status_code}. Reason: {response.text}" | ||||
|             ) | ||||
|         current_data = json.loads(response.text) | ||||
|  | ||||
|          | ||||
|         current_weather = {} | ||||
|         current_weather["detailed_status"] = current_data["weather"][0]["description"] | ||||
|         current_weather["weather_icon_name"] = current_data["weather"][0]["icon"] | ||||
| @@ -71,13 +77,17 @@ class OpenWeatherMap: | ||||
|         current_weather["wind"] = self.get_converted_windspeed( | ||||
|             current_data["wind"]["speed"] | ||||
|         )  # OWM Unit Default: meter/sec, Metric: meter/sec | ||||
|         current_weather["wind_gust"] = self.get_converted_windspeed(current_data["wind"]["gust"]) | ||||
|         if "gust" in current_data["wind"]: | ||||
|             current_weather["wind_gust"] = self.get_converted_windspeed(current_data["wind"]["gust"]) | ||||
|         else: | ||||
|             logger.info(f"OpenWeatherMap response did not contain a wind gust speed. Using base wind: {current_weather['wind']} m/s.") | ||||
|             current_weather["wind_gust"] = current_weather["wind"] | ||||
|         current_weather["uvi"] = None  # TODO: this is no longer supported with 2.5 API, find alternative | ||||
|         current_weather["sunrise"] = current_data["sys"]["sunrise"]  # unix timestamp | ||||
|         current_weather["sunset"] = current_data["sys"]["sunset"] | ||||
|         current_weather["sunrise"] = datetime.fromtimestamp(current_data["sys"]["sunrise"], tz=self.tz_zone)  # unix timestamp -> to our timezone | ||||
|         current_weather["sunset"] = datetime.fromtimestamp(current_data["sys"]["sunset"], tz=self.tz_zone) | ||||
|  | ||||
|         self.current_weather = current_weather | ||||
|          | ||||
|  | ||||
|         return current_weather | ||||
|  | ||||
|     def get_weather_forecast(self) -> List[Dict]: | ||||
| @@ -136,7 +146,8 @@ class OpenWeatherMap: | ||||
|     def get_forecast_for_day(self, days_from_today: int) -> Dict: | ||||
|         """ | ||||
|         Get temperature range, rain and most frequent icon code | ||||
|         for the day that is days_from_today away | ||||
|         for the day that is days_from_today away. | ||||
|         "Today" is based on our local system timezone. | ||||
|         :param days_from_today: | ||||
|             should be int from 0-4: e.g. 2 -> 2 days from today | ||||
|         :return: | ||||
| @@ -146,7 +157,7 @@ class OpenWeatherMap: | ||||
|         _ = self.get_weather_forecast() | ||||
|          | ||||
|         # Calculate the start and end times for the specified number of days from now | ||||
|         current_time = datetime.now() | ||||
|         current_time = datetime.now(tz=self.tz_zone) | ||||
|         start_time = ( | ||||
|             (current_time + timedelta(days=days_from_today)) | ||||
|             .replace(hour=0, minute=0, second=0, microsecond=0) | ||||
| @@ -178,7 +189,7 @@ class OpenWeatherMap: | ||||
|  | ||||
|         # Return a dict with that day's data | ||||
|         day_data = { | ||||
|             "datetime": start_time.timestamp(), | ||||
|             "datetime": start_time, | ||||
|             "icon": icon, | ||||
|             "temp_min": min(temps), | ||||
|             "temp_max": max(temps), | ||||
| @@ -277,7 +288,7 @@ def main(): | ||||
|     key = "" | ||||
|     city = 2643743 | ||||
|     lang = "de" | ||||
|     owm = OpenWeatherMap(api_key=key, city_id=city, language=lang) | ||||
|     owm = OpenWeatherMap(api_key=key, city_id=city, language=lang, tz="Europe/Berlin") | ||||
|  | ||||
|     current_weather = owm.get_current_weather() | ||||
|     print(current_weather) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 mrbwburns
					mrbwburns