Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate OpenWeaterMap to new library (support API 3.0) #116305

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
98fe0c3
Migrate integration to new library
freekode Apr 26, 2024
32bf27b
migrate to new forecast
freekode Apr 26, 2024
f159db5
migrate forecasts responses
freekode Apr 26, 2024
5d57a06
fix test
freekode Apr 26, 2024
ca76d2b
upd dependency
freekode Apr 26, 2024
9532399
return forecast sensors
freekode Apr 27, 2024
1646b18
fix forecast cloud sernsor
freekode Apr 27, 2024
9f7435a
remove commented
freekode Apr 27, 2024
3939fdb
move out from try
freekode Apr 27, 2024
3486b7f
Update homeassistant/components/openweathermap/weather_update_coordin…
freekode Apr 27, 2024
668e631
move out from try
freekode Apr 27, 2024
d45b0d3
fix tests, remove migration
freekode Apr 28, 2024
7da62db
remove timeout
freekode Apr 28, 2024
711931a
add fixtures
freekode Apr 28, 2024
6c35f80
style fixes
freekode Apr 28, 2024
ae546d7
add abort test
freekode Apr 28, 2024
eb9e61e
upd tests
freekode Apr 29, 2024
7babe60
Merge branch 'dev' into owm-upgrade
freekode Apr 29, 2024
e933bb3
Merge branch 'dev' into owm-upgrade
freekode Apr 30, 2024
074276d
add description placeholder
freekode Apr 30, 2024
5d3c648
upd lib version
freekode Apr 30, 2024
57cae47
Merge branch 'dev' into owm-upgrade
freekode Apr 30, 2024
29b517e
add error description
Apr 30, 2024
06925bf
v2.5 support
freekode May 4, 2024
8f2ee6b
Merge branch 'dev' into owm-upgrade
freekode May 4, 2024
6c9de77
Merge branch 'dev' into owm-upgrade
freekode May 5, 2024
6433137
v2.5 support
freekode May 5, 2024
7ea62cd
fix strings
freekode May 5, 2024
8634bc8
fix strings
freekode May 5, 2024
13848d8
Merge branch 'dev' into owm-upgrade
freekode May 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 4 additions & 29 deletions homeassistant/components/openweathermap/__init__.py
Expand Up @@ -5,16 +5,14 @@
import logging
from typing import Any

from pyowm import OWM
from pyowm.utils.config import get_default_config
from pyopenweathermap import OWMClient

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_API_KEY,
CONF_LANGUAGE,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_MODE,
CONF_NAME,
)
from homeassistant.core import HomeAssistant
Expand All @@ -24,8 +22,6 @@
DOMAIN,
ENTRY_NAME,
ENTRY_WEATHER_COORDINATOR,
FORECAST_MODE_FREE_DAILY,
FORECAST_MODE_ONECALL_DAILY,
PLATFORMS,
UPDATE_LISTENER,
)
Expand All @@ -40,14 +36,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
api_key = entry.data[CONF_API_KEY]
latitude = entry.data.get(CONF_LATITUDE, hass.config.latitude)
longitude = entry.data.get(CONF_LONGITUDE, hass.config.longitude)
forecast_mode = _get_config_value(entry, CONF_MODE)
language = _get_config_value(entry, CONF_LANGUAGE)

config_dict = _get_owm_config(language)

owm = OWM(api_key, config_dict).weather_manager()
owm_client = OWMClient(api_key, lang=language)
weather_coordinator = WeatherUpdateCoordinator(
owm, latitude, longitude, forecast_mode, hass
owm_client, latitude, longitude, hass
)

await weather_coordinator.async_config_entry_first_refresh()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should detect that they don't have the v3 api on their account and create an issue in the issue registry telling them what to do.

https://developers.home-assistant.io/docs/core/platform/repairs?_highlight=issue#creating-an-issue

Copy link
Contributor Author

@freekode freekode May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see
i think i can add support of api v2.5 in the lib, then add a select in HA where you choose which api to use. Then add repair notification to move from 2.5 to 3.0. Although I can't detect is user has subscription or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. It should be a config entry option

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, i added support v2.5
added migration, all users will be migrated to onecall v2.5
updated config flow, you can choose between v2.5 and v3.0 (default)
if you choose 2.5, during init repair issue will be created

Copy link
Contributor Author

@freekode freekode May 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry about mess with commit author emails, i will recreate pr later when everything will be done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new keys (or rather new accounts) don't work with v2.5 already

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continue here #116870

