mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 12:59:34 +00:00
Add hourly forecast for AccuWeather integration (#152178)
This commit is contained in:
@@ -2,21 +2,23 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from accuweather import AccuWeather
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_PLATFORM
|
||||
from homeassistant.const import CONF_API_KEY, CONF_NAME, Platform
|
||||
from homeassistant.const import CONF_API_KEY, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN, UPDATE_INTERVAL_DAILY_FORECAST, UPDATE_INTERVAL_OBSERVATION
|
||||
from .const import DOMAIN
|
||||
from .coordinator import (
|
||||
AccuWeatherConfigEntry,
|
||||
AccuWeatherDailyForecastDataUpdateCoordinator,
|
||||
AccuWeatherData,
|
||||
AccuWeatherHourlyForecastDataUpdateCoordinator,
|
||||
AccuWeatherObservationDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
@@ -28,7 +30,6 @@ PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: AccuWeatherConfigEntry) -> bool:
|
||||
"""Set up AccuWeather as config entry."""
|
||||
api_key: str = entry.data[CONF_API_KEY]
|
||||
name: str = entry.data[CONF_NAME]
|
||||
|
||||
location_key = entry.unique_id
|
||||
|
||||
@@ -41,26 +42,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: AccuWeatherConfigEntry)
|
||||
hass,
|
||||
entry,
|
||||
accuweather,
|
||||
name,
|
||||
"observation",
|
||||
UPDATE_INTERVAL_OBSERVATION,
|
||||
)
|
||||
|
||||
coordinator_daily_forecast = AccuWeatherDailyForecastDataUpdateCoordinator(
|
||||
hass,
|
||||
entry,
|
||||
accuweather,
|
||||
name,
|
||||
"daily forecast",
|
||||
UPDATE_INTERVAL_DAILY_FORECAST,
|
||||
)
|
||||
coordinator_hourly_forecast = AccuWeatherHourlyForecastDataUpdateCoordinator(
|
||||
hass,
|
||||
entry,
|
||||
accuweather,
|
||||
)
|
||||
|
||||
await coordinator_observation.async_config_entry_first_refresh()
|
||||
await coordinator_daily_forecast.async_config_entry_first_refresh()
|
||||
await asyncio.gather(
|
||||
coordinator_observation.async_config_entry_first_refresh(),
|
||||
coordinator_daily_forecast.async_config_entry_first_refresh(),
|
||||
coordinator_hourly_forecast.async_config_entry_first_refresh(),
|
||||
)
|
||||
|
||||
entry.runtime_data = AccuWeatherData(
|
||||
coordinator_observation=coordinator_observation,
|
||||
coordinator_daily_forecast=coordinator_daily_forecast,
|
||||
coordinator_hourly_forecast=coordinator_hourly_forecast,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -71,3 +71,4 @@ POLLEN_CATEGORY_MAP = {
|
||||
}
|
||||
UPDATE_INTERVAL_OBSERVATION = timedelta(minutes=10)
|
||||
UPDATE_INTERVAL_DAILY_FORECAST = timedelta(hours=6)
|
||||
UPDATE_INTERVAL_HOURLY_FORECAST = timedelta(hours=30)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncio import timeout
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
@@ -12,6 +13,7 @@ from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExcee
|
||||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
@@ -20,7 +22,13 @@ from homeassistant.helpers.update_coordinator import (
|
||||
UpdateFailed,
|
||||
)
|
||||
|
||||
from .const import DOMAIN, MANUFACTURER
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
MANUFACTURER,
|
||||
UPDATE_INTERVAL_DAILY_FORECAST,
|
||||
UPDATE_INTERVAL_HOURLY_FORECAST,
|
||||
UPDATE_INTERVAL_OBSERVATION,
|
||||
)
|
||||
|
||||
EXCEPTIONS = (ApiError, ClientConnectorError, InvalidApiKeyError, RequestsExceededError)
|
||||
|
||||
@@ -33,6 +41,7 @@ class AccuWeatherData:
|
||||
|
||||
coordinator_observation: AccuWeatherObservationDataUpdateCoordinator
|
||||
coordinator_daily_forecast: AccuWeatherDailyForecastDataUpdateCoordinator
|
||||
coordinator_hourly_forecast: AccuWeatherHourlyForecastDataUpdateCoordinator
|
||||
|
||||
|
||||
type AccuWeatherConfigEntry = ConfigEntry[AccuWeatherData]
|
||||
@@ -48,13 +57,11 @@ class AccuWeatherObservationDataUpdateCoordinator(
|
||||
hass: HomeAssistant,
|
||||
config_entry: AccuWeatherConfigEntry,
|
||||
accuweather: AccuWeather,
|
||||
name: str,
|
||||
coordinator_type: str,
|
||||
update_interval: timedelta,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
self.accuweather = accuweather
|
||||
self.location_key = accuweather.location_key
|
||||
name = config_entry.data[CONF_NAME]
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self.location_key is not None
|
||||
@@ -65,8 +72,8 @@ class AccuWeatherObservationDataUpdateCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
config_entry=config_entry,
|
||||
name=f"{name} ({coordinator_type})",
|
||||
update_interval=update_interval,
|
||||
name=f"{name} (observation)",
|
||||
update_interval=UPDATE_INTERVAL_OBSERVATION,
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Any]:
|
||||
@@ -86,23 +93,25 @@ class AccuWeatherObservationDataUpdateCoordinator(
|
||||
return result
|
||||
|
||||
|
||||
class AccuWeatherDailyForecastDataUpdateCoordinator(
|
||||
class AccuWeatherForecastDataUpdateCoordinator(
|
||||
TimestampDataUpdateCoordinator[list[dict[str, Any]]]
|
||||
):
|
||||
"""Class to manage fetching AccuWeather data API."""
|
||||
"""Base class for AccuWeather forecast."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: AccuWeatherConfigEntry,
|
||||
accuweather: AccuWeather,
|
||||
name: str,
|
||||
coordinator_type: str,
|
||||
update_interval: timedelta,
|
||||
fetch_method: Callable[..., Awaitable[list[dict[str, Any]]]],
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
self.accuweather = accuweather
|
||||
self.location_key = accuweather.location_key
|
||||
self._fetch_method = fetch_method
|
||||
name = config_entry.data[CONF_NAME]
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self.location_key is not None
|
||||
@@ -118,12 +127,10 @@ class AccuWeatherDailyForecastDataUpdateCoordinator(
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> list[dict[str, Any]]:
|
||||
"""Update data via library."""
|
||||
"""Update forecast data via library."""
|
||||
try:
|
||||
async with timeout(10):
|
||||
result = await self.accuweather.async_get_daily_forecast(
|
||||
language=self.hass.config.language
|
||||
)
|
||||
result = await self._fetch_method(language=self.hass.config.language)
|
||||
except EXCEPTIONS as error:
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
@@ -132,10 +139,53 @@ class AccuWeatherDailyForecastDataUpdateCoordinator(
|
||||
) from error
|
||||
|
||||
_LOGGER.debug("Requests remaining: %d", self.accuweather.requests_remaining)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class AccuWeatherDailyForecastDataUpdateCoordinator(
|
||||
AccuWeatherForecastDataUpdateCoordinator
|
||||
):
|
||||
"""Coordinator for daily forecast."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: AccuWeatherConfigEntry,
|
||||
accuweather: AccuWeather,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(
|
||||
hass,
|
||||
config_entry,
|
||||
accuweather,
|
||||
"daily forecast",
|
||||
UPDATE_INTERVAL_DAILY_FORECAST,
|
||||
fetch_method=accuweather.async_get_daily_forecast,
|
||||
)
|
||||
|
||||
|
||||
class AccuWeatherHourlyForecastDataUpdateCoordinator(
|
||||
AccuWeatherForecastDataUpdateCoordinator
|
||||
):
|
||||
"""Coordinator for hourly forecast."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: AccuWeatherConfigEntry,
|
||||
accuweather: AccuWeather,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(
|
||||
hass,
|
||||
config_entry,
|
||||
accuweather,
|
||||
"hourly forecast",
|
||||
UPDATE_INTERVAL_HOURLY_FORECAST,
|
||||
fetch_method=accuweather.async_get_hourly_forecast,
|
||||
)
|
||||
|
||||
|
||||
def _get_device_info(location_key: str, name: str) -> DeviceInfo:
|
||||
"""Get device info."""
|
||||
return DeviceInfo(
|
||||
|
||||
@@ -45,6 +45,7 @@ from .coordinator import (
|
||||
AccuWeatherConfigEntry,
|
||||
AccuWeatherDailyForecastDataUpdateCoordinator,
|
||||
AccuWeatherData,
|
||||
AccuWeatherHourlyForecastDataUpdateCoordinator,
|
||||
AccuWeatherObservationDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
@@ -64,6 +65,7 @@ class AccuWeatherEntity(
|
||||
CoordinatorWeatherEntity[
|
||||
AccuWeatherObservationDataUpdateCoordinator,
|
||||
AccuWeatherDailyForecastDataUpdateCoordinator,
|
||||
AccuWeatherHourlyForecastDataUpdateCoordinator,
|
||||
]
|
||||
):
|
||||
"""Define an AccuWeather entity."""
|
||||
@@ -76,6 +78,7 @@ class AccuWeatherEntity(
|
||||
super().__init__(
|
||||
observation_coordinator=accuweather_data.coordinator_observation,
|
||||
daily_coordinator=accuweather_data.coordinator_daily_forecast,
|
||||
hourly_coordinator=accuweather_data.coordinator_hourly_forecast,
|
||||
)
|
||||
|
||||
self._attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS
|
||||
@@ -86,10 +89,13 @@ class AccuWeatherEntity(
|
||||
self._attr_unique_id = accuweather_data.coordinator_observation.location_key
|
||||
self._attr_attribution = ATTRIBUTION
|
||||
self._attr_device_info = accuweather_data.coordinator_observation.device_info
|
||||
self._attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
|
||||
self._attr_supported_features = (
|
||||
WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY
|
||||
)
|
||||
|
||||
self.observation_coordinator = accuweather_data.coordinator_observation
|
||||
self.daily_coordinator = accuweather_data.coordinator_daily_forecast
|
||||
self.hourly_coordinator = accuweather_data.coordinator_hourly_forecast
|
||||
|
||||
@property
|
||||
def condition(self) -> str | None:
|
||||
@@ -207,3 +213,32 @@ class AccuWeatherEntity(
|
||||
}
|
||||
for item in self.daily_coordinator.data
|
||||
]
|
||||
|
||||
@callback
|
||||
def _async_forecast_hourly(self) -> list[Forecast] | None:
|
||||
"""Return the hourly forecast in native units."""
|
||||
return [
|
||||
{
|
||||
ATTR_FORECAST_TIME: utc_from_timestamp(
|
||||
item["EpochDateTime"]
|
||||
).isoformat(),
|
||||
ATTR_FORECAST_CLOUD_COVERAGE: item["CloudCover"],
|
||||
ATTR_FORECAST_HUMIDITY: item["RelativeHumidity"],
|
||||
ATTR_FORECAST_NATIVE_TEMP: item["Temperature"][ATTR_VALUE],
|
||||
ATTR_FORECAST_NATIVE_APPARENT_TEMP: item["RealFeelTemperature"][
|
||||
ATTR_VALUE
|
||||
],
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION: item["TotalLiquid"][ATTR_VALUE],
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: item[
|
||||
"PrecipitationProbability"
|
||||
],
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED: item["Wind"][ATTR_SPEED][ATTR_VALUE],
|
||||
ATTR_FORECAST_NATIVE_WIND_GUST_SPEED: item["WindGust"][ATTR_SPEED][
|
||||
ATTR_VALUE
|
||||
],
|
||||
ATTR_FORECAST_UV_INDEX: item["UVIndex"],
|
||||
ATTR_FORECAST_WIND_BEARING: item["Wind"][ATTR_DIRECTION]["Degrees"],
|
||||
ATTR_FORECAST_CONDITION: CONDITION_MAP.get(item["WeatherIcon"]),
|
||||
}
|
||||
for item in self.hourly_coordinator.data
|
||||
]
|
||||
|
||||
@@ -14,7 +14,8 @@ from tests.common import load_json_array_fixture, load_json_object_fixture
|
||||
def mock_accuweather_client() -> Generator[AsyncMock]:
|
||||
"""Mock a AccuWeather client."""
|
||||
current = load_json_object_fixture("current_conditions_data.json", DOMAIN)
|
||||
forecast = load_json_array_fixture("forecast_data.json", DOMAIN)
|
||||
daily_forecast = load_json_array_fixture("daily_forecast_data.json", DOMAIN)
|
||||
hourly_forecast = load_json_array_fixture("hourly_forecast_data.json", DOMAIN)
|
||||
location = load_json_object_fixture("location_data.json", DOMAIN)
|
||||
|
||||
with (
|
||||
@@ -29,7 +30,8 @@ def mock_accuweather_client() -> Generator[AsyncMock]:
|
||||
client = mock_client.return_value
|
||||
client.async_get_location.return_value = location
|
||||
client.async_get_current_conditions.return_value = current
|
||||
client.async_get_daily_forecast.return_value = forecast
|
||||
client.async_get_daily_forecast.return_value = daily_forecast
|
||||
client.async_get_hourly_forecast.return_value = hourly_forecast
|
||||
client.location_key = "0123456"
|
||||
client.requests_remaining = 10
|
||||
|
||||
|
||||
1334
tests/components/accuweather/fixtures/hourly_forecast_data.json
Normal file
1334
tests/components/accuweather/fixtures/hourly_forecast_data.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
# serializer version: 1
|
||||
# name: test_forecast_service[get_forecasts]
|
||||
# name: test_forecast_service[daily]
|
||||
dict({
|
||||
'weather.home': dict({
|
||||
'forecast': list([
|
||||
@@ -82,6 +82,182 @@
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_service[hourly]
|
||||
dict({
|
||||
'weather.home': dict({
|
||||
'forecast': list([
|
||||
dict({
|
||||
'apparent_temperature': 22.6,
|
||||
'cloud_coverage': 13,
|
||||
'condition': 'sunny',
|
||||
'datetime': '2025-09-12T14:00:00+00:00',
|
||||
'humidity': 48,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 1,
|
||||
'temperature': 22.5,
|
||||
'uv_index': 2,
|
||||
'wind_bearing': 239,
|
||||
'wind_gust_speed': 24.1,
|
||||
'wind_speed': 14.8,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 22.9,
|
||||
'cloud_coverage': 17,
|
||||
'condition': 'sunny',
|
||||
'datetime': '2025-09-12T15:00:00+00:00',
|
||||
'humidity': 48,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 1,
|
||||
'temperature': 23.1,
|
||||
'uv_index': 2,
|
||||
'wind_bearing': 238,
|
||||
'wind_gust_speed': 22.2,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 20.6,
|
||||
'cloud_coverage': 23,
|
||||
'condition': 'sunny',
|
||||
'datetime': '2025-09-12T16:00:00+00:00',
|
||||
'humidity': 56,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 1,
|
||||
'temperature': 21.3,
|
||||
'uv_index': 1,
|
||||
'wind_bearing': 232,
|
||||
'wind_gust_speed': 18.5,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 18.2,
|
||||
'cloud_coverage': 29,
|
||||
'condition': 'sunny',
|
||||
'datetime': '2025-09-12T17:00:00+00:00',
|
||||
'humidity': 62,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 2,
|
||||
'temperature': 19.5,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 224,
|
||||
'wind_gust_speed': 16.7,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 16.7,
|
||||
'cloud_coverage': 34,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2025-09-12T18:00:00+00:00',
|
||||
'humidity': 69,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 3,
|
||||
'temperature': 17.7,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 219,
|
||||
'wind_gust_speed': 14.8,
|
||||
'wind_speed': 11.1,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 14.9,
|
||||
'cloud_coverage': 30,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2025-09-12T19:00:00+00:00',
|
||||
'humidity': 77,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 3,
|
||||
'temperature': 15.8,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 230,
|
||||
'wind_gust_speed': 13.0,
|
||||
'wind_speed': 11.1,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 13.8,
|
||||
'cloud_coverage': 26,
|
||||
'condition': 'clear-night',
|
||||
'datetime': '2025-09-12T20:00:00+00:00',
|
||||
'humidity': 84,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 3,
|
||||
'temperature': 14.6,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 259,
|
||||
'wind_gust_speed': 13.0,
|
||||
'wind_speed': 9.3,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 13.8,
|
||||
'cloud_coverage': 22,
|
||||
'condition': 'clear-night',
|
||||
'datetime': '2025-09-12T21:00:00+00:00',
|
||||
'humidity': 86,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 4,
|
||||
'temperature': 14.4,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 272,
|
||||
'wind_gust_speed': 13.0,
|
||||
'wind_speed': 9.3,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 13.5,
|
||||
'cloud_coverage': 48,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2025-09-12T22:00:00+00:00',
|
||||
'humidity': 89,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 4,
|
||||
'temperature': 13.9,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 265,
|
||||
'wind_gust_speed': 13.0,
|
||||
'wind_speed': 7.4,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 13.2,
|
||||
'cloud_coverage': 74,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2025-09-12T23:00:00+00:00',
|
||||
'humidity': 91,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 4,
|
||||
'temperature': 13.6,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 256,
|
||||
'wind_gust_speed': 11.1,
|
||||
'wind_speed': 7.4,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 13.5,
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2025-09-13T00:00:00+00:00',
|
||||
'humidity': 90,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 5,
|
||||
'temperature': 13.9,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 244,
|
||||
'wind_gust_speed': 11.1,
|
||||
'wind_speed': 7.4,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 13.6,
|
||||
'cloud_coverage': 98,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2025-09-13T01:00:00+00:00',
|
||||
'humidity': 89,
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 5,
|
||||
'temperature': 14.0,
|
||||
'uv_index': 0,
|
||||
'wind_bearing': 229,
|
||||
'wind_gust_speed': 9.3,
|
||||
'wind_speed': 7.4,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_subscription
|
||||
list([
|
||||
dict({
|
||||
@@ -269,7 +445,7 @@
|
||||
'platform': 'accuweather',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <WeatherEntityFeature: 1>,
|
||||
'supported_features': <WeatherEntityFeature: 3>,
|
||||
'translation_key': None,
|
||||
'unique_id': '0123456',
|
||||
'unit_of_measurement': None,
|
||||
@@ -287,7 +463,7 @@
|
||||
'precipitation_unit': <UnitOfPrecipitationDepth.MILLIMETERS: 'mm'>,
|
||||
'pressure': 1012.0,
|
||||
'pressure_unit': <UnitOfPressure.HPA: 'hPa'>,
|
||||
'supported_features': <WeatherEntityFeature: 1>,
|
||||
'supported_features': <WeatherEntityFeature: 3>,
|
||||
'temperature': 22.6,
|
||||
'temperature_unit': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
'uv_index': 6,
|
||||
|
||||
@@ -107,24 +107,24 @@ async def test_unsupported_condition_icon_data(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service"),
|
||||
[SERVICE_GET_FORECASTS],
|
||||
("forecast_type"),
|
||||
["daily", "hourly"],
|
||||
)
|
||||
async def test_forecast_service(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
service: str,
|
||||
forecast_type: str,
|
||||
) -> None:
|
||||
"""Test multiple forecast."""
|
||||
await init_integration(hass)
|
||||
|
||||
response = await hass.services.async_call(
|
||||
WEATHER_DOMAIN,
|
||||
service,
|
||||
SERVICE_GET_FORECASTS,
|
||||
{
|
||||
"entity_id": "weather.home",
|
||||
"type": "daily",
|
||||
"type": forecast_type,
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
|
||||
Reference in New Issue
Block a user