mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Modernize accuweather weather (#99001)
This commit is contained in:
parent
f96c1516f8
commit
27f7399071
@ -17,7 +17,8 @@ from homeassistant.components.weather import (
|
||||
ATTR_FORECAST_UV_INDEX,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
Forecast,
|
||||
WeatherEntity,
|
||||
SingleCoordinatorWeatherEntity,
|
||||
WeatherEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
@ -27,9 +28,8 @@ from homeassistant.const import (
|
||||
UnitOfSpeed,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
|
||||
from . import AccuWeatherDataUpdateCoordinator
|
||||
@ -58,7 +58,7 @@ async def async_setup_entry(
|
||||
|
||||
|
||||
class AccuWeatherEntity(
|
||||
CoordinatorEntity[AccuWeatherDataUpdateCoordinator], WeatherEntity
|
||||
SingleCoordinatorWeatherEntity[AccuWeatherDataUpdateCoordinator]
|
||||
):
|
||||
"""Define an AccuWeather entity."""
|
||||
|
||||
@ -76,6 +76,8 @@ class AccuWeatherEntity(
|
||||
self._attr_unique_id = coordinator.location_key
|
||||
self._attr_attribution = ATTRIBUTION
|
||||
self._attr_device_info = coordinator.device_info
|
||||
if self.coordinator.forecast:
|
||||
self._attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
|
||||
|
||||
@property
|
||||
def condition(self) -> str | None:
|
||||
@ -174,3 +176,8 @@ class AccuWeatherEntity(
|
||||
}
|
||||
for item in self.coordinator.data[ATTR_FORECAST]
|
||||
]
|
||||
|
||||
@callback
|
||||
def _async_forecast_daily(self) -> list[Forecast] | None:
|
||||
"""Return the daily forecast in native units."""
|
||||
return self.forecast
|
||||
|
225
tests/components/accuweather/snapshots/test_weather.ambr
Normal file
225
tests/components/accuweather/snapshots/test_weather.ambr
Normal file
@ -0,0 +1,225 @@
|
||||
# serializer version: 1
|
||||
# name: test_forecast_service
|
||||
dict({
|
||||
'forecast': list([
|
||||
dict({
|
||||
'apparent_temperature': 29.8,
|
||||
'cloud_coverage': 58,
|
||||
'condition': 'lightning-rainy',
|
||||
'datetime': '2020-07-26T05:00:00+00:00',
|
||||
'precipitation': 2.5,
|
||||
'precipitation_probability': 60,
|
||||
'temperature': 29.5,
|
||||
'templow': 15.4,
|
||||
'uv_index': 5,
|
||||
'wind_bearing': 166,
|
||||
'wind_gust_speed': 29.6,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 28.9,
|
||||
'cloud_coverage': 52,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-27T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 25,
|
||||
'temperature': 26.2,
|
||||
'templow': 15.9,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 297,
|
||||
'wind_gust_speed': 14.8,
|
||||
'wind_speed': 9.3,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 31.6,
|
||||
'cloud_coverage': 65,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-28T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 10,
|
||||
'temperature': 31.7,
|
||||
'templow': 16.8,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 198,
|
||||
'wind_gust_speed': 24.1,
|
||||
'wind_speed': 16.7,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 26.5,
|
||||
'cloud_coverage': 45,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-29T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 9,
|
||||
'temperature': 24.0,
|
||||
'templow': 11.7,
|
||||
'uv_index': 6,
|
||||
'wind_bearing': 293,
|
||||
'wind_gust_speed': 24.1,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 22.2,
|
||||
'cloud_coverage': 50,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-30T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 1,
|
||||
'temperature': 21.4,
|
||||
'templow': 12.2,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 280,
|
||||
'wind_gust_speed': 27.8,
|
||||
'wind_speed': 18.5,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_subscription
|
||||
list([
|
||||
dict({
|
||||
'apparent_temperature': 29.8,
|
||||
'cloud_coverage': 58,
|
||||
'condition': 'lightning-rainy',
|
||||
'datetime': '2020-07-26T05:00:00+00:00',
|
||||
'precipitation': 2.5,
|
||||
'precipitation_probability': 60,
|
||||
'temperature': 29.5,
|
||||
'templow': 15.4,
|
||||
'uv_index': 5,
|
||||
'wind_bearing': 166,
|
||||
'wind_gust_speed': 29.6,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 28.9,
|
||||
'cloud_coverage': 52,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-27T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 25,
|
||||
'temperature': 26.2,
|
||||
'templow': 15.9,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 297,
|
||||
'wind_gust_speed': 14.8,
|
||||
'wind_speed': 9.3,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 31.6,
|
||||
'cloud_coverage': 65,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-28T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 10,
|
||||
'temperature': 31.7,
|
||||
'templow': 16.8,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 198,
|
||||
'wind_gust_speed': 24.1,
|
||||
'wind_speed': 16.7,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 26.5,
|
||||
'cloud_coverage': 45,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-29T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 9,
|
||||
'temperature': 24.0,
|
||||
'templow': 11.7,
|
||||
'uv_index': 6,
|
||||
'wind_bearing': 293,
|
||||
'wind_gust_speed': 24.1,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 22.2,
|
||||
'cloud_coverage': 50,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-30T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 1,
|
||||
'temperature': 21.4,
|
||||
'templow': 12.2,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 280,
|
||||
'wind_gust_speed': 27.8,
|
||||
'wind_speed': 18.5,
|
||||
}),
|
||||
])
|
||||
# ---
|
||||
# name: test_forecast_subscription.1
|
||||
list([
|
||||
dict({
|
||||
'apparent_temperature': 29.8,
|
||||
'cloud_coverage': 58,
|
||||
'condition': 'lightning-rainy',
|
||||
'datetime': '2020-07-26T05:00:00+00:00',
|
||||
'precipitation': 2.5,
|
||||
'precipitation_probability': 60,
|
||||
'temperature': 29.5,
|
||||
'templow': 15.4,
|
||||
'uv_index': 5,
|
||||
'wind_bearing': 166,
|
||||
'wind_gust_speed': 29.6,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 28.9,
|
||||
'cloud_coverage': 52,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-27T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 25,
|
||||
'temperature': 26.2,
|
||||
'templow': 15.9,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 297,
|
||||
'wind_gust_speed': 14.8,
|
||||
'wind_speed': 9.3,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 31.6,
|
||||
'cloud_coverage': 65,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-28T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 10,
|
||||
'temperature': 31.7,
|
||||
'templow': 16.8,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 198,
|
||||
'wind_gust_speed': 24.1,
|
||||
'wind_speed': 16.7,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 26.5,
|
||||
'cloud_coverage': 45,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-29T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 9,
|
||||
'temperature': 24.0,
|
||||
'templow': 11.7,
|
||||
'uv_index': 6,
|
||||
'wind_bearing': 293,
|
||||
'wind_gust_speed': 24.1,
|
||||
'wind_speed': 13.0,
|
||||
}),
|
||||
dict({
|
||||
'apparent_temperature': 22.2,
|
||||
'cloud_coverage': 50,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2020-07-30T05:00:00+00:00',
|
||||
'precipitation': 0.0,
|
||||
'precipitation_probability': 1,
|
||||
'temperature': 21.4,
|
||||
'templow': 12.2,
|
||||
'uv_index': 7,
|
||||
'wind_bearing': 280,
|
||||
'wind_gust_speed': 27.8,
|
||||
'wind_speed': 18.5,
|
||||
}),
|
||||
])
|
||||
# ---
|
@ -2,6 +2,9 @@
|
||||
from datetime import timedelta
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.accuweather.const import ATTRIBUTION
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_FORECAST,
|
||||
@ -27,8 +30,16 @@ from homeassistant.components.weather import (
|
||||
ATTR_WEATHER_WIND_BEARING,
|
||||
ATTR_WEATHER_WIND_GUST_SPEED,
|
||||
ATTR_WEATHER_WIND_SPEED,
|
||||
DOMAIN as WEATHER_DOMAIN,
|
||||
SERVICE_GET_FORECAST,
|
||||
WeatherEntityFeature,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ENTITY_ID, STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
@ -41,6 +52,7 @@ from tests.common import (
|
||||
load_json_array_fixture,
|
||||
load_json_object_fixture,
|
||||
)
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
|
||||
async def test_weather_without_forecast(hass: HomeAssistant) -> None:
|
||||
@ -64,6 +76,7 @@ async def test_weather_without_forecast(hass: HomeAssistant) -> None:
|
||||
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
|
||||
|
||||
entry = registry.async_get("weather.home")
|
||||
assert entry
|
||||
@ -90,6 +103,9 @@ async def test_weather_with_forecast(hass: HomeAssistant) -> None:
|
||||
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 (
|
||||
state.attributes[ATTR_SUPPORTED_FEATURES] == WeatherEntityFeature.FORECAST_DAILY
|
||||
)
|
||||
forecast = state.attributes.get(ATTR_FORECAST)[0]
|
||||
assert forecast.get(ATTR_FORECAST_CONDITION) == "lightning-rainy"
|
||||
assert forecast.get(ATTR_FORECAST_PRECIPITATION) == 2.5
|
||||
@ -186,3 +202,81 @@ async def test_unsupported_condition_icon_data(hass: HomeAssistant) -> None:
|
||||
|
||||
state = hass.states.get("weather.home")
|
||||
assert state.attributes.get(ATTR_FORECAST_CONDITION) is None
|
||||
|
||||
|
||||
async def test_forecast_service(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test multiple forecast."""
|
||||
await init_integration(hass, forecast=True)
|
||||
|
||||
response = await hass.services.async_call(
|
||||
WEATHER_DOMAIN,
|
||||
SERVICE_GET_FORECAST,
|
||||
{
|
||||
"entity_id": "weather.home",
|
||||
"type": "daily",
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert response["forecast"] != []
|
||||
assert response == snapshot
|
||||
|
||||
|
||||
async def test_forecast_subscription(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test multiple forecast."""
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
await init_integration(hass, forecast=True)
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "weather/subscribe_forecast",
|
||||
"forecast_type": "daily",
|
||||
"entity_id": "weather.home",
|
||||
}
|
||||
)
|
||||
msg = await client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"] is None
|
||||
subscription_id = msg["id"]
|
||||
|
||||
msg = await client.receive_json()
|
||||
assert msg["id"] == subscription_id
|
||||
assert msg["type"] == "event"
|
||||
forecast1 = msg["event"]["forecast"]
|
||||
|
||||
assert forecast1 != []
|
||||
assert forecast1 == snapshot
|
||||
|
||||
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,
|
||||
), 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,
|
||||
):
|
||||
freezer.tick(timedelta(minutes=80) + timedelta(seconds=1))
|
||||
await hass.async_block_till_done()
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg["id"] == subscription_id
|
||||
assert msg["type"] == "event"
|
||||
forecast2 = msg["event"]["forecast"]
|
||||
|
||||
assert forecast2 != []
|
||||
assert forecast2 == snapshot
|
||||
|
Loading…
x
Reference in New Issue
Block a user