Expand All @@ -68,21 +61,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old entry."""
config_entries = hass.config_entries
data = entry.data
version = entry.version

_LOGGER.debug("Migrating OpenWeatherMap entry from version %s", version)

if version == 1:
if (mode := data[CONF_MODE]) == FORECAST_MODE_FREE_DAILY:
mode = FORECAST_MODE_ONECALL_DAILY

new_data = {**data, CONF_MODE: mode}
config_entries.async_update_entry(
entry, data=new_data, version=CONFIG_FLOW_VERSION
)

_LOGGER.info("Empty migration, left for compatibility")
_LOGGER.info("Migration to version %s successful", CONFIG_FLOW_VERSION)

return True
Expand All @@ -108,10 +90,3 @@ def _get_config_value(config_entry: ConfigEntry, key: str) -> Any:
if config_entry.options:
return config_entry.options[key]
return config_entry.data[key]


def _get_owm_config(language: str) -> dict[str, Any]:
"""Get OpenWeatherMap configuration and add language to it."""
config_dict = get_default_config()
config_dict["language"] = language
return config_dict
57 changes: 27 additions & 30 deletions homeassistant/components/openweathermap/config_flow.py
Expand Up @@ -2,29 +2,30 @@

from __future__ import annotations

from pyowm import OWM
from pyowm.commons.exceptions import APIRequestError, UnauthorizedError
from pyopenweathermap import OWMClient, RequestError
import voluptuous as vol

from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import (
CONF_API_KEY,
CONF_LANGUAGE,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_MODE,
CONF_NAME,
)
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv

from .const import (
CONFIG_FLOW_VERSION,
DEFAULT_FORECAST_MODE,
DEFAULT_LANGUAGE,
DEFAULT_NAME,
DOMAIN,
FORECAST_MODES,
LANGUAGES,
)

Expand All @@ -42,9 +43,10 @@
"""Get the options flow for this handler."""
return OpenWeatherMapOptionsFlow(config_entry)

async def async_step_user(self, user_input=None):
async def async_step_user(self, user_input=None) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
errors = {}
description_placeholders = {}

if user_input is not None:
latitude = user_input[CONF_LATITUDE]
Expand All @@ -53,16 +55,16 @@
await self.async_set_unique_id(f"{latitude}-{longitude}")
self._abort_if_unique_id_configured()

api_key_valid = None
try:
api_online = await _is_owm_api_online(
self.hass, user_input[CONF_API_KEY], latitude, longitude
)
if not api_online:
errors["base"] = "invalid_api_key"
except UnauthorizedError:
errors["base"] = "invalid_api_key"
except APIRequestError:
owm_client = OWMClient(user_input[CONF_API_KEY])
api_key_valid = await owm_client.validate_key()
except RequestError as error:
errors["base"] = "cannot_connect"
description_placeholders["error"] = str(error)
bdraco marked this conversation as resolved.
Show resolved Hide resolved

if api_key_valid is False:
errors["base"] = "invalid_api_key"

if not errors:
return self.async_create_entry(
Expand All @@ -79,16 +81,18 @@
vol.Optional(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
vol.Optional(CONF_MODE, default=DEFAULT_FORECAST_MODE): vol.In(
FORECAST_MODES
),
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(
LANGUAGES
),
}
)

return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
return self.async_show_form(
step_id="user",
data_schema=schema,
errors=errors,
description_placeholders=description_placeholders,
)


class OpenWeatherMapOptionsFlow(OptionsFlow):
Expand All @@ -98,7 +102,7 @@
"""Initialize options flow."""
self.config_entry = config_entry

async def async_step_init(self, user_input=None):
async def async_step_init(self, user_input=None) -> ConfigFlowResult:
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
Expand All @@ -111,13 +115,6 @@
def _get_options_schema(self):
return vol.Schema(
{
vol.Optional(
CONF_MODE,
default=self.config_entry.options.get(
CONF_MODE,
self.config_entry.data.get(CONF_MODE, DEFAULT_FORECAST_MODE),
),
): vol.In(FORECAST_MODES),
vol.Optional(
CONF_LANGUAGE,
default=self.config_entry.options.get(
Expand All @@ -129,6 +126,6 @@
)


async def _is_owm_api_online(hass, api_key, lat, lon):
owm = OWM(api_key).weather_manager()
return await hass.async_add_executor_job(owm.weather_at_coords, lat, lon)
async def _is_owm_api_key_valid(api_key):
owm_client = OWMClient(api_key)
return await owm_client.validate_key()

Check warning on line 131 in homeassistant/components/openweathermap/config_flow.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/openweathermap/config_flow.py#L130-L131

Added lines #L130 - L131 were not covered by tests
4 changes: 4 additions & 0 deletions homeassistant/components/openweathermap/const.py
Expand Up @@ -47,7 +47,11 @@
ATTR_API_UV_INDEX = "uv_index"
ATTR_API_VISIBILITY_DISTANCE = "visibility_distance"
ATTR_API_WEATHER_CODE = "weather_code"
ATTR_API_CLOUD_COVERAGE = "cloud_coverage"
ATTR_API_FORECAST = "forecast"
ATTR_API_CURRENT = "current"
ATTR_API_HOURLY_FORECAST = "hourly_forecast"
ATTR_API_DAILY_FORECAST = "daily_forecast"
UPDATE_LISTENER = "update_listener"
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]

Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/openweathermap/manifest.json
Expand Up @@ -5,6 +5,6 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/openweathermap",
"iot_class": "cloud_polling",
"loggers": ["geojson", "pyowm", "pysocks"],
"requirements": ["pyowm==3.2.0"]
"loggers": ["pyopenweathermap"],
"requirements": ["pyopenweathermap==0.0.8"]
}
20 changes: 10 additions & 10 deletions homeassistant/components/openweathermap/sensor.py
Expand Up @@ -30,12 +30,13 @@
from homeassistant.util import dt as dt_util

from .const import (
ATTR_API_CLOUD_COVERAGE,
ATTR_API_CLOUDS,
ATTR_API_CONDITION,
ATTR_API_CURRENT,
ATTR_API_DAILY_FORECAST,
ATTR_API_DEW_POINT,
ATTR_API_FEELS_LIKE_TEMPERATURE,
ATTR_API_FORECAST,
ATTR_API_FORECAST_CONDITION,
ATTR_API_FORECAST_PRECIPITATION,
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
ATTR_API_FORECAST_PRESSURE,
Expand Down Expand Up @@ -164,7 +165,7 @@
)
FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key=ATTR_API_FORECAST_CONDITION,
key=ATTR_API_CONDITION,
name="Condition",
),
SensorEntityDescription(
Expand Down Expand Up @@ -213,7 +214,7 @@
device_class=SensorDeviceClass.WIND_SPEED,
),
SensorEntityDescription(
key=ATTR_API_CLOUDS,
key=ATTR_API_CLOUD_COVERAGE,
name="Cloud coverage",
native_unit_of_measurement=PERCENTAGE,
),
Expand Down Expand Up @@ -315,7 +316,9 @@ def __init__(
@property
def native_value(self) -> StateType:
"""Return the state of the device."""
return self._weather_coordinator.data.get(self.entity_description.key, None)
return self._weather_coordinator.data[ATTR_API_CURRENT].get(
self.entity_description.key
)


class OpenWeatherMapForecastSensor(AbstractOpenWeatherMapSensor):
Expand All @@ -335,11 +338,8 @@ def __init__(
@property
def native_value(self) -> StateType | datetime:
"""Return the state of the device."""
forecasts = self._weather_coordinator.data.get(ATTR_API_FORECAST)
if not forecasts:
return None

value = forecasts[0].get(self.entity_description.key, None)
forecasts = self._weather_coordinator.data[ATTR_API_DAILY_FORECAST]
value = forecasts[0].get(self.entity_description.key)
if (
value
and self.entity_description.device_class is SensorDeviceClass.TIMESTAMP
Expand Down
8 changes: 3 additions & 5 deletions homeassistant/components/openweathermap/strings.json
Expand Up @@ -5,7 +5,7 @@
},
"error": {
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
"cannot_connect": "Failed to connect: {error}"
},
"step": {
"user": {
Expand All @@ -14,8 +14,7 @@
"language": "[%key:common::config_flow::data::language%]",
"latitude": "[%key:common::config_flow::data::latitude%]",
"longitude": "[%key:common::config_flow::data::longitude%]",
"mode": "[%key:common::config_flow::data::mode%]",
"name": "[%key:common::config_flow::data::name%]"
"mode": "[%key:common::config_flow::data::mode%]"
},
"description": "To generate API key go to https://openweathermap.org/appid"
}
Expand All @@ -25,8 +24,7 @@
"step": {
"init": {
"data": {
"language": "[%key:common::config_flow::data::language%]",
"mode": "[%key:common::config_flow::data::mode%]"
"language": "[%key:common::config_flow::data::language%]"
}
}
}
Expand Down