mirror of
https://github.com/home-assistant/core.git
synced 2025-05-14 10:59:15 +00:00
Address weatherkit late review comments (#100265)
* Address review comments from original weatherkit PR * Use .get() for optional fields
This commit is contained in:
parent
e87603aa59
commit
dd95b51d10
homeassistant/components/weatherkit
tests/components/weatherkit
@ -120,7 +120,7 @@ class WeatherKitFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
location[CONF_LONGITUDE],
|
location[CONF_LONGITUDE],
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(availability) == 0:
|
if not availability:
|
||||||
raise WeatherKitUnsupportedLocationError(
|
raise WeatherKitUnsupportedLocationError(
|
||||||
"API does not support this location"
|
"API does not support this location"
|
||||||
)
|
)
|
||||||
|
@ -5,7 +5,10 @@ LOGGER: Logger = getLogger(__package__)
|
|||||||
|
|
||||||
NAME = "Apple WeatherKit"
|
NAME = "Apple WeatherKit"
|
||||||
DOMAIN = "weatherkit"
|
DOMAIN = "weatherkit"
|
||||||
ATTRIBUTION = "Data provided by Apple Weather. https://developer.apple.com/weatherkit/data-source-attribution/"
|
ATTRIBUTION = (
|
||||||
|
"Data provided by Apple Weather. "
|
||||||
|
"https://developer.apple.com/weatherkit/data-source-attribution/"
|
||||||
|
)
|
||||||
|
|
||||||
CONF_KEY_ID = "key_id"
|
CONF_KEY_ID = "key_id"
|
||||||
CONF_SERVICE_ID = "service_id"
|
CONF_SERVICE_ID = "service_id"
|
||||||
|
@ -5,6 +5,18 @@ from typing import Any, cast
|
|||||||
from apple_weatherkit import DataSetType
|
from apple_weatherkit import DataSetType
|
||||||
|
|
||||||
from homeassistant.components.weather import (
|
from homeassistant.components.weather import (
|
||||||
|
ATTR_CONDITION_CLOUDY,
|
||||||
|
ATTR_CONDITION_EXCEPTIONAL,
|
||||||
|
ATTR_CONDITION_FOG,
|
||||||
|
ATTR_CONDITION_HAIL,
|
||||||
|
ATTR_CONDITION_LIGHTNING,
|
||||||
|
ATTR_CONDITION_PARTLYCLOUDY,
|
||||||
|
ATTR_CONDITION_POURING,
|
||||||
|
ATTR_CONDITION_RAINY,
|
||||||
|
ATTR_CONDITION_SNOWY,
|
||||||
|
ATTR_CONDITION_SNOWY_RAINY,
|
||||||
|
ATTR_CONDITION_SUNNY,
|
||||||
|
ATTR_CONDITION_WINDY,
|
||||||
Forecast,
|
Forecast,
|
||||||
SingleCoordinatorWeatherEntity,
|
SingleCoordinatorWeatherEntity,
|
||||||
WeatherEntityFeature,
|
WeatherEntityFeature,
|
||||||
@ -40,71 +52,71 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
|
|
||||||
condition_code_to_hass = {
|
condition_code_to_hass = {
|
||||||
"BlowingDust": "windy",
|
"BlowingDust": ATTR_CONDITION_WINDY,
|
||||||
"Clear": "sunny",
|
"Clear": ATTR_CONDITION_SUNNY,
|
||||||
"Cloudy": "cloudy",
|
"Cloudy": ATTR_CONDITION_CLOUDY,
|
||||||
"Foggy": "fog",
|
"Foggy": ATTR_CONDITION_FOG,
|
||||||
"Haze": "fog",
|
"Haze": ATTR_CONDITION_FOG,
|
||||||
"MostlyClear": "sunny",
|
"MostlyClear": ATTR_CONDITION_SUNNY,
|
||||||
"MostlyCloudy": "cloudy",
|
"MostlyCloudy": ATTR_CONDITION_CLOUDY,
|
||||||
"PartlyCloudy": "partlycloudy",
|
"PartlyCloudy": ATTR_CONDITION_PARTLYCLOUDY,
|
||||||
"Smoky": "fog",
|
"Smoky": ATTR_CONDITION_FOG,
|
||||||
"Breezy": "windy",
|
"Breezy": ATTR_CONDITION_WINDY,
|
||||||
"Windy": "windy",
|
"Windy": ATTR_CONDITION_WINDY,
|
||||||
"Drizzle": "rainy",
|
"Drizzle": ATTR_CONDITION_RAINY,
|
||||||
"HeavyRain": "pouring",
|
"HeavyRain": ATTR_CONDITION_POURING,
|
||||||
"IsolatedThunderstorms": "lightning",
|
"IsolatedThunderstorms": ATTR_CONDITION_LIGHTNING,
|
||||||
"Rain": "rainy",
|
"Rain": ATTR_CONDITION_RAINY,
|
||||||
"SunShowers": "rainy",
|
"SunShowers": ATTR_CONDITION_RAINY,
|
||||||
"ScatteredThunderstorms": "lightning",
|
"ScatteredThunderstorms": ATTR_CONDITION_LIGHTNING,
|
||||||
"StrongStorms": "lightning",
|
"StrongStorms": ATTR_CONDITION_LIGHTNING,
|
||||||
"Thunderstorms": "lightning",
|
"Thunderstorms": ATTR_CONDITION_LIGHTNING,
|
||||||
"Frigid": "snowy",
|
"Frigid": ATTR_CONDITION_SNOWY,
|
||||||
"Hail": "hail",
|
"Hail": ATTR_CONDITION_HAIL,
|
||||||
"Hot": "sunny",
|
"Hot": ATTR_CONDITION_SUNNY,
|
||||||
"Flurries": "snowy",
|
"Flurries": ATTR_CONDITION_SNOWY,
|
||||||
"Sleet": "snowy",
|
"Sleet": ATTR_CONDITION_SNOWY,
|
||||||
"Snow": "snowy",
|
"Snow": ATTR_CONDITION_SNOWY,
|
||||||
"SunFlurries": "snowy",
|
"SunFlurries": ATTR_CONDITION_SNOWY,
|
||||||
"WintryMix": "snowy",
|
"WintryMix": ATTR_CONDITION_SNOWY,
|
||||||
"Blizzard": "snowy",
|
"Blizzard": ATTR_CONDITION_SNOWY,
|
||||||
"BlowingSnow": "snowy",
|
"BlowingSnow": ATTR_CONDITION_SNOWY,
|
||||||
"FreezingDrizzle": "snowy-rainy",
|
"FreezingDrizzle": ATTR_CONDITION_SNOWY_RAINY,
|
||||||
"FreezingRain": "snowy-rainy",
|
"FreezingRain": ATTR_CONDITION_SNOWY_RAINY,
|
||||||
"HeavySnow": "snowy",
|
"HeavySnow": ATTR_CONDITION_SNOWY,
|
||||||
"Hurricane": "exceptional",
|
"Hurricane": ATTR_CONDITION_EXCEPTIONAL,
|
||||||
"TropicalStorm": "exceptional",
|
"TropicalStorm": ATTR_CONDITION_EXCEPTIONAL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _map_daily_forecast(forecast) -> Forecast:
|
def _map_daily_forecast(forecast: dict[str, Any]) -> Forecast:
|
||||||
return {
|
return {
|
||||||
"datetime": forecast.get("forecastStart"),
|
"datetime": forecast["forecastStart"],
|
||||||
"condition": condition_code_to_hass[forecast.get("conditionCode")],
|
"condition": condition_code_to_hass[forecast["conditionCode"]],
|
||||||
"native_temperature": forecast.get("temperatureMax"),
|
"native_temperature": forecast["temperatureMax"],
|
||||||
"native_templow": forecast.get("temperatureMin"),
|
"native_templow": forecast["temperatureMin"],
|
||||||
"native_precipitation": forecast.get("precipitationAmount"),
|
"native_precipitation": forecast["precipitationAmount"],
|
||||||
"precipitation_probability": forecast.get("precipitationChance") * 100,
|
"precipitation_probability": forecast["precipitationChance"] * 100,
|
||||||
"uv_index": forecast.get("maxUvIndex"),
|
"uv_index": forecast["maxUvIndex"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _map_hourly_forecast(forecast) -> Forecast:
|
def _map_hourly_forecast(forecast: dict[str, Any]) -> Forecast:
|
||||||
return {
|
return {
|
||||||
"datetime": forecast.get("forecastStart"),
|
"datetime": forecast["forecastStart"],
|
||||||
"condition": condition_code_to_hass[forecast.get("conditionCode")],
|
"condition": condition_code_to_hass[forecast["conditionCode"]],
|
||||||
"native_temperature": forecast.get("temperature"),
|
"native_temperature": forecast["temperature"],
|
||||||
"native_apparent_temperature": forecast.get("temperatureApparent"),
|
"native_apparent_temperature": forecast["temperatureApparent"],
|
||||||
"native_dew_point": forecast.get("temperatureDewPoint"),
|
"native_dew_point": forecast.get("temperatureDewPoint"),
|
||||||
"native_pressure": forecast.get("pressure"),
|
"native_pressure": forecast["pressure"],
|
||||||
"native_wind_gust_speed": forecast.get("windGust"),
|
"native_wind_gust_speed": forecast.get("windGust"),
|
||||||
"native_wind_speed": forecast.get("windSpeed"),
|
"native_wind_speed": forecast["windSpeed"],
|
||||||
"wind_bearing": forecast.get("windDirection"),
|
"wind_bearing": forecast.get("windDirection"),
|
||||||
"humidity": forecast.get("humidity") * 100,
|
"humidity": forecast["humidity"] * 100,
|
||||||
"native_precipitation": forecast.get("precipitationAmount"),
|
"native_precipitation": forecast.get("precipitationAmount"),
|
||||||
"precipitation_probability": forecast.get("precipitationChance") * 100,
|
"precipitation_probability": forecast["precipitationChance"] * 100,
|
||||||
"cloud_coverage": forecast.get("cloudCover") * 100,
|
"cloud_coverage": forecast["cloudCover"] * 100,
|
||||||
"uv_index": forecast.get("uvIndex"),
|
"uv_index": forecast["uvIndex"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -142,10 +154,11 @@ class WeatherKitWeather(
|
|||||||
@property
|
@property
|
||||||
def supported_features(self) -> WeatherEntityFeature:
|
def supported_features(self) -> WeatherEntityFeature:
|
||||||
"""Determine supported features based on available data sets reported by WeatherKit."""
|
"""Determine supported features based on available data sets reported by WeatherKit."""
|
||||||
if not self.coordinator.supported_data_sets:
|
|
||||||
return WeatherEntityFeature(0)
|
|
||||||
|
|
||||||
features = WeatherEntityFeature(0)
|
features = WeatherEntityFeature(0)
|
||||||
|
|
||||||
|
if not self.coordinator.supported_data_sets:
|
||||||
|
return features
|
||||||
|
|
||||||
if DataSetType.DAILY_FORECAST in self.coordinator.supported_data_sets:
|
if DataSetType.DAILY_FORECAST in self.coordinator.supported_data_sets:
|
||||||
features |= WeatherEntityFeature.FORECAST_DAILY
|
features |= WeatherEntityFeature.FORECAST_DAILY
|
||||||
if DataSetType.HOURLY_FORECAST in self.coordinator.supported_data_sets:
|
if DataSetType.HOURLY_FORECAST in self.coordinator.supported_data_sets:
|
||||||
|
@ -40,26 +40,6 @@ EXAMPLE_USER_INPUT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def _test_exception_generates_error(
|
|
||||||
hass: HomeAssistant, exception: Exception, error: str
|
|
||||||
) -> None:
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.weatherkit.WeatherKitApiClient.get_availability",
|
|
||||||
side_effect=exception,
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
EXAMPLE_USER_INPUT,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert result["type"] == FlowResultType.FORM
|
|
||||||
assert result["errors"] == {"base": error}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||||
"""Test we get the form and create an entry."""
|
"""Test we get the form and create an entry."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
@ -69,8 +49,8 @@ async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
|||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.weatherkit.config_flow.WeatherKitFlowHandler._test_config",
|
"homeassistant.components.weatherkit.WeatherKitApiClient.get_availability",
|
||||||
return_value=None,
|
return_value=[DataSetType.CURRENT_WEATHER],
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
@ -100,7 +80,21 @@ async def test_error_handling(
|
|||||||
hass: HomeAssistant, exception: Exception, expected_error: str
|
hass: HomeAssistant, exception: Exception, expected_error: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that we handle various exceptions and generate appropriate errors."""
|
"""Test that we handle various exceptions and generate appropriate errors."""
|
||||||
await _test_exception_generates_error(hass, exception, expected_error)
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.weatherkit.WeatherKitApiClient.get_availability",
|
||||||
|
side_effect=exception,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
EXAMPLE_USER_INPUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == FlowResultType.FORM
|
||||||
|
assert result["errors"] == {"base": expected_error}
|
||||||
|
|
||||||
|
|
||||||
async def test_form_unsupported_location(hass: HomeAssistant) -> None:
|
async def test_form_unsupported_location(hass: HomeAssistant) -> None:
|
||||||
|
@ -5,13 +5,10 @@ from apple_weatherkit.client import (
|
|||||||
WeatherKitApiClientAuthenticationError,
|
WeatherKitApiClientAuthenticationError,
|
||||||
WeatherKitApiClientError,
|
WeatherKitApiClientError,
|
||||||
)
|
)
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.weatherkit import async_setup_entry
|
|
||||||
from homeassistant.components.weatherkit.const import DOMAIN
|
from homeassistant.components.weatherkit.const import DOMAIN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
|
||||||
|
|
||||||
from . import EXAMPLE_CONFIG_DATA
|
from . import EXAMPLE_CONFIG_DATA
|
||||||
|
|
||||||
@ -50,7 +47,7 @@ async def test_client_error_handling(hass: HomeAssistant) -> None:
|
|||||||
data=EXAMPLE_CONFIG_DATA,
|
data=EXAMPLE_CONFIG_DATA,
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(ConfigEntryNotReady), patch(
|
with patch(
|
||||||
"homeassistant.components.weatherkit.WeatherKitApiClient.get_weather_data",
|
"homeassistant.components.weatherkit.WeatherKitApiClient.get_weather_data",
|
||||||
side_effect=WeatherKitApiClientError,
|
side_effect=WeatherKitApiClientError,
|
||||||
), patch(
|
), patch(
|
||||||
@ -58,6 +55,7 @@ async def test_client_error_handling(hass: HomeAssistant) -> None:
|
|||||||
side_effect=WeatherKitApiClientError,
|
side_effect=WeatherKitApiClientError,
|
||||||
):
|
):
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
config_entries.current_entry.set(entry)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await async_setup_entry(hass, entry)
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert entry.state == config_entries.ConfigEntryState.SETUP_RETRY
|
||||||
|
Loading…
x
Reference in New Issue
Block a user