Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,7 @@ omit =
homeassistant/components/openuv/sensor.py
homeassistant/components/openweathermap/__init__.py
homeassistant/components/openweathermap/coordinator.py
homeassistant/components/openweathermap/repairs.py
homeassistant/components/openweathermap/sensor.py
homeassistant/components/openweathermap/weather.py
homeassistant/components/opnsense/__init__.py
Expand Down
36 changes: 12 additions & 24 deletions homeassistant/components/openweathermap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
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 (
Expand All @@ -20,13 +19,9 @@
)
from homeassistant.core import HomeAssistant

from .const import (
CONFIG_FLOW_VERSION,
FORECAST_MODE_FREE_DAILY,
FORECAST_MODE_ONECALL_DAILY,
PLATFORMS,
)
from .const import CONFIG_FLOW_VERSION, OWM_MODE_V25, PLATFORMS
from .coordinator import WeatherUpdateCoordinator
from .repairs import async_create_issue, async_delete_issue

_LOGGER = logging.getLogger(__name__)

Expand All @@ -49,14 +44,17 @@ async def async_setup_entry(
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)
mode = _get_config_value(entry, CONF_MODE)

config_dict = _get_owm_config(language)
if mode == OWM_MODE_V25:
async_create_issue(hass, entry.entry_id)
else:
async_delete_issue(hass, entry.entry_id)

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

await weather_coordinator.async_config_entry_first_refresh()
Expand All @@ -78,11 +76,8 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

_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}
if version < 3:
new_data = {**data, CONF_MODE: OWM_MODE_V25}
config_entries.async_update_entry(
entry, data=new_data, version=CONFIG_FLOW_VERSION
)
Expand All @@ -108,10 +103,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
53 changes: 25 additions & 28 deletions homeassistant/components/openweathermap/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

from __future__ import annotations

from pyowm import OWM
from pyowm.commons.exceptions import APIRequestError, UnauthorizedError
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,
Expand All @@ -20,13 +23,14 @@

from .const import (
CONFIG_FLOW_VERSION,
DEFAULT_FORECAST_MODE,
DEFAULT_LANGUAGE,
DEFAULT_NAME,
DEFAULT_OWM_MODE,
DOMAIN,
FORECAST_MODES,
LANGUAGES,
OWM_MODES,
)
from .utils import validate_api_key


class OpenWeatherMapConfigFlow(ConfigFlow, domain=DOMAIN):
Expand All @@ -42,27 +46,22 @@ def async_get_options_flow(
"""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]
longitude = user_input[CONF_LONGITUDE]
mode = user_input[CONF_MODE]

await self.async_set_unique_id(f"{latitude}-{longitude}")
self._abort_if_unique_id_configured()

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:
errors["base"] = "cannot_connect"
errors, description_placeholders = await validate_api_key(
user_input[CONF_API_KEY], mode
)

if not errors:
return self.async_create_entry(
Expand All @@ -79,16 +78,19 @@ async def async_step_user(self, user_input=None):
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_MODE, default=DEFAULT_OWM_MODE): vol.In(OWM_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 +100,7 @@ def __init__(self, config_entry: ConfigEntry) -> None:
"""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: dict | None = None) -> ConfigFlowResult:
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
Expand All @@ -115,9 +117,9 @@ def _get_options_schema(self):
CONF_MODE,
default=self.config_entry.options.get(
CONF_MODE,
self.config_entry.data.get(CONF_MODE, DEFAULT_FORECAST_MODE),
self.config_entry.data.get(CONF_MODE, DEFAULT_OWM_MODE),
),
): vol.In(FORECAST_MODES),
): vol.In(OWM_MODES),
vol.Optional(
CONF_LANGUAGE,
default=self.config_entry.options.get(
Expand All @@ -127,8 +129,3 @@ def _get_options_schema(self):
): vol.In(LANGUAGES),
}
)


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)
17 changes: 9 additions & 8 deletions homeassistant/components/openweathermap/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
DEFAULT_LANGUAGE = "en"
ATTRIBUTION = "Data provided by OpenWeatherMap"
MANUFACTURER = "OpenWeather"
CONFIG_FLOW_VERSION = 2
CONFIG_FLOW_VERSION = 3
ATTR_API_PRECIPITATION = "precipitation"
ATTR_API_PRECIPITATION_KIND = "precipitation_kind"
ATTR_API_DATETIME = "datetime"
Expand All @@ -45,7 +45,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 All @@ -67,13 +71,10 @@
FORECAST_MODE_FREE_DAILY = "freedaily"
FORECAST_MODE_ONECALL_HOURLY = "onecall_hourly"
FORECAST_MODE_ONECALL_DAILY = "onecall_daily"
FORECAST_MODES = [
FORECAST_MODE_HOURLY,
FORECAST_MODE_DAILY,
FORECAST_MODE_ONECALL_HOURLY,
FORECAST_MODE_ONECALL_DAILY,
]
DEFAULT_FORECAST_MODE = FORECAST_MODE_HOURLY
OWM_MODE_V25 = "v2.5"
OWM_MODE_V30 = "v3.0"
OWM_MODES = [OWM_MODE_V30, OWM_MODE_V25]
DEFAULT_OWM_MODE = OWM_MODE_V30

LANGUAGES = [
"af",
Expand Down
Loading