Fix for Environment Canada date being wrong after midnight (#121850)
* Use async_connect in newly bumped 0.5.8 UPB library. * Fix tests. * Fix date being wrong after midnight for Environment Canada * Fix typing. * Add test. * Formatting. * Remove tests until can be added properly. * Add weather tests back. * Fix tests * Change of tactic for determining previous day's data. --------- Co-authored-by: G Johansson <goran.johansson@shiftit.se>
This commit is contained in:
parent
bc7d2d2195
commit
6645932fb7
9 changed files with 281 additions and 123 deletions
|
@ -6,5 +6,5 @@
|
|||
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["env_canada"],
|
||||
"requirements": ["env-canada==0.7.1"]
|
||||
"requirements": ["env-canada==0.7.2"]
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
|
@ -37,7 +35,6 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import device_info
|
||||
from .const import DOMAIN
|
||||
|
@ -193,53 +190,24 @@ def get_forecast(ec_data, hourly) -> list[Forecast] | None:
|
|||
if not (half_days := ec_data.daily_forecasts):
|
||||
return None
|
||||
|
||||
today: Forecast = {
|
||||
ATTR_FORECAST_TIME: dt_util.now().isoformat(),
|
||||
ATTR_FORECAST_CONDITION: icon_code_to_condition(
|
||||
int(half_days[0]["icon_code"])
|
||||
),
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: int(
|
||||
half_days[0]["precip_probability"]
|
||||
),
|
||||
}
|
||||
def get_day_forecast(fcst: list[dict[str, str]]) -> Forecast:
|
||||
high_temp = int(fcst[0]["temperature"]) if len(fcst) == 2 else None
|
||||
return {
|
||||
ATTR_FORECAST_TIME: fcst[0]["timestamp"],
|
||||
ATTR_FORECAST_NATIVE_TEMP: high_temp,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW: int(fcst[-1]["temperature"]),
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: int(
|
||||
fcst[0]["precip_probability"]
|
||||
),
|
||||
ATTR_FORECAST_CONDITION: icon_code_to_condition(
|
||||
int(fcst[0]["icon_code"])
|
||||
),
|
||||
}
|
||||
|
||||
if half_days[0]["temperature_class"] == "high":
|
||||
today.update(
|
||||
{
|
||||
ATTR_FORECAST_NATIVE_TEMP: int(half_days[0]["temperature"]),
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW: int(half_days[1]["temperature"]),
|
||||
}
|
||||
)
|
||||
half_days = half_days[2:]
|
||||
else:
|
||||
today.update(
|
||||
{
|
||||
ATTR_FORECAST_NATIVE_TEMP: None,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW: int(half_days[0]["temperature"]),
|
||||
}
|
||||
)
|
||||
half_days = half_days[1:]
|
||||
|
||||
forecast_array.append(today)
|
||||
|
||||
for day, high, low in zip(
|
||||
range(1, 6), range(0, 9, 2), range(1, 10, 2), strict=False
|
||||
):
|
||||
forecast_array.append(
|
||||
{
|
||||
ATTR_FORECAST_TIME: (
|
||||
dt_util.now() + datetime.timedelta(days=day)
|
||||
).isoformat(),
|
||||
ATTR_FORECAST_NATIVE_TEMP: int(half_days[high]["temperature"]),
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW: int(half_days[low]["temperature"]),
|
||||
ATTR_FORECAST_CONDITION: icon_code_to_condition(
|
||||
int(half_days[high]["icon_code"])
|
||||
),
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: int(
|
||||
half_days[high]["precip_probability"]
|
||||
),
|
||||
}
|
||||
)
|
||||
i = 2 if half_days[0]["temperature_class"] == "high" else 1
|
||||
forecast_array.append(get_day_forecast(half_days[0:i]))
|
||||
for i in range(i, len(half_days) - 1, 2):
|
||||
forecast_array.append(get_day_forecast(half_days[i : i + 2])) # noqa: PERF401
|
||||
|
||||
else:
|
||||
forecast_array.extend(
|
||||
|
|
|
@ -813,7 +813,7 @@ enocean==0.50
|
|||
enturclient==0.2.4
|
||||
|
||||
# homeassistant.components.environment_canada
|
||||
env-canada==0.7.1
|
||||
env-canada==0.7.2
|
||||
|
||||
# homeassistant.components.season
|
||||
ephem==4.1.5
|
||||
|
|
|
@ -682,7 +682,7 @@ energyzero==2.1.1
|
|||
enocean==0.50
|
||||
|
||||
# homeassistant.components.environment_canada
|
||||
env-canada==0.7.1
|
||||
env-canada==0.7.2
|
||||
|
||||
# homeassistant.components.season
|
||||
ephem==4.1.5
|
||||
|
|
|
@ -1 +1,67 @@
|
|||
"""Tests for the Environment Canada integration."""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from homeassistant.components.environment_canada.const import CONF_STATION, DOMAIN
|
||||
from homeassistant.const import CONF_LANGUAGE, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
FIXTURE_USER_INPUT = {
|
||||
CONF_LATITUDE: 55.55,
|
||||
CONF_LONGITUDE: 42.42,
|
||||
CONF_STATION: "XX/1234567",
|
||||
CONF_LANGUAGE: "Gibberish",
|
||||
}
|
||||
|
||||
|
||||
async def init_integration(hass: HomeAssistant, ec_data) -> MockConfigEntry:
|
||||
"""Set up the Environment Canada integration in Home Assistant."""
|
||||
|
||||
def mock_ec():
|
||||
ec_mock = MagicMock()
|
||||
ec_mock.station_id = FIXTURE_USER_INPUT[CONF_STATION]
|
||||
ec_mock.lat = FIXTURE_USER_INPUT[CONF_LATITUDE]
|
||||
ec_mock.lon = FIXTURE_USER_INPUT[CONF_LONGITUDE]
|
||||
ec_mock.language = FIXTURE_USER_INPUT[CONF_LANGUAGE]
|
||||
ec_mock.update = AsyncMock()
|
||||
return ec_mock
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=FIXTURE_USER_INPUT, title="Home")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
weather_mock = mock_ec()
|
||||
ec_data["metadata"]["timestamp"] = datetime(2022, 10, 4, tzinfo=UTC)
|
||||
weather_mock.conditions = ec_data["conditions"]
|
||||
weather_mock.alerts = ec_data["alerts"]
|
||||
weather_mock.daily_forecasts = ec_data["daily_forecasts"]
|
||||
weather_mock.metadata = ec_data["metadata"]
|
||||
|
||||
radar_mock = mock_ec()
|
||||
radar_mock.image = b"GIF..."
|
||||
radar_mock.timestamp = datetime(2022, 10, 4, tzinfo=UTC)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.environment_canada.ECWeather",
|
||||
return_value=weather_mock,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.environment_canada.ECAirQuality",
|
||||
return_value=mock_ec(),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.environment_canada.ECRadar",
|
||||
return_value=radar_mock,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.environment_canada.config_flow.ECWeather",
|
||||
return_value=weather_mock,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return config_entry
|
||||
|
|
|
@ -135,7 +135,8 @@
|
|||
"icon_code": "30",
|
||||
"temperature": -1,
|
||||
"temperature_class": "low",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-03 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Tuesday",
|
||||
|
@ -143,7 +144,8 @@
|
|||
"icon_code": "00",
|
||||
"temperature": 18,
|
||||
"temperature_class": "high",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-04 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Tuesday night",
|
||||
|
@ -151,7 +153,8 @@
|
|||
"icon_code": "30",
|
||||
"temperature": 3,
|
||||
"temperature_class": "low",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-04 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Wednesday",
|
||||
|
@ -159,7 +162,8 @@
|
|||
"icon_code": "00",
|
||||
"temperature": 20,
|
||||
"temperature_class": "high",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-05 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Wednesday night",
|
||||
|
@ -167,7 +171,8 @@
|
|||
"icon_code": "30",
|
||||
"temperature": 9,
|
||||
"temperature_class": "low",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-05 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Thursday",
|
||||
|
@ -175,7 +180,8 @@
|
|||
"icon_code": "02",
|
||||
"temperature": 20,
|
||||
"temperature_class": "high",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-06 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Thursday night",
|
||||
|
@ -183,7 +189,8 @@
|
|||
"icon_code": "12",
|
||||
"temperature": 7,
|
||||
"temperature_class": "low",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-06 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Friday",
|
||||
|
@ -191,7 +198,8 @@
|
|||
"icon_code": "12",
|
||||
"temperature": 13,
|
||||
"temperature_class": "high",
|
||||
"precip_probability": 40
|
||||
"precip_probability": 40,
|
||||
"timestamp": "2022-10-07 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Friday night",
|
||||
|
@ -199,7 +207,8 @@
|
|||
"icon_code": "32",
|
||||
"temperature": 1,
|
||||
"temperature_class": "low",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-07 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Saturday",
|
||||
|
@ -207,7 +216,8 @@
|
|||
"icon_code": "02",
|
||||
"temperature": 10,
|
||||
"temperature_class": "high",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-08 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Saturday night",
|
||||
|
@ -215,7 +225,8 @@
|
|||
"icon_code": "32",
|
||||
"temperature": 3,
|
||||
"temperature_class": "low",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-08 15:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"period": "Sunday",
|
||||
|
@ -223,7 +234,8 @@
|
|||
"icon_code": "02",
|
||||
"temperature": 12,
|
||||
"temperature_class": "high",
|
||||
"precip_probability": 0
|
||||
"precip_probability": 0,
|
||||
"timestamp": "2022-10-09 15:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
# serializer version: 1
|
||||
# name: test_forecast_daily
|
||||
dict({
|
||||
'weather.home_forecast': dict({
|
||||
'forecast': list([
|
||||
dict({
|
||||
'condition': 'sunny',
|
||||
'datetime': '2022-10-04 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': 18.0,
|
||||
'templow': 3.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'sunny',
|
||||
'datetime': '2022-10-05 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': 20.0,
|
||||
'templow': 9.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2022-10-06 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': 20.0,
|
||||
'templow': 7.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'rainy',
|
||||
'datetime': '2022-10-07 15:00:00+00:00',
|
||||
'precipitation_probability': 40,
|
||||
'temperature': 13.0,
|
||||
'templow': 1.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2022-10-08 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': 10.0,
|
||||
'templow': 3.0,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_daily_with_some_previous_days_data
|
||||
dict({
|
||||
'weather.home_forecast': dict({
|
||||
'forecast': list([
|
||||
dict({
|
||||
'condition': 'clear-night',
|
||||
'datetime': '2022-10-03 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': None,
|
||||
'templow': -1.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'sunny',
|
||||
'datetime': '2022-10-04 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': 18.0,
|
||||
'templow': 3.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'sunny',
|
||||
'datetime': '2022-10-05 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': 20.0,
|
||||
'templow': 9.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2022-10-06 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': 20.0,
|
||||
'templow': 7.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'rainy',
|
||||
'datetime': '2022-10-07 15:00:00+00:00',
|
||||
'precipitation_probability': 40,
|
||||
'temperature': 13.0,
|
||||
'templow': 1.0,
|
||||
}),
|
||||
dict({
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2022-10-08 15:00:00+00:00',
|
||||
'precipitation_probability': 0,
|
||||
'temperature': 10.0,
|
||||
'templow': 3.0,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
# ---
|
|
@ -1,16 +1,16 @@
|
|||
"""Test Environment Canada diagnostics."""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.environment_canada.const import CONF_STATION, DOMAIN
|
||||
from homeassistant.components.environment_canada.const import CONF_STATION
|
||||
from homeassistant.const import CONF_LANGUAGE, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import load_fixture
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
@ -22,60 +22,6 @@ FIXTURE_USER_INPUT = {
|
|||
}
|
||||
|
||||
|
||||
async def init_integration(hass: HomeAssistant) -> MockConfigEntry:
|
||||
"""Set up the Environment Canada integration in Home Assistant."""
|
||||
|
||||
def mock_ec():
|
||||
ec_mock = MagicMock()
|
||||
ec_mock.station_id = FIXTURE_USER_INPUT[CONF_STATION]
|
||||
ec_mock.lat = FIXTURE_USER_INPUT[CONF_LATITUDE]
|
||||
ec_mock.lon = FIXTURE_USER_INPUT[CONF_LONGITUDE]
|
||||
ec_mock.language = FIXTURE_USER_INPUT[CONF_LANGUAGE]
|
||||
ec_mock.update = AsyncMock()
|
||||
return ec_mock
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=FIXTURE_USER_INPUT)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
ec_data = json.loads(
|
||||
load_fixture("environment_canada/current_conditions_data.json")
|
||||
)
|
||||
|
||||
weather_mock = mock_ec()
|
||||
ec_data["metadata"]["timestamp"] = datetime(2022, 10, 4, tzinfo=UTC)
|
||||
weather_mock.conditions = ec_data["conditions"]
|
||||
weather_mock.alerts = ec_data["alerts"]
|
||||
weather_mock.daily_forecasts = ec_data["daily_forecasts"]
|
||||
weather_mock.metadata = ec_data["metadata"]
|
||||
|
||||
radar_mock = mock_ec()
|
||||
radar_mock.image = b"GIF..."
|
||||
radar_mock.timestamp = datetime(2022, 10, 4, tzinfo=UTC)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.environment_canada.ECWeather",
|
||||
return_value=weather_mock,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.environment_canada.ECAirQuality",
|
||||
return_value=mock_ec(),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.environment_canada.ECRadar",
|
||||
return_value=radar_mock,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.environment_canada.config_flow.ECWeather",
|
||||
return_value=weather_mock,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return config_entry
|
||||
|
||||
|
||||
async def test_entry_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
|
@ -83,7 +29,11 @@ async def test_entry_diagnostics(
|
|||
) -> None:
|
||||
"""Test config entry diagnostics."""
|
||||
|
||||
config_entry = await init_integration(hass)
|
||||
ec_data = json.loads(
|
||||
load_fixture("environment_canada/current_conditions_data.json")
|
||||
)
|
||||
|
||||
config_entry = await init_integration(hass, ec_data)
|
||||
diagnostics = await get_diagnostics_for_config_entry(
|
||||
hass, hass_client, config_entry
|
||||
)
|
||||
|
|
68
tests/components/environment_canada/test_weather.py
Normal file
68
tests/components/environment_canada/test_weather.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
"""Test weather."""
|
||||
|
||||
import json
|
||||
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
DOMAIN as WEATHER_DOMAIN,
|
||||
SERVICE_GET_FORECASTS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import load_fixture
|
||||
|
||||
|
||||
async def test_forecast_daily(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test basic forecast."""
|
||||
|
||||
ec_data = json.loads(
|
||||
load_fixture("environment_canada/current_conditions_data.json")
|
||||
)
|
||||
|
||||
# First entry in test data is a half day; we don't want that for this test
|
||||
del ec_data["daily_forecasts"][0]
|
||||
|
||||
await init_integration(hass, ec_data)
|
||||
|
||||
response = await hass.services.async_call(
|
||||
WEATHER_DOMAIN,
|
||||
SERVICE_GET_FORECASTS,
|
||||
{
|
||||
"entity_id": "weather.home_forecast",
|
||||
"type": "daily",
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert response == snapshot
|
||||
|
||||
|
||||
async def test_forecast_daily_with_some_previous_days_data(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test forecast with half day at start."""
|
||||
|
||||
ec_data = json.loads(
|
||||
load_fixture("environment_canada/current_conditions_data.json")
|
||||
)
|
||||
|
||||
await init_integration(hass, ec_data)
|
||||
|
||||
response = await hass.services.async_call(
|
||||
WEATHER_DOMAIN,
|
||||
SERVICE_GET_FORECASTS,
|
||||
{
|
||||
"entity_id": "weather.home_forecast",
|
||||
"type": "daily",
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert response == snapshot
|
Loading…
Add table
Reference in a new issue