pyopenweathermap 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/PKG-INFO +5 -2
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/pyproject.toml +3 -1
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/__init__.py +2 -1
- pyopenweathermap-0.2.2/src/pyopenweathermap/air_pollution.py +21 -0
- pyopenweathermap-0.2.2/src/pyopenweathermap/client/air_pollution_client.py +42 -0
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/client/free_client.py +4 -0
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/client/onecall_client.py +7 -5
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/client/owm_abstract_client.py +5 -0
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/client/owm_client_factory.py +4 -1
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/data_converter.py +35 -1
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/LICENSE +0 -0
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/README.md +0 -0
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/exception.py +0 -0
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/http_client.py +0 -0
- {pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/weather.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: pyopenweathermap
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: lib for OpenWeatherMap for Home Assistant
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: homeassistant,owm,openweathermap
|
|
@@ -11,7 +11,10 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
15
|
Requires-Dist: aiohttp (>=3.9.5,<4.0.0)
|
|
16
|
+
Project-URL: Homepage, https://github.com/freekode/pyopenweathermap
|
|
17
|
+
Project-URL: Repository, https://github.com/freekode/pyopenweathermap
|
|
15
18
|
Description-Content-Type: text/markdown
|
|
16
19
|
|
|
17
20
|
# pyopenweathermap
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pyopenweathermap"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "lib for OpenWeatherMap for Home Assistant"
|
|
5
5
|
authors = ["Evgeny <iam@freekode.org>"]
|
|
6
6
|
license = "MIT"
|
|
7
7
|
readme = "README.md"
|
|
8
|
+
homepage = "https://github.com/freekode/pyopenweathermap"
|
|
9
|
+
repository = "https://github.com/freekode/pyopenweathermap"
|
|
8
10
|
keywords = ["homeassistant", "owm", "openweathermap"]
|
|
9
11
|
packages = [
|
|
10
12
|
{ include = "pyopenweathermap", from = "src" },
|
|
@@ -6,5 +6,6 @@ from .exception import (
|
|
|
6
6
|
from .client.owm_abstract_client import OWMClient
|
|
7
7
|
from .client.owm_client_factory import create_owm_client
|
|
8
8
|
from .weather import (
|
|
9
|
-
CurrentWeather, HourlyWeatherForecast, DailyWeatherForecast, WeatherReport, DailyTemperature, WeatherCondition
|
|
9
|
+
CurrentWeather, MinutelyWeatherForecast, HourlyWeatherForecast, DailyWeatherForecast, WeatherReport, DailyTemperature, WeatherCondition
|
|
10
10
|
)
|
|
11
|
+
from .air_pollution import CurrentAirPollution, AirPollutionReport
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import decimal
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class CurrentAirPollution:
|
|
7
|
+
date_time: datetime
|
|
8
|
+
aqi: int
|
|
9
|
+
co: decimal.Decimal
|
|
10
|
+
no: decimal.Decimal
|
|
11
|
+
no2: decimal.Decimal
|
|
12
|
+
o3: decimal.Decimal
|
|
13
|
+
so2: decimal.Decimal
|
|
14
|
+
pm2_5: decimal.Decimal
|
|
15
|
+
pm10: decimal.Decimal
|
|
16
|
+
nh3: decimal.Decimal
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class AirPollutionReport:
|
|
20
|
+
current: CurrentAirPollution | None
|
|
21
|
+
hourly_forecast: list[CurrentAirPollution]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from .owm_abstract_client import OWMClient
|
|
2
|
+
from ..data_converter import DataConverter
|
|
3
|
+
from ..exception import UnauthorizedError
|
|
4
|
+
from ..weather import WeatherReport
|
|
5
|
+
from ..air_pollution import AirPollutionReport
|
|
6
|
+
|
|
7
|
+
CURRENT_AIR_POLLUTION_API_URL = 'https://api.openweathermap.org/data/2.5/air_pollution'
|
|
8
|
+
FORECAST_API_URL = 'https://api.openweathermap.org/data/2.5/air_pollution/forecast'
|
|
9
|
+
|
|
10
|
+
class OWMAirPollutionClient(OWMClient):
|
|
11
|
+
def __init__(self, api_key):
|
|
12
|
+
super().__init__()
|
|
13
|
+
self.api_key = api_key
|
|
14
|
+
|
|
15
|
+
async def get_air_pollution(self, lat, lon) -> AirPollutionReport:
|
|
16
|
+
url = self._get_url(lat, lon, 'current')
|
|
17
|
+
current_json = await self.http_client.request(url)
|
|
18
|
+
current = DataConverter.air_pollution_current(current_json)
|
|
19
|
+
|
|
20
|
+
url = self._get_url(lat, lon, 'forecast')
|
|
21
|
+
forecast_json = await self.http_client.request(url)
|
|
22
|
+
hourly = [DataConverter.air_pollution_hourly(item) for item in forecast_json['list']]
|
|
23
|
+
|
|
24
|
+
return AirPollutionReport(current, hourly)
|
|
25
|
+
|
|
26
|
+
async def get_weather(self, lat, lon) -> WeatherReport:
|
|
27
|
+
raise NotImplementedError('Air Pollution client does not have get_weather()')
|
|
28
|
+
|
|
29
|
+
async def validate_key(self) -> bool:
|
|
30
|
+
url = self._get_url(50.06, 14.44, 'api_type')
|
|
31
|
+
try:
|
|
32
|
+
await self.http_client.request(url)
|
|
33
|
+
return True
|
|
34
|
+
except UnauthorizedError:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
def _get_url(self, lat, lon, api_type):
|
|
38
|
+
url = CURRENT_AIR_POLLUTION_API_URL if api_type == 'current' else FORECAST_API_URL
|
|
39
|
+
return (f"{url}?"
|
|
40
|
+
f"lat={lat}&"
|
|
41
|
+
f"lon={lon}&"
|
|
42
|
+
f"appid={self.api_key}")
|
{pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/client/free_client.py
RENAMED
|
@@ -2,6 +2,7 @@ from .owm_abstract_client import OWMClient
|
|
|
2
2
|
from ..data_converter import DataConverter
|
|
3
3
|
from ..exception import UnauthorizedError
|
|
4
4
|
from ..weather import WeatherReport
|
|
5
|
+
from ..air_pollution import AirPollutionReport
|
|
5
6
|
|
|
6
7
|
CURRENT_WEATHER_API_URL = 'https://api.openweathermap.org/data/2.5/weather'
|
|
7
8
|
FORECAST_API_URL = 'https://api.openweathermap.org/data/2.5/forecast'
|
|
@@ -26,6 +27,9 @@ class OWMFreeClient(OWMClient):
|
|
|
26
27
|
hourly = [DataConverter.free_to_hourly_weather_forecast(item) for item in json_response['list']]
|
|
27
28
|
return WeatherReport(current,[], hourly, [])
|
|
28
29
|
|
|
30
|
+
async def get_air_pollution(self, lat, lon) -> AirPollutionReport:
|
|
31
|
+
raise NotImplementedError('free client does not have get_air_pollution()')
|
|
32
|
+
|
|
29
33
|
async def validate_key(self) -> bool:
|
|
30
34
|
url = self._get_url(50.06, 14.44)
|
|
31
35
|
try:
|
{pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/client/onecall_client.py
RENAMED
|
@@ -2,9 +2,9 @@ from .owm_abstract_client import OWMClient
|
|
|
2
2
|
from ..data_converter import DataConverter
|
|
3
3
|
from ..exception import UnauthorizedError
|
|
4
4
|
from ..weather import WeatherReport
|
|
5
|
+
from ..air_pollution import AirPollutionReport
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
V25_API_URL = 'https://api.openweathermap.org/data/2.5/onecall'
|
|
7
|
+
API_URL = 'https://api.openweathermap.org/data/3.0/onecall'
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class OWMOneCallClient(OWMClient):
|
|
@@ -19,7 +19,7 @@ class OWMOneCallClient(OWMClient):
|
|
|
19
19
|
url = self._get_url(lat, lon)
|
|
20
20
|
json_response = await self.http_client.request(url)
|
|
21
21
|
|
|
22
|
-
current, hourly, daily = None, [], []
|
|
22
|
+
current, minutely, hourly, daily = None, [], [], []
|
|
23
23
|
if json_response.get('current') is not None:
|
|
24
24
|
current = DataConverter.onecall_to_current_weather(json_response['current'])
|
|
25
25
|
if json_response.get('minutely') is not None:
|
|
@@ -31,6 +31,9 @@ class OWMOneCallClient(OWMClient):
|
|
|
31
31
|
|
|
32
32
|
return WeatherReport(current, minutely, hourly, daily)
|
|
33
33
|
|
|
34
|
+
async def get_air_pollution(self, lat, lon) -> AirPollutionReport:
|
|
35
|
+
raise NotImplementedError('free client does not have get_air_pollution()')
|
|
36
|
+
|
|
34
37
|
async def validate_key(self) -> bool:
|
|
35
38
|
url = (f"{self._get_url(50.06, 14.44)}"
|
|
36
39
|
f"&exclude=current,minutely,hourly,daily,alerts)")
|
|
@@ -41,8 +44,7 @@ class OWMOneCallClient(OWMClient):
|
|
|
41
44
|
return False
|
|
42
45
|
|
|
43
46
|
def _get_url(self, lat, lon):
|
|
44
|
-
|
|
45
|
-
return (f"{url}?"
|
|
47
|
+
return (f"{API_URL}?"
|
|
46
48
|
f"lat={lat}&"
|
|
47
49
|
f"lon={lon}&"
|
|
48
50
|
f"appid={self.api_key}&"
|
{pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/client/owm_abstract_client.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from ..weather import WeatherReport
|
|
3
|
+
from ..air_pollution import AirPollutionReport
|
|
3
4
|
from ..http_client import HttpClient
|
|
4
5
|
|
|
5
6
|
|
|
@@ -13,6 +14,10 @@ class OWMClient(ABC):
|
|
|
13
14
|
async def get_weather(self, lat, lon) -> WeatherReport:
|
|
14
15
|
pass
|
|
15
16
|
|
|
17
|
+
@abstractmethod
|
|
18
|
+
async def get_air_pollution(self, lat, lon) -> AirPollutionReport:
|
|
19
|
+
pass
|
|
20
|
+
|
|
16
21
|
@abstractmethod
|
|
17
22
|
async def validate_key(self) -> bool:
|
|
18
23
|
pass
|
{pyopenweathermap-0.2.0 → pyopenweathermap-0.2.2}/src/pyopenweathermap/client/owm_client_factory.py
RENAMED
|
@@ -2,15 +2,18 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
from .free_client import OWMFreeClient
|
|
4
4
|
from .onecall_client import OWMOneCallClient
|
|
5
|
+
from .air_pollution_client import OWMAirPollutionClient
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def create_owm_client(api_key, api_type, units="metric", lang='en'):
|
|
8
9
|
logger = logging.getLogger(__name__)
|
|
9
10
|
|
|
10
11
|
logger.info('Initializing OWMClient with api type: ' + str(api_type))
|
|
11
|
-
if api_type == 'v3.0'
|
|
12
|
+
if api_type == 'v3.0':
|
|
12
13
|
return OWMOneCallClient(api_key, api_type, units, lang)
|
|
13
14
|
if api_type == 'current' or api_type == 'forecast':
|
|
14
15
|
return OWMFreeClient(api_key, api_type, units, lang)
|
|
16
|
+
if api_type == 'air_pollution':
|
|
17
|
+
return OWMAirPollutionClient(api_key)
|
|
15
18
|
else:
|
|
16
19
|
raise Exception('Unsupported API type ' + str(api_type))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from datetime import UTC, datetime
|
|
2
2
|
|
|
3
3
|
from .weather import CurrentWeather, WeatherCondition, MinutelyWeatherForecast, HourlyWeatherForecast, DailyWeatherForecast, DailyTemperature
|
|
4
|
+
from .air_pollution import CurrentAirPollution
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class DataConverter:
|
|
@@ -72,7 +73,40 @@ class DataConverter:
|
|
|
72
73
|
snow=json.get('snow', 0),
|
|
73
74
|
condition=DataConverter._to_weather_condition(json['weather'][0]),
|
|
74
75
|
)
|
|
75
|
-
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def air_pollution_current(json):
|
|
79
|
+
data = json['list'][0]
|
|
80
|
+
components = data['components']
|
|
81
|
+
return CurrentAirPollution(
|
|
82
|
+
date_time=datetime.fromtimestamp(data["dt"], tz=UTC),
|
|
83
|
+
aqi=data['main']['aqi'],
|
|
84
|
+
co=components['co'],
|
|
85
|
+
no=components['no'],
|
|
86
|
+
no2=components['no2'],
|
|
87
|
+
o3=components['o3'],
|
|
88
|
+
so2=components['so2'],
|
|
89
|
+
pm2_5=components['pm2_5'],
|
|
90
|
+
pm10=components['pm10'],
|
|
91
|
+
nh3=components['nh3'],
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def air_pollution_hourly(json):
|
|
96
|
+
components = json['components']
|
|
97
|
+
return CurrentAirPollution(
|
|
98
|
+
date_time=datetime.fromtimestamp(json["dt"], tz=UTC),
|
|
99
|
+
aqi=json['main']['aqi'],
|
|
100
|
+
co=components['co'],
|
|
101
|
+
no=components['no'],
|
|
102
|
+
no2=components['no2'],
|
|
103
|
+
o3=components['o3'],
|
|
104
|
+
so2=components['so2'],
|
|
105
|
+
pm2_5=components['pm2_5'],
|
|
106
|
+
pm10=components['pm10'],
|
|
107
|
+
nh3=components['nh3'],
|
|
108
|
+
)
|
|
109
|
+
|
|
76
110
|
@staticmethod
|
|
77
111
|
def free_to_current_weather(json):
|
|
78
112
|
return CurrentWeather(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|