Use separate data coordinators for AccuWeather observation and forecast (#115628)
* Remove forecast option * Update strings * Use separate DataUpdateCoordinator for observation and forecast * Fix tests * Remove unneeded variable * Separate data coordinator classes * Use list comprehension * Separate coordinator clasess to add type annotations * Test the availability of the forecast sensor entity * Add DataUpdateCoordinator types * Use snapshot for test_sensor() --------- Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
This commit is contained in:
parent
81036967f0
commit
e7076ac83f
16 changed files with 6913 additions and 852 deletions
|
@ -2,14 +2,10 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncio import timeout
|
||||
from datetime import timedelta
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
|
||||
from aiohttp import ClientSession
|
||||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
from accuweather import AccuWeather
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_PLATFORM
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
@ -17,43 +13,70 @@ from homeassistant.const import CONF_API_KEY, CONF_NAME, Platform
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import ATTR_FORECAST, CONF_FORECAST, DOMAIN, MANUFACTURER
|
||||
from .const import DOMAIN, UPDATE_INTERVAL_DAILY_FORECAST, UPDATE_INTERVAL_OBSERVATION
|
||||
from .coordinator import (
|
||||
AccuWeatherDailyForecastDataUpdateCoordinator,
|
||||
AccuWeatherObservationDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
|
||||
|
||||
|
||||
@dataclass
|
||||
class AccuWeatherData:
|
||||
"""Data for AccuWeather integration."""
|
||||
|
||||
coordinator_observation: AccuWeatherObservationDataUpdateCoordinator
|
||||
coordinator_daily_forecast: AccuWeatherDailyForecastDataUpdateCoordinator
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up AccuWeather as config entry."""
|
||||
api_key: str = entry.data[CONF_API_KEY]
|
||||
name: str = entry.data[CONF_NAME]
|
||||
assert entry.unique_id is not None
|
||||
location_key = entry.unique_id
|
||||
forecast: bool = entry.options.get(CONF_FORECAST, False)
|
||||
|
||||
_LOGGER.debug("Using location_key: %s, get forecast: %s", location_key, forecast)
|
||||
location_key = entry.unique_id
|
||||
|
||||
_LOGGER.debug("Using location_key: %s", location_key)
|
||||
|
||||
websession = async_get_clientsession(hass)
|
||||
accuweather = AccuWeather(api_key, websession, location_key=location_key)
|
||||
|
||||
coordinator = AccuWeatherDataUpdateCoordinator(
|
||||
hass, websession, api_key, location_key, forecast, name
|
||||
coordinator_observation = AccuWeatherObservationDataUpdateCoordinator(
|
||||
hass,
|
||||
accuweather,
|
||||
name,
|
||||
"observation",
|
||||
UPDATE_INTERVAL_OBSERVATION,
|
||||
)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
coordinator_daily_forecast = AccuWeatherDailyForecastDataUpdateCoordinator(
|
||||
hass,
|
||||
accuweather,
|
||||
name,
|
||||
"daily forecast",
|
||||
UPDATE_INTERVAL_DAILY_FORECAST,
|
||||
)
|
||||
|
||||
await coordinator_observation.async_config_entry_first_refresh()
|
||||
await coordinator_daily_forecast.async_config_entry_first_refresh()
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = AccuWeatherData(
|
||||
coordinator_observation=coordinator_observation,
|
||||
coordinator_daily_forecast=coordinator_daily_forecast,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
# Remove ozone sensors from registry if they exist
|
||||
ent_reg = er.async_get(hass)
|
||||
for day in range(5):
|
||||
unique_id = f"{coordinator.location_key}-ozone-{day}"
|
||||
unique_id = f"{location_key}-ozone-{day}"
|
||||
if entity_id := ent_reg.async_get_entity_id(SENSOR_PLATFORM, DOMAIN, unique_id):
|
||||
_LOGGER.debug("Removing ozone sensor entity %s", entity_id)
|
||||
ent_reg.async_remove(entity_id)
|
||||
|
@ -74,65 +97,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
class AccuWeatherDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): # pylint: disable=hass-enforce-coordinator-module
|
||||
"""Class to manage fetching AccuWeather data API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
session: ClientSession,
|
||||
api_key: str,
|
||||
location_key: str,
|
||||
forecast: bool,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
self.location_key = location_key
|
||||
self.forecast = forecast
|
||||
self.accuweather = AccuWeather(api_key, session, location_key=location_key)
|
||||
self.device_info = DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, location_key)},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=name,
|
||||
# You don't need to provide specific details for the URL,
|
||||
# so passing in _ characters is fine if the location key
|
||||
# is correct
|
||||
configuration_url=(
|
||||
"http://accuweather.com/en/"
|
||||
f"_/_/{location_key}/"
|
||||
f"weather-forecast/{location_key}/"
|
||||
),
|
||||
)
|
||||
|
||||
# Enabling the forecast download increases the number of requests per data
|
||||
# update, we use 40 minutes for current condition only and 80 minutes for
|
||||
# current condition and forecast as update interval to not exceed allowed number
|
||||
# of requests. We have 50 requests allowed per day, so we use 36 and leave 14 as
|
||||
# a reserve for restarting HA.
|
||||
update_interval = timedelta(minutes=40)
|
||||
if self.forecast:
|
||||
update_interval *= 2
|
||||
_LOGGER.debug("Data will be update every %s", update_interval)
|
||||
|
||||
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Any]:
|
||||
"""Update data via library."""
|
||||
forecast: list[dict[str, Any]] = []
|
||||
try:
|
||||
async with timeout(10):
|
||||
current = await self.accuweather.async_get_current_conditions()
|
||||
if self.forecast:
|
||||
forecast = await self.accuweather.async_get_daily_forecast()
|
||||
except (
|
||||
ApiError,
|
||||
ClientConnectorError,
|
||||
InvalidApiKeyError,
|
||||
RequestsExceededError,
|
||||
) as error:
|
||||
raise UpdateFailed(error) from error
|
||||
_LOGGER.debug("Requests remaining: %d", self.accuweather.requests_remaining)
|
||||
return {**current, ATTR_FORECAST: forecast}
|
||||
|
|
|
@ -10,26 +10,12 @@ from aiohttp import ClientError
|
|||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.schema_config_entry_flow import (
|
||||
SchemaFlowFormStep,
|
||||
SchemaOptionsFlowHandler,
|
||||
)
|
||||
|
||||
from .const import CONF_FORECAST, DOMAIN
|
||||
|
||||
OPTIONS_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_FORECAST, default=False): bool,
|
||||
}
|
||||
)
|
||||
OPTIONS_FLOW = {
|
||||
"init": SchemaFlowFormStep(OPTIONS_SCHEMA),
|
||||
}
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
@ -87,9 +73,3 @@ class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry: ConfigEntry) -> SchemaOptionsFlowHandler:
|
||||
"""Options callback for AccuWeather."""
|
||||
return SchemaOptionsFlowHandler(config_entry, OPTIONS_FLOW)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
|
@ -27,10 +28,8 @@ ATTR_CATEGORY: Final = "Category"
|
|||
ATTR_DIRECTION: Final = "Direction"
|
||||
ATTR_ENGLISH: Final = "English"
|
||||
ATTR_LEVEL: Final = "level"
|
||||
ATTR_FORECAST: Final = "forecast"
|
||||
ATTR_SPEED: Final = "Speed"
|
||||
ATTR_VALUE: Final = "Value"
|
||||
CONF_FORECAST: Final = "forecast"
|
||||
DOMAIN: Final = "accuweather"
|
||||
MANUFACTURER: Final = "AccuWeather, Inc."
|
||||
MAX_FORECAST_DAYS: Final = 4
|
||||
|
@ -56,3 +55,5 @@ CONDITION_MAP = {
|
|||
for cond_ha, cond_codes in CONDITION_CLASSES.items()
|
||||
for cond_code in cond_codes
|
||||
}
|
||||
UPDATE_INTERVAL_OBSERVATION = timedelta(minutes=40)
|
||||
UPDATE_INTERVAL_DAILY_FORECAST = timedelta(hours=6)
|
||||
|
|
124
homeassistant/components/accuweather/coordinator.py
Normal file
124
homeassistant/components/accuweather/coordinator.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
"""The AccuWeather coordinator."""
|
||||
|
||||
from asyncio import timeout
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
|
||||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
DataUpdateCoordinator,
|
||||
TimestampDataUpdateCoordinator,
|
||||
UpdateFailed,
|
||||
)
|
||||
|
||||
from .const import DOMAIN, MANUFACTURER
|
||||
|
||||
EXCEPTIONS = (ApiError, ClientConnectorError, InvalidApiKeyError, RequestsExceededError)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccuWeatherObservationDataUpdateCoordinator(
|
||||
DataUpdateCoordinator[dict[str, Any]]
|
||||
):
|
||||
"""Class to manage fetching AccuWeather data API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
accuweather: AccuWeather,
|
||||
name: str,
|
||||
coordinator_type: str,
|
||||
update_interval: timedelta,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
self.accuweather = accuweather
|
||||
self.location_key = accuweather.location_key
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self.location_key is not None
|
||||
|
||||
self.device_info = _get_device_info(self.location_key, name)
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"{name} ({coordinator_type})",
|
||||
update_interval=update_interval,
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Any]:
|
||||
"""Update data via library."""
|
||||
try:
|
||||
async with timeout(10):
|
||||
result = await self.accuweather.async_get_current_conditions()
|
||||
except EXCEPTIONS as error:
|
||||
raise UpdateFailed(error) from error
|
||||
|
||||
_LOGGER.debug("Requests remaining: %d", self.accuweather.requests_remaining)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class AccuWeatherDailyForecastDataUpdateCoordinator(
|
||||
TimestampDataUpdateCoordinator[list[dict[str, Any]]]
|
||||
):
|
||||
"""Class to manage fetching AccuWeather data API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
accuweather: AccuWeather,
|
||||
name: str,
|
||||
coordinator_type: str,
|
||||
update_interval: timedelta,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
self.accuweather = accuweather
|
||||
self.location_key = accuweather.location_key
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self.location_key is not None
|
||||
|
||||
self.device_info = _get_device_info(self.location_key, name)
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"{name} ({coordinator_type})",
|
||||
update_interval=update_interval,
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> list[dict[str, Any]]:
|
||||
"""Update data via library."""
|
||||
try:
|
||||
async with timeout(10):
|
||||
result = await self.accuweather.async_get_daily_forecast()
|
||||
except EXCEPTIONS as error:
|
||||
raise UpdateFailed(error) from error
|
||||
|
||||
_LOGGER.debug("Requests remaining: %d", self.accuweather.requests_remaining)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _get_device_info(location_key: str, name: str) -> DeviceInfo:
|
||||
"""Get device info."""
|
||||
return DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, location_key)},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=name,
|
||||
# You don't need to provide specific details for the URL,
|
||||
# so passing in _ characters is fine if the location key
|
||||
# is correct
|
||||
configuration_url=(
|
||||
"http://accuweather.com/en/"
|
||||
f"_/_/{location_key}/weather-forecast/{location_key}/"
|
||||
),
|
||||
)
|
|
@ -9,7 +9,7 @@ from homeassistant.config_entries import ConfigEntry
|
|||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import AccuWeatherDataUpdateCoordinator
|
||||
from . import AccuWeatherData
|
||||
from .const import DOMAIN
|
||||
|
||||
TO_REDACT = {CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE}
|
||||
|
@ -19,11 +19,9 @@ async def async_get_config_entry_diagnostics(
|
|||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: AccuWeatherDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
]
|
||||
accuweather_data: AccuWeatherData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
return {
|
||||
"config_entry_data": async_redact_data(dict(config_entry.data), TO_REDACT),
|
||||
"coordinator_data": coordinator.data,
|
||||
"observation_data": accuweather_data.coordinator_observation.data,
|
||||
}
|
||||
|
|
|
@ -28,13 +28,12 @@ from homeassistant.core import HomeAssistant, callback
|
|||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import AccuWeatherDataUpdateCoordinator
|
||||
from . import AccuWeatherData
|
||||
from .const import (
|
||||
API_METRIC,
|
||||
ATTR_CATEGORY,
|
||||
ATTR_DIRECTION,
|
||||
ATTR_ENGLISH,
|
||||
ATTR_FORECAST,
|
||||
ATTR_LEVEL,
|
||||
ATTR_SPEED,
|
||||
ATTR_VALUE,
|
||||
|
@ -42,6 +41,10 @@ from .const import (
|
|||
DOMAIN,
|
||||
MAX_FORECAST_DAYS,
|
||||
)
|
||||
from .coordinator import (
|
||||
AccuWeatherDailyForecastDataUpdateCoordinator,
|
||||
AccuWeatherObservationDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
|
@ -52,12 +55,18 @@ class AccuWeatherSensorDescription(SensorEntityDescription):
|
|||
|
||||
value_fn: Callable[[dict[str, Any]], str | int | float | None]
|
||||
attr_fn: Callable[[dict[str, Any]], dict[str, Any]] = lambda _: {}
|
||||
day: int | None = None
|
||||
|
||||
|
||||
FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class AccuWeatherForecastSensorDescription(AccuWeatherSensorDescription):
|
||||
"""Class describing AccuWeather sensor entities."""
|
||||
|
||||
day: int
|
||||
|
||||
|
||||
FORECAST_SENSOR_TYPES: tuple[AccuWeatherForecastSensorDescription, ...] = (
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="AirQuality",
|
||||
icon="mdi:air-filter",
|
||||
value_fn=lambda data: cast(str, data[ATTR_CATEGORY]),
|
||||
|
@ -69,7 +78,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="CloudCoverDay",
|
||||
icon="mdi:weather-cloudy",
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -81,7 +90,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="CloudCoverNight",
|
||||
icon="mdi:weather-cloudy",
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -93,7 +102,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="Grass",
|
||||
icon="mdi:grass",
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -106,7 +115,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="HoursOfSun",
|
||||
icon="mdi:weather-partly-cloudy",
|
||||
native_unit_of_measurement=UnitOfTime.HOURS,
|
||||
|
@ -117,7 +126,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="LongPhraseDay",
|
||||
value_fn=lambda data: cast(str, data),
|
||||
translation_key=f"condition_day_{day}d",
|
||||
|
@ -126,7 +135,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="LongPhraseNight",
|
||||
value_fn=lambda data: cast(str, data),
|
||||
translation_key=f"condition_night_{day}d",
|
||||
|
@ -135,7 +144,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="Mold",
|
||||
icon="mdi:blur",
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -148,7 +157,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="Ragweed",
|
||||
icon="mdi:sprout",
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
|
@ -161,7 +170,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="RealFeelTemperatureMax",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
|
@ -172,7 +181,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="RealFeelTemperatureMin",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
|
@ -183,7 +192,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="RealFeelTemperatureShadeMax",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -195,7 +204,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="RealFeelTemperatureShadeMin",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -207,7 +216,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="SolarIrradianceDay",
|
||||
icon="mdi:weather-sunny",
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -219,7 +228,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="SolarIrradianceNight",
|
||||
icon="mdi:weather-sunny",
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -231,7 +240,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="ThunderstormProbabilityDay",
|
||||
icon="mdi:weather-lightning",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
|
@ -242,7 +251,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="ThunderstormProbabilityNight",
|
||||
icon="mdi:weather-lightning",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
|
@ -253,7 +262,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="Tree",
|
||||
icon="mdi:tree-outline",
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
|
@ -266,7 +275,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="UVIndex",
|
||||
icon="mdi:weather-sunny",
|
||||
native_unit_of_measurement=UV_INDEX,
|
||||
|
@ -278,7 +287,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="WindGustDay",
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -291,7 +300,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="WindGustNight",
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
entity_registry_enabled_default=False,
|
||||
|
@ -304,7 +313,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="WindDay",
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
|
@ -316,7 +325,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
|
|||
for day in range(MAX_FORECAST_DAYS + 1)
|
||||
),
|
||||
*(
|
||||
AccuWeatherSensorDescription(
|
||||
AccuWeatherForecastSensorDescription(
|
||||
key="WindNight",
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
|
@ -453,25 +462,33 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Add AccuWeather entities from a config_entry."""
|
||||
|
||||
coordinator: AccuWeatherDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
accuweather_data: AccuWeatherData = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
sensors = [
|
||||
AccuWeatherSensor(coordinator, description) for description in SENSOR_TYPES
|
||||
observation_coordinator: AccuWeatherObservationDataUpdateCoordinator = (
|
||||
accuweather_data.coordinator_observation
|
||||
)
|
||||
forecast_daily_coordinator: AccuWeatherDailyForecastDataUpdateCoordinator = (
|
||||
accuweather_data.coordinator_daily_forecast
|
||||
)
|
||||
|
||||
sensors: list[AccuWeatherSensor | AccuWeatherForecastSensor] = [
|
||||
AccuWeatherSensor(observation_coordinator, description)
|
||||
for description in SENSOR_TYPES
|
||||
]
|
||||
|
||||
if coordinator.forecast:
|
||||
for description in FORECAST_SENSOR_TYPES:
|
||||
# Some air quality/allergy sensors are only available for certain
|
||||
# locations.
|
||||
if description.key not in coordinator.data[ATTR_FORECAST][description.day]:
|
||||
continue
|
||||
sensors.append(AccuWeatherSensor(coordinator, description))
|
||||
sensors.extend(
|
||||
[
|
||||
AccuWeatherForecastSensor(forecast_daily_coordinator, description)
|
||||
for description in FORECAST_SENSOR_TYPES
|
||||
if description.key in forecast_daily_coordinator.data[description.day]
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class AccuWeatherSensor(
|
||||
CoordinatorEntity[AccuWeatherDataUpdateCoordinator], SensorEntity
|
||||
CoordinatorEntity[AccuWeatherObservationDataUpdateCoordinator], SensorEntity
|
||||
):
|
||||
"""Define an AccuWeather entity."""
|
||||
|
||||
|
@ -481,22 +498,15 @@ class AccuWeatherSensor(
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AccuWeatherDataUpdateCoordinator,
|
||||
coordinator: AccuWeatherObservationDataUpdateCoordinator,
|
||||
description: AccuWeatherSensorDescription,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
self.forecast_day = description.day
|
||||
|
||||
self.entity_description = description
|
||||
self._sensor_data = _get_sensor_data(
|
||||
coordinator.data, description.key, self.forecast_day
|
||||
)
|
||||
if self.forecast_day is not None:
|
||||
self._attr_unique_id = f"{coordinator.location_key}-{description.key}-{self.forecast_day}".lower()
|
||||
else:
|
||||
self._attr_unique_id = (
|
||||
f"{coordinator.location_key}-{description.key}".lower()
|
||||
)
|
||||
self._sensor_data = self._get_sensor_data(coordinator.data, description.key)
|
||||
self._attr_unique_id = f"{coordinator.location_key}-{description.key}".lower()
|
||||
self._attr_device_info = coordinator.device_info
|
||||
|
||||
@property
|
||||
|
@ -507,30 +517,78 @@ class AccuWeatherSensor(
|
|||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
if self.forecast_day is not None:
|
||||
return self.entity_description.attr_fn(self._sensor_data)
|
||||
|
||||
return self.entity_description.attr_fn(self.coordinator.data)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle data update."""
|
||||
self._sensor_data = _get_sensor_data(
|
||||
self._sensor_data = self._get_sensor_data(
|
||||
self.coordinator.data, self.entity_description.key
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@staticmethod
|
||||
def _get_sensor_data(
|
||||
sensors: dict[str, Any],
|
||||
kind: str,
|
||||
) -> Any:
|
||||
"""Get sensor data."""
|
||||
if kind == "Precipitation":
|
||||
return sensors["PrecipitationSummary"]["PastHour"]
|
||||
|
||||
return sensors[kind]
|
||||
|
||||
|
||||
class AccuWeatherForecastSensor(
|
||||
CoordinatorEntity[AccuWeatherDailyForecastDataUpdateCoordinator], SensorEntity
|
||||
):
|
||||
"""Define an AccuWeather entity."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
_attr_has_entity_name = True
|
||||
entity_description: AccuWeatherForecastSensorDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AccuWeatherDailyForecastDataUpdateCoordinator,
|
||||
description: AccuWeatherForecastSensorDescription,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self.forecast_day = description.day
|
||||
self.entity_description = description
|
||||
self._sensor_data = self._get_sensor_data(
|
||||
coordinator.data, description.key, self.forecast_day
|
||||
)
|
||||
self._attr_unique_id = (
|
||||
f"{coordinator.location_key}-{description.key}-{self.forecast_day}".lower()
|
||||
)
|
||||
self._attr_device_info = coordinator.device_info
|
||||
|
||||
@property
|
||||
def native_value(self) -> str | int | float | None:
|
||||
"""Return the state."""
|
||||
return self.entity_description.value_fn(self._sensor_data)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
return self.entity_description.attr_fn(self._sensor_data)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle data update."""
|
||||
self._sensor_data = self._get_sensor_data(
|
||||
self.coordinator.data, self.entity_description.key, self.forecast_day
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
def _get_sensor_data(
|
||||
sensors: dict[str, Any],
|
||||
kind: str,
|
||||
forecast_day: int | None = None,
|
||||
) -> Any:
|
||||
"""Get sensor data."""
|
||||
if forecast_day is not None:
|
||||
return sensors[ATTR_FORECAST][forecast_day][kind]
|
||||
|
||||
if kind == "Precipitation":
|
||||
return sensors["PrecipitationSummary"]["PastHour"]
|
||||
|
||||
return sensors[kind]
|
||||
@staticmethod
|
||||
def _get_sensor_data(
|
||||
sensors: list[dict[str, Any]],
|
||||
kind: str,
|
||||
forecast_day: int,
|
||||
) -> Any:
|
||||
"""Get sensor data."""
|
||||
return sensors[forecast_day][kind]
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Some sensors are not enabled by default. You can enable them in the entity registry after the integration configuration.\nWeather forecast is not enabled by default. You can enable it in the integration options."
|
||||
"default": "Some sensors are not enabled by default. You can enable them in the entity registry after the integration configuration."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
|
@ -790,16 +790,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes.",
|
||||
"data": {
|
||||
"forecast": "Weather forecast"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"system_health": {
|
||||
"info": {
|
||||
"can_reach_server": "Reach AccuWeather server",
|
||||
|
|
|
@ -17,8 +17,8 @@ from homeassistant.components.weather import (
|
|||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_UV_INDEX,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
CoordinatorWeatherEntity,
|
||||
Forecast,
|
||||
SingleCoordinatorWeatherEntity,
|
||||
WeatherEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
@ -31,19 +31,23 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import TimestampDataUpdateCoordinator
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
|
||||
from . import AccuWeatherDataUpdateCoordinator
|
||||
from . import AccuWeatherData
|
||||
from .const import (
|
||||
API_METRIC,
|
||||
ATTR_DIRECTION,
|
||||
ATTR_FORECAST,
|
||||
ATTR_SPEED,
|
||||
ATTR_VALUE,
|
||||
ATTRIBUTION,
|
||||
CONDITION_MAP,
|
||||
DOMAIN,
|
||||
)
|
||||
from .coordinator import (
|
||||
AccuWeatherDailyForecastDataUpdateCoordinator,
|
||||
AccuWeatherObservationDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
|
@ -52,106 +56,134 @@ async def async_setup_entry(
|
|||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Add a AccuWeather weather entity from a config_entry."""
|
||||
accuweather_data: AccuWeatherData = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
coordinator: AccuWeatherDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities([AccuWeatherEntity(coordinator)])
|
||||
async_add_entities([AccuWeatherEntity(accuweather_data)])
|
||||
|
||||
|
||||
class AccuWeatherEntity(
|
||||
SingleCoordinatorWeatherEntity[AccuWeatherDataUpdateCoordinator]
|
||||
CoordinatorWeatherEntity[
|
||||
AccuWeatherObservationDataUpdateCoordinator,
|
||||
AccuWeatherDailyForecastDataUpdateCoordinator,
|
||||
TimestampDataUpdateCoordinator,
|
||||
TimestampDataUpdateCoordinator,
|
||||
]
|
||||
):
|
||||
"""Define an AccuWeather entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, coordinator: AccuWeatherDataUpdateCoordinator) -> None:
|
||||
def __init__(self, accuweather_data: AccuWeatherData) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
super().__init__(
|
||||
observation_coordinator=accuweather_data.coordinator_observation,
|
||||
daily_coordinator=accuweather_data.coordinator_daily_forecast,
|
||||
)
|
||||
|
||||
self._attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS
|
||||
self._attr_native_pressure_unit = UnitOfPressure.HPA
|
||||
self._attr_native_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
self._attr_native_visibility_unit = UnitOfLength.KILOMETERS
|
||||
self._attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR
|
||||
self._attr_unique_id = coordinator.location_key
|
||||
self._attr_unique_id = accuweather_data.coordinator_observation.location_key
|
||||
self._attr_attribution = ATTRIBUTION
|
||||
self._attr_device_info = coordinator.device_info
|
||||
if self.coordinator.forecast:
|
||||
self._attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
|
||||
self._attr_device_info = accuweather_data.coordinator_observation.device_info
|
||||
self._attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
|
||||
|
||||
self.observation_coordinator = accuweather_data.coordinator_observation
|
||||
self.daily_coordinator = accuweather_data.coordinator_daily_forecast
|
||||
|
||||
@property
|
||||
def condition(self) -> str | None:
|
||||
"""Return the current condition."""
|
||||
return CONDITION_MAP.get(self.coordinator.data["WeatherIcon"])
|
||||
return CONDITION_MAP.get(self.observation_coordinator.data["WeatherIcon"])
|
||||
|
||||
@property
|
||||
def cloud_coverage(self) -> float:
|
||||
"""Return the Cloud coverage in %."""
|
||||
return cast(float, self.coordinator.data["CloudCover"])
|
||||
return cast(float, self.observation_coordinator.data["CloudCover"])
|
||||
|
||||
@property
|
||||
def native_apparent_temperature(self) -> float:
|
||||
"""Return the apparent temperature."""
|
||||
return cast(
|
||||
float, self.coordinator.data["ApparentTemperature"][API_METRIC][ATTR_VALUE]
|
||||
float,
|
||||
self.observation_coordinator.data["ApparentTemperature"][API_METRIC][
|
||||
ATTR_VALUE
|
||||
],
|
||||
)
|
||||
|
||||
@property
|
||||
def native_temperature(self) -> float:
|
||||
"""Return the temperature."""
|
||||
return cast(float, self.coordinator.data["Temperature"][API_METRIC][ATTR_VALUE])
|
||||
return cast(
|
||||
float,
|
||||
self.observation_coordinator.data["Temperature"][API_METRIC][ATTR_VALUE],
|
||||
)
|
||||
|
||||
@property
|
||||
def native_pressure(self) -> float:
|
||||
"""Return the pressure."""
|
||||
return cast(float, self.coordinator.data["Pressure"][API_METRIC][ATTR_VALUE])
|
||||
return cast(
|
||||
float, self.observation_coordinator.data["Pressure"][API_METRIC][ATTR_VALUE]
|
||||
)
|
||||
|
||||
@property
|
||||
def native_dew_point(self) -> float:
|
||||
"""Return the dew point."""
|
||||
return cast(float, self.coordinator.data["DewPoint"][API_METRIC][ATTR_VALUE])
|
||||
return cast(
|
||||
float, self.observation_coordinator.data["DewPoint"][API_METRIC][ATTR_VALUE]
|
||||
)
|
||||
|
||||
@property
|
||||
def humidity(self) -> int:
|
||||
"""Return the humidity."""
|
||||
return cast(int, self.coordinator.data["RelativeHumidity"])
|
||||
return cast(int, self.observation_coordinator.data["RelativeHumidity"])
|
||||
|
||||
@property
|
||||
def native_wind_gust_speed(self) -> float:
|
||||
"""Return the wind gust speed."""
|
||||
return cast(
|
||||
float, self.coordinator.data["WindGust"][ATTR_SPEED][API_METRIC][ATTR_VALUE]
|
||||
float,
|
||||
self.observation_coordinator.data["WindGust"][ATTR_SPEED][API_METRIC][
|
||||
ATTR_VALUE
|
||||
],
|
||||
)
|
||||
|
||||
@property
|
||||
def native_wind_speed(self) -> float:
|
||||
"""Return the wind speed."""
|
||||
return cast(
|
||||
float, self.coordinator.data["Wind"][ATTR_SPEED][API_METRIC][ATTR_VALUE]
|
||||
float,
|
||||
self.observation_coordinator.data["Wind"][ATTR_SPEED][API_METRIC][
|
||||
ATTR_VALUE
|
||||
],
|
||||
)
|
||||
|
||||
@property
|
||||
def wind_bearing(self) -> int:
|
||||
"""Return the wind bearing."""
|
||||
return cast(int, self.coordinator.data["Wind"][ATTR_DIRECTION]["Degrees"])
|
||||
return cast(
|
||||
int, self.observation_coordinator.data["Wind"][ATTR_DIRECTION]["Degrees"]
|
||||
)
|
||||
|
||||
@property
|
||||
def native_visibility(self) -> float:
|
||||
"""Return the visibility."""
|
||||
return cast(float, self.coordinator.data["Visibility"][API_METRIC][ATTR_VALUE])
|
||||
return cast(
|
||||
float,
|
||||
self.observation_coordinator.data["Visibility"][API_METRIC][ATTR_VALUE],
|
||||
)
|
||||
|
||||
@property
|
||||
def uv_index(self) -> float:
|
||||
"""Return the UV index."""
|
||||
return cast(float, self.coordinator.data["UVIndex"])
|
||||
return cast(float, self.observation_coordinator.data["UVIndex"])
|
||||
|
||||
@callback
|
||||
def _async_forecast_daily(self) -> list[Forecast] | None:
|
||||
"""Return the daily forecast in native units."""
|
||||
if not self.coordinator.forecast:
|
||||
return None
|
||||
# remap keys from library to keys understood by the weather component
|
||||
return [
|
||||
{
|
||||
ATTR_FORECAST_TIME: utc_from_timestamp(item["EpochDate"]).isoformat(),
|
||||
|
@ -175,5 +207,5 @@ class AccuWeatherEntity(
|
|||
ATTR_FORECAST_WIND_BEARING: item["WindDay"][ATTR_DIRECTION]["Degrees"],
|
||||
ATTR_FORECAST_CONDITION: CONDITION_MAP.get(item["IconDay"]),
|
||||
}
|
||||
for item in self.coordinator.data[ATTR_FORECAST]
|
||||
for item in self.daily_coordinator.data
|
||||
]
|
||||
|
|
|
@ -11,14 +11,8 @@ from tests.common import (
|
|||
)
|
||||
|
||||
|
||||
async def init_integration(
|
||||
hass, forecast=False, unsupported_icon=False
|
||||
) -> MockConfigEntry:
|
||||
async def init_integration(hass, unsupported_icon=False) -> MockConfigEntry:
|
||||
"""Set up the AccuWeather integration in Home Assistant."""
|
||||
options = {}
|
||||
if forecast:
|
||||
options["forecast"] = True
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="Home",
|
||||
|
@ -29,7 +23,6 @@ async def init_integration(
|
|||
"longitude": 122.12,
|
||||
"name": "Home",
|
||||
},
|
||||
options=options,
|
||||
)
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
'longitude': '**REDACTED**',
|
||||
'name': 'Home',
|
||||
}),
|
||||
'coordinator_data': dict({
|
||||
'observation_data': dict({
|
||||
'ApparentTemperature': dict({
|
||||
'Imperial': dict({
|
||||
'Unit': 'F',
|
||||
|
@ -297,8 +297,6 @@
|
|||
}),
|
||||
}),
|
||||
}),
|
||||
'forecast': list([
|
||||
]),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
|
|
6436
tests/components/accuweather/snapshots/test_sensor.ambr
Normal file
6436
tests/components/accuweather/snapshots/test_sensor.ambr
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,10 @@
|
|||
"""Define tests for the AccuWeather config flow."""
|
||||
|
||||
from unittest.mock import PropertyMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
from accuweather import ApiError, InvalidApiKeyError, RequestsExceededError
|
||||
|
||||
from homeassistant.components.accuweather.const import CONF_FORECAST, DOMAIN
|
||||
from homeassistant.components.accuweather.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
@ -140,52 +140,3 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
|||
assert result["data"][CONF_LATITUDE] == 55.55
|
||||
assert result["data"][CONF_LONGITUDE] == 122.12
|
||||
assert result["data"][CONF_API_KEY] == "32-character-string-1234567890qw"
|
||||
|
||||
|
||||
async def test_options_flow(hass: HomeAssistant) -> None:
|
||||
"""Test config flow options."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="123456",
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||
return_value=load_json_object_fixture("accuweather/location_data.json"),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=load_json_object_fixture(
|
||||
"accuweather/current_conditions_data.json"
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast"
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={CONF_FORECAST: True}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert config_entry.options == {CONF_FORECAST: True}
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
|
|
@ -6,7 +6,6 @@ from homeassistant.core import HomeAssistant
|
|||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import load_json_object_fixture
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
@ -19,12 +18,6 @@ async def test_entry_diagnostics(
|
|||
"""Test config entry diagnostics."""
|
||||
entry = await init_integration(hass)
|
||||
|
||||
coordinator_data = load_json_object_fixture(
|
||||
"current_conditions_data.json", "accuweather"
|
||||
)
|
||||
|
||||
coordinator_data["forecast"] = []
|
||||
|
||||
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
|
||||
|
||||
assert result == snapshot
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
"""Test init of AccuWeather integration."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from accuweather import ApiError
|
||||
|
||||
from homeassistant.components.accuweather.const import DOMAIN
|
||||
from homeassistant.components.accuweather.const import (
|
||||
DOMAIN,
|
||||
UPDATE_INTERVAL_DAILY_FORECAST,
|
||||
UPDATE_INTERVAL_OBSERVATION,
|
||||
)
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_PLATFORM
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
|
@ -76,30 +79,8 @@ async def test_update_interval(hass: HomeAssistant) -> None:
|
|||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
future = utcnow() + timedelta(minutes=40)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
) as mock_current:
|
||||
assert mock_current.call_count == 0
|
||||
|
||||
async_fire_time_changed(hass, future)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_current.call_count == 1
|
||||
|
||||
|
||||
async def test_update_interval_forecast(hass: HomeAssistant) -> None:
|
||||
"""Test correct update interval when forecast is True."""
|
||||
entry = await init_integration(hass, forecast=True)
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||
future = utcnow() + timedelta(minutes=80)
|
||||
|
||||
with (
|
||||
patch(
|
||||
|
@ -114,10 +95,14 @@ async def test_update_interval_forecast(hass: HomeAssistant) -> None:
|
|||
assert mock_current.call_count == 0
|
||||
assert mock_forecast.call_count == 0
|
||||
|
||||
async_fire_time_changed(hass, future)
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL_OBSERVATION)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_current.call_count == 1
|
||||
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_forecast.call_count == 1
|
||||
|
||||
|
||||
|
|
|
@ -3,29 +3,20 @@
|
|||
from datetime import timedelta
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
from homeassistant.components.accuweather.const import ATTRIBUTION
|
||||
from homeassistant.components.sensor import (
|
||||
ATTR_OPTIONS,
|
||||
ATTR_STATE_CLASS,
|
||||
SensorDeviceClass,
|
||||
SensorStateClass,
|
||||
)
|
||||
from accuweather import ApiError, InvalidApiKeyError, RequestsExceededError
|
||||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.accuweather.const import UPDATE_INTERVAL_DAILY_FORECAST
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_ICON,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
PERCENTAGE,
|
||||
STATE_UNAVAILABLE,
|
||||
UV_INDEX,
|
||||
UnitOfIrradiance,
|
||||
Platform,
|
||||
UnitOfLength,
|
||||
UnitOfSpeed,
|
||||
UnitOfTemperature,
|
||||
UnitOfTime,
|
||||
UnitOfVolumetricFlux,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
@ -42,517 +33,23 @@ from tests.common import (
|
|||
)
|
||||
|
||||
|
||||
async def test_sensor_without_forecast(
|
||||
async def test_sensor(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test states of the sensor without forecast."""
|
||||
await init_integration(hass)
|
||||
"""Test states of the sensor."""
|
||||
with patch("homeassistant.components.accuweather.PLATFORMS", [Platform.SENSOR]):
|
||||
entry = await init_integration(hass)
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_ceiling")
|
||||
assert state
|
||||
assert state.state == "3200.0"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-fog"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfLength.METERS
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.DISTANCE
|
||||
entity_entries = er.async_entries_for_config_entry(entity_registry, entry.entry_id)
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_cloud_ceiling")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-ceiling"
|
||||
assert entry.options["sensor"] == {"suggested_display_precision": 0}
|
||||
|
||||
state = hass.states.get("sensor.home_precipitation")
|
||||
assert state
|
||||
assert state.state == "0.0"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR
|
||||
)
|
||||
assert state.attributes.get(ATTR_ICON) is None
|
||||
assert state.attributes.get("type") is None
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
assert (
|
||||
state.attributes.get(ATTR_DEVICE_CLASS)
|
||||
== SensorDeviceClass.PRECIPITATION_INTENSITY
|
||||
)
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_precipitation")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-precipitation"
|
||||
|
||||
state = hass.states.get("sensor.home_pressure_tendency")
|
||||
assert state
|
||||
assert state.state == "falling"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:gauge"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
assert state.attributes.get(ATTR_OPTIONS) == ["falling", "rising", "steady"]
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_pressure_tendency")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-pressuretendency"
|
||||
assert entry.translation_key == "pressure_tendency"
|
||||
|
||||
state = hass.states.get("sensor.home_realfeel_temperature")
|
||||
assert state
|
||||
assert state.state == "25.1"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_realfeel_temperature")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-realfeeltemperature"
|
||||
|
||||
state = hass.states.get("sensor.home_uv_index")
|
||||
assert state
|
||||
assert state.state == "6"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UV_INDEX
|
||||
assert state.attributes.get("level") == "High"
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_uv_index")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-uvindex"
|
||||
|
||||
state = hass.states.get("sensor.home_apparent_temperature")
|
||||
assert state
|
||||
assert state.state == "22.8"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_apparent_temperature")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-apparenttemperature"
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_cover")
|
||||
assert state
|
||||
assert state.state == "10"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-cloudy"
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_cloud_cover")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-cloudcover"
|
||||
|
||||
state = hass.states.get("sensor.home_dew_point")
|
||||
assert state
|
||||
assert state.state == "16.2"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_dew_point")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-dewpoint"
|
||||
|
||||
state = hass.states.get("sensor.home_realfeel_temperature_shade")
|
||||
assert state
|
||||
assert state.state == "21.1"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_realfeel_temperature_shade")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-realfeeltemperatureshade"
|
||||
|
||||
state = hass.states.get("sensor.home_wet_bulb_temperature")
|
||||
assert state
|
||||
assert state.state == "18.6"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_wet_bulb_temperature")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-wetbulbtemperature"
|
||||
|
||||
state = hass.states.get("sensor.home_wind_chill_temperature")
|
||||
assert state
|
||||
assert state.state == "22.8"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_wind_chill_temperature")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-windchilltemperature"
|
||||
|
||||
state = hass.states.get("sensor.home_wind_gust_speed")
|
||||
assert state
|
||||
assert state.state == "20.3"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfSpeed.KILOMETERS_PER_HOUR
|
||||
)
|
||||
assert state.attributes.get(ATTR_ICON) is None
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_wind_gust_speed")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-windgust"
|
||||
|
||||
state = hass.states.get("sensor.home_wind_speed")
|
||||
assert state
|
||||
assert state.state == "14.5"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfSpeed.KILOMETERS_PER_HOUR
|
||||
)
|
||||
assert state.attributes.get(ATTR_ICON) is None
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_wind_speed")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-wind"
|
||||
|
||||
|
||||
async def test_sensor_with_forecast(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test states of the sensor with forecast."""
|
||||
await init_integration(hass, forecast=True)
|
||||
|
||||
state = hass.states.get("sensor.home_hours_of_sun_today")
|
||||
assert state
|
||||
assert state.state == "7.2"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-partly-cloudy"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTime.HOURS
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_hours_of_sun_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-hoursofsun-0"
|
||||
|
||||
state = hass.states.get("sensor.home_realfeel_temperature_max_today")
|
||||
assert state
|
||||
assert state.state == "29.8"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_realfeel_temperature_max_today")
|
||||
assert entry
|
||||
|
||||
state = hass.states.get("sensor.home_realfeel_temperature_min_today")
|
||||
assert state
|
||||
assert state.state == "15.1"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_realfeel_temperature_min_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-realfeeltemperaturemin-0"
|
||||
|
||||
state = hass.states.get("sensor.home_thunderstorm_probability_today")
|
||||
assert state
|
||||
assert state.state == "40"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-lightning"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_thunderstorm_probability_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-thunderstormprobabilityday-0"
|
||||
|
||||
state = hass.states.get("sensor.home_thunderstorm_probability_tonight")
|
||||
assert state
|
||||
assert state.state == "40"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-lightning"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_thunderstorm_probability_tonight")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-thunderstormprobabilitynight-0"
|
||||
|
||||
state = hass.states.get("sensor.home_uv_index_today")
|
||||
assert state
|
||||
assert state.state == "5"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-sunny"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UV_INDEX
|
||||
assert state.attributes.get("level") == "moderate"
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_uv_index_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-uvindex-0"
|
||||
|
||||
state = hass.states.get("sensor.home_air_quality_today")
|
||||
assert state
|
||||
assert state.state == "good"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:air-filter"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM
|
||||
assert state.attributes.get(ATTR_OPTIONS) == [
|
||||
"good",
|
||||
"hazardous",
|
||||
"high",
|
||||
"low",
|
||||
"moderate",
|
||||
"unhealthy",
|
||||
]
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_cover_today")
|
||||
assert state
|
||||
assert state.state == "58"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-cloudy"
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_cloud_cover_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-cloudcoverday-0"
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_cover_tonight")
|
||||
assert state
|
||||
assert state.state == "65"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-cloudy"
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_cloud_cover_tonight")
|
||||
assert entry
|
||||
|
||||
state = hass.states.get("sensor.home_grass_pollen_today")
|
||||
assert state
|
||||
assert state.state == "0"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== CONCENTRATION_PARTS_PER_CUBIC_METER
|
||||
)
|
||||
assert state.attributes.get("level") == "low"
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:grass"
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_grass_pollen_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-grass-0"
|
||||
|
||||
state = hass.states.get("sensor.home_mold_pollen_today")
|
||||
assert state
|
||||
assert state.state == "0"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== CONCENTRATION_PARTS_PER_CUBIC_METER
|
||||
)
|
||||
assert state.attributes.get("level") == "low"
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:blur"
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_mold_pollen_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-mold-0"
|
||||
|
||||
state = hass.states.get("sensor.home_ragweed_pollen_today")
|
||||
assert state
|
||||
assert state.state == "0"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== CONCENTRATION_PARTS_PER_CUBIC_METER
|
||||
)
|
||||
assert state.attributes.get("level") == "low"
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:sprout"
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_ragweed_pollen_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-ragweed-0"
|
||||
|
||||
state = hass.states.get("sensor.home_realfeel_temperature_shade_max_today")
|
||||
assert state
|
||||
assert state.state == "28.0"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get(
|
||||
"sensor.home_realfeel_temperature_shade_max_today"
|
||||
)
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-realfeeltemperatureshademax-0"
|
||||
|
||||
state = hass.states.get("sensor.home_realfeel_temperature_shade_min_today")
|
||||
assert state
|
||||
assert state.state == "15.1"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
|
||||
entry = entity_registry.async_get(
|
||||
"sensor.home_realfeel_temperature_shade_min_today"
|
||||
)
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-realfeeltemperatureshademin-0"
|
||||
|
||||
state = hass.states.get("sensor.home_tree_pollen_today")
|
||||
assert state
|
||||
assert state.state == "0"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== CONCENTRATION_PARTS_PER_CUBIC_METER
|
||||
)
|
||||
assert state.attributes.get("level") == "low"
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:tree-outline"
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_tree_pollen_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-tree-0"
|
||||
|
||||
state = hass.states.get("sensor.home_wind_speed_today")
|
||||
assert state
|
||||
assert state.state == "13.0"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfSpeed.KILOMETERS_PER_HOUR
|
||||
)
|
||||
assert state.attributes.get("direction") == "SSE"
|
||||
assert state.attributes.get(ATTR_ICON) is None
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_wind_speed_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-windday-0"
|
||||
|
||||
state = hass.states.get("sensor.home_wind_speed_tonight")
|
||||
assert state
|
||||
assert state.state == "7.4"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfSpeed.KILOMETERS_PER_HOUR
|
||||
)
|
||||
assert state.attributes.get("direction") == "WNW"
|
||||
assert state.attributes.get(ATTR_ICON) is None
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_wind_speed_tonight")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-windnight-0"
|
||||
|
||||
state = hass.states.get("sensor.home_wind_gust_speed_today")
|
||||
assert state
|
||||
assert state.state == "29.6"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfSpeed.KILOMETERS_PER_HOUR
|
||||
)
|
||||
assert state.attributes.get("direction") == "S"
|
||||
assert state.attributes.get(ATTR_ICON) is None
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_wind_gust_speed_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-windgustday-0"
|
||||
|
||||
state = hass.states.get("sensor.home_wind_gust_speed_tonight")
|
||||
assert state
|
||||
assert state.state == "18.5"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfSpeed.KILOMETERS_PER_HOUR
|
||||
)
|
||||
assert state.attributes.get("direction") == "WSW"
|
||||
assert state.attributes.get(ATTR_ICON) is None
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) is None
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_wind_gust_speed_tonight")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-windgustnight-0"
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_air_quality_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-airquality-0"
|
||||
|
||||
state = hass.states.get("sensor.home_solar_irradiance_today")
|
||||
assert state
|
||||
assert state.state == "7447.1"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-sunny"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfIrradiance.WATTS_PER_SQUARE_METER
|
||||
)
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_solar_irradiance_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-solarirradianceday-0"
|
||||
|
||||
state = hass.states.get("sensor.home_solar_irradiance_tonight")
|
||||
assert state
|
||||
assert state.state == "271.6"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:weather-sunny"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfIrradiance.WATTS_PER_SQUARE_METER
|
||||
)
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_solar_irradiance_tonight")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-solarirradiancenight-0"
|
||||
|
||||
state = hass.states.get("sensor.home_condition_today")
|
||||
assert state
|
||||
assert (
|
||||
state.state
|
||||
== "Clouds and sunshine with a couple of showers and a thunderstorm around late this afternoon"
|
||||
)
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_condition_today")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-longphraseday-0"
|
||||
|
||||
state = hass.states.get("sensor.home_condition_tonight")
|
||||
assert state
|
||||
assert state.state == "Partly cloudy"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
|
||||
entry = entity_registry.async_get("sensor.home_condition_tonight")
|
||||
assert entry
|
||||
assert entry.unique_id == "0123456-longphrasenight-0"
|
||||
assert entity_entries
|
||||
for entity_entry in entity_entries:
|
||||
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
|
||||
assert (state := hass.states.get(entity_entry.entity_id))
|
||||
assert state == snapshot(name=f"{entity_entry.entity_id}-state")
|
||||
|
||||
|
||||
async def test_availability(hass: HomeAssistant) -> None:
|
||||
|
@ -599,24 +96,88 @@ async def test_availability(hass: HomeAssistant) -> None:
|
|||
assert state.state == "3200.0"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"exception",
|
||||
[
|
||||
ApiError,
|
||||
ConnectionError,
|
||||
ClientConnectorError,
|
||||
InvalidApiKeyError,
|
||||
RequestsExceededError,
|
||||
],
|
||||
)
|
||||
async def test_availability_forecast(hass: HomeAssistant, exception: Exception) -> None:
|
||||
"""Ensure that we mark the entities unavailable correctly when service is offline."""
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||
entity_id = "sensor.home_hours_of_sun_day_2"
|
||||
|
||||
await init_integration(hass)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "5.7"
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
side_effect=exception,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
return_value=forecast,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL_DAILY_FORECAST * 2)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "5.7"
|
||||
|
||||
|
||||
async def test_manual_update_entity(hass: HomeAssistant) -> None:
|
||||
"""Test manual update entity via service homeassistant/update_entity."""
|
||||
await init_integration(hass, forecast=True)
|
||||
await init_integration(hass)
|
||||
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
) as mock_current,
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
return_value=forecast,
|
||||
) as mock_forecast,
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
|
@ -629,8 +190,7 @@ async def test_manual_update_entity(hass: HomeAssistant) -> None:
|
|||
{ATTR_ENTITY_ID: ["sensor.home_cloud_ceiling"]},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_current.call_count == 1
|
||||
assert mock_forecast.call_count == 1
|
||||
assert mock_current.call_count == 1
|
||||
|
||||
|
||||
async def test_sensor_imperial_units(hass: HomeAssistant) -> None:
|
||||
|
|
|
@ -7,7 +7,10 @@ from freezegun.api import FrozenDateTimeFactory
|
|||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.accuweather.const import ATTRIBUTION
|
||||
from homeassistant.components.accuweather.const import (
|
||||
ATTRIBUTION,
|
||||
UPDATE_INTERVAL_DAILY_FORECAST,
|
||||
)
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_WEATHER_APPARENT_TEMPERATURE,
|
||||
|
@ -24,6 +27,7 @@ from homeassistant.components.weather import (
|
|||
DOMAIN as WEATHER_DOMAIN,
|
||||
LEGACY_SERVICE_GET_FORECAST,
|
||||
SERVICE_GET_FORECASTS,
|
||||
WeatherEntityFeature,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
|
@ -65,7 +69,10 @@ async def test_weather(hass: HomeAssistant, entity_registry: er.EntityRegistry)
|
|||
assert state.attributes.get(ATTR_WEATHER_WIND_GUST_SPEED) == 20.3
|
||||
assert state.attributes.get(ATTR_WEATHER_UV_INDEX) == 6
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert ATTR_SUPPORTED_FEATURES not in state.attributes
|
||||
assert (
|
||||
state.attributes.get(ATTR_SUPPORTED_FEATURES)
|
||||
is WeatherEntityFeature.FORECAST_DAILY
|
||||
)
|
||||
|
||||
entry = entity_registry.async_get("weather.home")
|
||||
assert entry
|
||||
|
@ -118,22 +125,17 @@ async def test_availability(hass: HomeAssistant) -> None:
|
|||
|
||||
async def test_manual_update_entity(hass: HomeAssistant) -> None:
|
||||
"""Test manual update entity via service homeassistant/update_entity."""
|
||||
await init_integration(hass, forecast=True)
|
||||
await init_integration(hass)
|
||||
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
) as mock_current,
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
return_value=forecast,
|
||||
) as mock_forecast,
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
|
@ -147,12 +149,11 @@ async def test_manual_update_entity(hass: HomeAssistant) -> None:
|
|||
blocking=True,
|
||||
)
|
||||
assert mock_current.call_count == 1
|
||||
assert mock_forecast.call_count == 1
|
||||
|
||||
|
||||
async def test_unsupported_condition_icon_data(hass: HomeAssistant) -> None:
|
||||
"""Test with unsupported condition icon data."""
|
||||
await init_integration(hass, forecast=True, unsupported_icon=True)
|
||||
await init_integration(hass, unsupported_icon=True)
|
||||
|
||||
state = hass.states.get("weather.home")
|
||||
assert state.attributes.get(ATTR_FORECAST_CONDITION) is None
|
||||
|
@ -171,7 +172,7 @@ async def test_forecast_service(
|
|||
service: str,
|
||||
) -> None:
|
||||
"""Test multiple forecast."""
|
||||
await init_integration(hass, forecast=True)
|
||||
await init_integration(hass)
|
||||
|
||||
response = await hass.services.async_call(
|
||||
WEATHER_DOMAIN,
|
||||
|
@ -195,7 +196,7 @@ async def test_forecast_subscription(
|
|||
"""Test multiple forecast."""
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
await init_integration(hass, forecast=True)
|
||||
await init_integration(hass)
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
|
@ -235,7 +236,7 @@ async def test_forecast_subscription(
|
|||
return_value=10,
|
||||
),
|
||||
):
|
||||
freezer.tick(timedelta(minutes=80) + timedelta(seconds=1))
|
||||
freezer.tick(UPDATE_INTERVAL_DAILY_FORECAST + timedelta(seconds=1))
|
||||
await hass.async_block_till_done()
|
||||
msg = await client.receive_json()
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue