diff --git a/tests/components/openweathermap/__init__.py b/tests/components/openweathermap/__init__.py index e718962766f..9552cdb4f70 100644 --- a/tests/components/openweathermap/__init__.py +++ b/tests/components/openweathermap/__init__.py @@ -1 +1,24 @@ -"""Tests for the OpenWeatherMap integration.""" +"""Shared utilities for OpenWeatherMap tests.""" + +from unittest.mock import patch + +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +async def setup_platform( + hass: HomeAssistant, + config_entry: MockConfigEntry, + platforms: list[Platform], +): + """Set up the OpenWeatherMap platform.""" + config_entry.add_to_hass(hass) + with ( + patch("homeassistant.components.openweathermap.PLATFORMS", platforms), + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state is ConfigEntryState.LOADED diff --git a/tests/components/openweathermap/conftest.py b/tests/components/openweathermap/conftest.py new file mode 100644 index 00000000000..44f4b971bd8 --- /dev/null +++ b/tests/components/openweathermap/conftest.py @@ -0,0 +1,146 @@ +"""Configure tests for the OpenWeatherMap integration.""" + +from collections.abc import Generator +from datetime import UTC, datetime +from unittest.mock import AsyncMock + +from pyopenweathermap import ( + CurrentWeather, + DailyTemperature, + DailyWeatherForecast, + MinutelyWeatherForecast, + WeatherCondition, + WeatherReport, +) +from pyopenweathermap.client.owm_abstract_client import OWMClient +import pytest + +from homeassistant.components.openweathermap.const import DEFAULT_LANGUAGE, DOMAIN +from homeassistant.const import ( + CONF_API_KEY, + CONF_LANGUAGE, + CONF_LATITUDE, + CONF_LONGITUDE, + CONF_MODE, + CONF_NAME, +) + +from tests.common import MockConfigEntry, patch + +API_KEY = "test_api_key" +LATITUDE = 12.34 +LONGITUDE = 56.78 +NAME = "openweathermap" + + +@pytest.fixture +def mode(request: pytest.FixtureRequest) -> str: + """Return mode passed in parameter.""" + return request.param + + +@pytest.fixture +def mock_config_entry(mode: str) -> MockConfigEntry: + """Fixture for creating a mock OpenWeatherMap config entry.""" + return MockConfigEntry( + domain=DOMAIN, + data={ + CONF_API_KEY: API_KEY, + CONF_LATITUDE: LATITUDE, + CONF_LONGITUDE: LONGITUDE, + CONF_NAME: NAME, + }, + options={ + CONF_MODE: mode, + CONF_LANGUAGE: DEFAULT_LANGUAGE, + }, + entry_id="test", + version=5, + unique_id=f"{LATITUDE}-{LONGITUDE}", + ) + + +@pytest.fixture +def owm_client_mock() -> Generator[AsyncMock]: + """Mock OWMClient.""" + client = AsyncMock(spec=OWMClient, autospec=True) + current_weather = CurrentWeather( + date_time=datetime.fromtimestamp(1714063536, tz=UTC), + temperature=6.84, + feels_like=2.07, + pressure=1000, + humidity=82, + dew_point=3.99, + uv_index=0.13, + cloud_coverage=75, + visibility=10000, + wind_speed=9.83, + wind_bearing=199, + wind_gust=None, + rain={"1h": 1.21}, + snow=None, + condition=WeatherCondition( + id=803, + main="Clouds", + description="broken clouds", + icon="04d", + ), + ) + daily_weather_forecast = DailyWeatherForecast( + date_time=datetime.fromtimestamp(1714063536, tz=UTC), + summary="There will be clear sky until morning, then partly cloudy", + temperature=DailyTemperature( + day=18.76, + min=8.11, + max=21.26, + night=13.06, + evening=20.51, + morning=8.47, + ), + feels_like=DailyTemperature( + day=18.76, + min=8.11, + max=21.26, + night=13.06, + evening=20.51, + morning=8.47, + ), + pressure=1015, + humidity=62, + dew_point=11.34, + wind_speed=8.14, + wind_bearing=168, + wind_gust=11.81, + condition=WeatherCondition( + id=803, + main="Clouds", + description="broken clouds", + icon="04d", + ), + cloud_coverage=84, + precipitation_probability=0, + uv_index=4.06, + rain=0, + snow=0, + ) + minutely_weather_forecast = [ + MinutelyWeatherForecast(date_time=1728672360, precipitation=0), + MinutelyWeatherForecast(date_time=1728672420, precipitation=1.23), + MinutelyWeatherForecast(date_time=1728672480, precipitation=4.5), + MinutelyWeatherForecast(date_time=1728672540, precipitation=0), + ] + client.get_weather.return_value = WeatherReport( + current_weather, minutely_weather_forecast, [], [daily_weather_forecast] + ) + client.validate_key.return_value = True + with ( + patch( + "homeassistant.components.openweathermap.create_owm_client", + return_value=client, + ), + patch( + "homeassistant.components.openweathermap.utils.create_owm_client", + return_value=client, + ), + ): + yield client diff --git a/tests/components/openweathermap/snapshots/test_sensor.ambr b/tests/components/openweathermap/snapshots/test_sensor.ambr new file mode 100644 index 00000000000..1f416f76578 --- /dev/null +++ b/tests/components/openweathermap/snapshots/test_sensor.ambr @@ -0,0 +1,1659 @@ +# serializer version: 1 +# name: test_sensor_states[current][sensor.openweathermap_cloud_coverage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_cloud_coverage', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Cloud coverage', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-clouds', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_cloud_coverage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Cloud coverage', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_cloud_coverage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_condition-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_condition', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Condition', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-condition', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_condition-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Condition', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_condition', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'cloudy', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_dew_point-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_dew_point', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Dew Point', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-dew_point', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_dew_point-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'temperature', + 'friendly_name': 'openweathermap Dew Point', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_dew_point', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '3.99', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_feels_like_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_feels_like_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Feels like temperature', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-feels_like_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_feels_like_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'temperature', + 'friendly_name': 'openweathermap Feels like temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_feels_like_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2.07', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_humidity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_humidity', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Humidity', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-humidity', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_humidity-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'humidity', + 'friendly_name': 'openweathermap Humidity', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_humidity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '82', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_precipitation_kind-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_precipitation_kind', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Precipitation kind', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-precipitation_kind', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_precipitation_kind-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Precipitation kind', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_precipitation_kind', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'Rain', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_pressure-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_pressure', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Pressure', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-pressure', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_pressure-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'pressure', + 'friendly_name': 'openweathermap Pressure', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_pressure', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1000', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_rain-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_rain', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Rain', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-rain', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_rain-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'precipitation_intensity', + 'friendly_name': 'openweathermap Rain', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_rain', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1.21', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_snow-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_snow', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Snow', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-snow', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_snow-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'precipitation_intensity', + 'friendly_name': 'openweathermap Snow', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_snow', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Temperature', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'temperature', + 'friendly_name': 'openweathermap Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '6.84', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_uv_index-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_uv_index', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap UV Index', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-uv_index', + 'unit_of_measurement': 'UV index', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_uv_index-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap UV Index', + 'state_class': , + 'unit_of_measurement': 'UV index', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_uv_index', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.13', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_visibility-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_visibility', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Visibility', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-visibility_distance', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_visibility-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'distance', + 'friendly_name': 'openweathermap Visibility', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_visibility', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10000', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_weather-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_weather', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Weather', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-weather', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_weather-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Weather', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_weather', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'broken clouds', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_weather_code-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_weather_code', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Weather Code', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-weather_code', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_weather_code-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Weather Code', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_weather_code', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '803', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_wind_bearing-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_wind_bearing', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Wind bearing', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-wind_bearing', + 'unit_of_measurement': '°', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_wind_bearing-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'wind_direction', + 'friendly_name': 'openweathermap Wind bearing', + 'state_class': , + 'unit_of_measurement': '°', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_wind_bearing', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '199', + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_wind_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_wind_speed', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Wind speed', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-wind_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[current][sensor.openweathermap_wind_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'wind_speed', + 'friendly_name': 'openweathermap Wind speed', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_wind_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '35.39', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_cloud_coverage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_cloud_coverage', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Cloud coverage', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-clouds', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_cloud_coverage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Cloud coverage', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_cloud_coverage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_condition-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_condition', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Condition', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-condition', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_condition-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Condition', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_condition', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'cloudy', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_dew_point-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_dew_point', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Dew Point', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-dew_point', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_dew_point-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'temperature', + 'friendly_name': 'openweathermap Dew Point', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_dew_point', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '3.99', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_feels_like_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_feels_like_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Feels like temperature', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-feels_like_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_feels_like_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'temperature', + 'friendly_name': 'openweathermap Feels like temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_feels_like_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2.07', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_humidity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_humidity', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Humidity', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-humidity', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_humidity-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'humidity', + 'friendly_name': 'openweathermap Humidity', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_humidity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '82', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_precipitation_kind-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_precipitation_kind', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Precipitation kind', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-precipitation_kind', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_precipitation_kind-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Precipitation kind', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_precipitation_kind', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'Rain', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_pressure-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_pressure', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Pressure', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-pressure', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_pressure-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'pressure', + 'friendly_name': 'openweathermap Pressure', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_pressure', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1000', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_rain-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_rain', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Rain', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-rain', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_rain-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'precipitation_intensity', + 'friendly_name': 'openweathermap Rain', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_rain', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1.21', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_snow-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_snow', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Snow', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-snow', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_snow-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'precipitation_intensity', + 'friendly_name': 'openweathermap Snow', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_snow', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Temperature', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'temperature', + 'friendly_name': 'openweathermap Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '6.84', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_uv_index-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_uv_index', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap UV Index', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-uv_index', + 'unit_of_measurement': 'UV index', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_uv_index-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap UV Index', + 'state_class': , + 'unit_of_measurement': 'UV index', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_uv_index', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.13', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_visibility-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_visibility', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Visibility', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-visibility_distance', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_visibility-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'distance', + 'friendly_name': 'openweathermap Visibility', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_visibility', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10000', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_weather-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_weather', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Weather', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-weather', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_weather-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Weather', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_weather', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'broken clouds', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_weather_code-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_weather_code', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap Weather Code', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-weather_code', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_weather_code-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'friendly_name': 'openweathermap Weather Code', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_weather_code', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '803', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_wind_bearing-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_wind_bearing', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Wind bearing', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-wind_bearing', + 'unit_of_measurement': '°', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_wind_bearing-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'wind_direction', + 'friendly_name': 'openweathermap Wind bearing', + 'state_class': , + 'unit_of_measurement': '°', + }), + 'context': , + 'entity_id': 'sensor.openweathermap_wind_bearing', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '199', + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_wind_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.openweathermap_wind_speed', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'openweathermap Wind speed', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78-wind_speed', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states[v3.0][sensor.openweathermap_wind_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Data provided by OpenWeatherMap', + 'device_class': 'wind_speed', + 'friendly_name': 'openweathermap Wind speed', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.openweathermap_wind_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '35.39', + }) +# --- diff --git a/tests/components/openweathermap/snapshots/test_weather.ambr b/tests/components/openweathermap/snapshots/test_weather.ambr index c89dcb96a9c..90f583d9db1 100644 --- a/tests/components/openweathermap/snapshots/test_weather.ambr +++ b/tests/components/openweathermap/snapshots/test_weather.ambr @@ -1,5 +1,5 @@ # serializer version: 1 -# name: test_get_minute_forecast[mock_service_response] +# name: test_get_minute_forecast[v3.0][mock_service_response] dict({ 'weather.openweathermap': dict({ 'forecast': list([ @@ -23,3 +23,188 @@ }), }) # --- +# name: test_weather_states[current][weather.openweathermap-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'weather', + 'entity_category': None, + 'entity_id': 'weather.openweathermap', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12.34-56.78', + 'unit_of_measurement': None, + }) +# --- +# name: test_weather_states[current][weather.openweathermap-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'apparent_temperature': 2.1, + 'attribution': 'Data provided by OpenWeatherMap', + 'cloud_coverage': 75, + 'dew_point': 4.0, + 'friendly_name': 'openweathermap', + 'humidity': 82, + 'precipitation_unit': , + 'pressure': 1000.0, + 'pressure_unit': , + 'temperature': 6.8, + 'temperature_unit': , + 'visibility_unit': , + 'wind_bearing': 199, + 'wind_speed': 35.39, + 'wind_speed_unit': , + }), + 'context': , + 'entity_id': 'weather.openweathermap', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'cloudy', + }) +# --- +# name: test_weather_states[forecast][weather.openweathermap-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'weather', + 'entity_category': None, + 'entity_id': 'weather.openweathermap', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '12.34-56.78', + 'unit_of_measurement': None, + }) +# --- +# name: test_weather_states[forecast][weather.openweathermap-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'apparent_temperature': 2.1, + 'attribution': 'Data provided by OpenWeatherMap', + 'cloud_coverage': 75, + 'dew_point': 4.0, + 'friendly_name': 'openweathermap', + 'humidity': 82, + 'precipitation_unit': , + 'pressure': 1000.0, + 'pressure_unit': , + 'supported_features': , + 'temperature': 6.8, + 'temperature_unit': , + 'visibility_unit': , + 'wind_bearing': 199, + 'wind_speed': 35.39, + 'wind_speed_unit': , + }), + 'context': , + 'entity_id': 'weather.openweathermap', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'cloudy', + }) +# --- +# name: test_weather_states[v3.0][weather.openweathermap-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'weather', + 'entity_category': None, + 'entity_id': 'weather.openweathermap', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'openweathermap', + 'platform': 'openweathermap', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '12.34-56.78', + 'unit_of_measurement': None, + }) +# --- +# name: test_weather_states[v3.0][weather.openweathermap-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'apparent_temperature': 2.1, + 'attribution': 'Data provided by OpenWeatherMap', + 'cloud_coverage': 75, + 'dew_point': 4.0, + 'friendly_name': 'openweathermap', + 'humidity': 82, + 'precipitation_unit': , + 'pressure': 1000.0, + 'pressure_unit': , + 'supported_features': , + 'temperature': 6.8, + 'temperature_unit': , + 'visibility_unit': , + 'wind_bearing': 199, + 'wind_speed': 35.39, + 'wind_speed_unit': , + }), + 'context': , + 'entity_id': 'weather.openweathermap', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'cloudy', + }) +# --- diff --git a/tests/components/openweathermap/test_config_flow.py b/tests/components/openweathermap/test_config_flow.py index d5e01677dd8..0315ca91010 100644 --- a/tests/components/openweathermap/test_config_flow.py +++ b/tests/components/openweathermap/test_config_flow.py @@ -1,17 +1,8 @@ """Define tests for the OpenWeatherMap config flow.""" -from datetime import UTC, datetime -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock -from pyopenweathermap import ( - CurrentWeather, - DailyTemperature, - DailyWeatherForecast, - MinutelyWeatherForecast, - RequestError, - WeatherCondition, - WeatherReport, -) +from pyopenweathermap import RequestError import pytest from homeassistant.components.openweathermap.const import ( @@ -32,13 +23,15 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from .conftest import LATITUDE, LONGITUDE + from tests.common import MockConfigEntry CONFIG = { CONF_NAME: "openweathermap", CONF_API_KEY: "foo", - CONF_LATITUDE: 50, - CONF_LONGITUDE: 40, + CONF_LATITUDE: LATITUDE, + CONF_LONGITUDE: LONGITUDE, CONF_LANGUAGE: DEFAULT_LANGUAGE, CONF_MODE: OWM_MODE_V30, } @@ -46,118 +39,11 @@ CONFIG = { VALID_YAML_CONFIG = {CONF_API_KEY: "foo"} -def _create_static_weather_report() -> WeatherReport: - """Create a static WeatherReport.""" - - current_weather = CurrentWeather( - date_time=datetime.fromtimestamp(1714063536, tz=UTC), - temperature=6.84, - feels_like=2.07, - pressure=1000, - humidity=82, - dew_point=3.99, - uv_index=0.13, - cloud_coverage=75, - visibility=10000, - wind_speed=9.83, - wind_bearing=199, - wind_gust=None, - rain={"1h": 1.21}, - snow=None, - condition=WeatherCondition( - id=803, - main="Clouds", - description="broken clouds", - icon="04d", - ), - ) - daily_weather_forecast = DailyWeatherForecast( - date_time=datetime.fromtimestamp(1714063536, tz=UTC), - summary="There will be clear sky until morning, then partly cloudy", - temperature=DailyTemperature( - day=18.76, - min=8.11, - max=21.26, - night=13.06, - evening=20.51, - morning=8.47, - ), - feels_like=DailyTemperature( - day=18.76, - min=8.11, - max=21.26, - night=13.06, - evening=20.51, - morning=8.47, - ), - pressure=1015, - humidity=62, - dew_point=11.34, - wind_speed=8.14, - wind_bearing=168, - wind_gust=11.81, - condition=WeatherCondition( - id=803, - main="Clouds", - description="broken clouds", - icon="04d", - ), - cloud_coverage=84, - precipitation_probability=0, - uv_index=4.06, - rain=0, - snow=0, - ) - minutely_weather_forecast = [ - MinutelyWeatherForecast(date_time=1728672360, precipitation=0), - MinutelyWeatherForecast(date_time=1728672420, precipitation=1.23), - MinutelyWeatherForecast(date_time=1728672480, precipitation=4.5), - MinutelyWeatherForecast(date_time=1728672540, precipitation=0), - ] - return WeatherReport( - current_weather, minutely_weather_forecast, [], [daily_weather_forecast] - ) - - -def _create_mocked_owm_factory(is_valid: bool): - """Create a mocked OWM client.""" - - weather_report = _create_static_weather_report() - mocked_owm_client = MagicMock() - mocked_owm_client.validate_key = AsyncMock(return_value=is_valid) - mocked_owm_client.get_weather = AsyncMock(return_value=weather_report) - - return mocked_owm_client - - -@pytest.fixture(name="owm_client_mock") -def mock_owm_client(): - """Mock config_flow OWMClient.""" - with patch( - "homeassistant.components.openweathermap.create_owm_client", - ) as mock: - yield mock - - -@pytest.fixture(name="config_flow_owm_client_mock") -def mock_config_flow_owm_client(): - """Mock config_flow OWMClient.""" - with patch( - "homeassistant.components.openweathermap.utils.create_owm_client", - ) as mock: - yield mock - - async def test_successful_config_flow( hass: HomeAssistant, - owm_client_mock, - config_flow_owm_client_mock, + owm_client_mock: AsyncMock, ) -> None: """Test that the form is served with valid input.""" - mock = _create_mocked_owm_factory(True) - owm_client_mock.return_value = mock - config_flow_owm_client_mock.return_value = mock - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) @@ -187,39 +73,32 @@ async def test_successful_config_flow( assert result["data"][CONF_API_KEY] == CONFIG[CONF_API_KEY] +@pytest.mark.parametrize("mode", [OWM_MODE_V30], indirect=True) async def test_abort_config_flow( hass: HomeAssistant, - owm_client_mock, - config_flow_owm_client_mock, + owm_client_mock: AsyncMock, + mock_config_entry: MockConfigEntry, ) -> None: """Test that the form is served with same data.""" - mock = _create_mocked_owm_factory(True) - owm_client_mock.return_value = mock - config_flow_owm_client_mock.return_value = mock - + mock_config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONFIG + DOMAIN, context={"source": SOURCE_USER} ) - await hass.async_block_till_done() - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONFIG - ) - await hass.async_block_till_done() + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + result = await hass.config_entries.flow.async_configure(result["flow_id"], CONFIG) assert result["type"] is FlowResultType.ABORT async def test_config_flow_options_change( hass: HomeAssistant, - owm_client_mock, - config_flow_owm_client_mock, + owm_client_mock: AsyncMock, ) -> None: """Test that the options form.""" - mock = _create_mocked_owm_factory(True) - owm_client_mock.return_value = mock - config_flow_owm_client_mock.return_value = mock - config_entry = MockConfigEntry( domain=DOMAIN, unique_id="openweathermap_unique_id", data=CONFIG ) @@ -274,10 +153,10 @@ async def test_config_flow_options_change( async def test_form_invalid_api_key( hass: HomeAssistant, - config_flow_owm_client_mock, + owm_client_mock: AsyncMock, ) -> None: """Test that the form is served with no input.""" - config_flow_owm_client_mock.return_value = _create_mocked_owm_factory(False) + owm_client_mock.validate_key.return_value = False result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=CONFIG ) @@ -285,7 +164,7 @@ async def test_form_invalid_api_key( assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": "invalid_api_key"} - config_flow_owm_client_mock.return_value = _create_mocked_owm_factory(True) + owm_client_mock.validate_key.return_value = True result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=CONFIG ) @@ -295,11 +174,10 @@ async def test_form_invalid_api_key( async def test_form_api_call_error( hass: HomeAssistant, - config_flow_owm_client_mock, + owm_client_mock: AsyncMock, ) -> None: """Test setting up with api call error.""" - config_flow_owm_client_mock.return_value = _create_mocked_owm_factory(True) - config_flow_owm_client_mock.side_effect = RequestError("oops") + owm_client_mock.validate_key.side_effect = RequestError("oops") result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=CONFIG ) @@ -307,7 +185,7 @@ async def test_form_api_call_error( assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": "cannot_connect"} - config_flow_owm_client_mock.side_effect = None + owm_client_mock.validate_key.side_effect = None result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=CONFIG ) diff --git a/tests/components/openweathermap/test_sensor.py b/tests/components/openweathermap/test_sensor.py new file mode 100644 index 00000000000..8cb8bd11c26 --- /dev/null +++ b/tests/components/openweathermap/test_sensor.py @@ -0,0 +1,49 @@ +"""Tests for OpenWeatherMap sensors.""" + +from unittest.mock import MagicMock + +import pytest +from syrupy import SnapshotAssertion + +from homeassistant.components.openweathermap.const import ( + OWM_MODE_FREE_CURRENT, + OWM_MODE_FREE_FORECAST, + OWM_MODE_V30, +) +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import setup_platform + +from tests.common import MockConfigEntry, snapshot_platform + + +@pytest.mark.parametrize("mode", [OWM_MODE_V30, OWM_MODE_FREE_CURRENT], indirect=True) +async def test_sensor_states( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, + owm_client_mock: MagicMock, + mode: str, +) -> None: + """Test sensor states are correctly collected from library with different modes and mocked function responses.""" + + await setup_platform(hass, mock_config_entry, [Platform.SENSOR]) + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +@pytest.mark.parametrize("mode", [OWM_MODE_FREE_FORECAST], indirect=True) +async def test_mode_no_sensor( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, + owm_client_mock: MagicMock, + mode: str, +) -> None: + """Test modes that do not provide any sensor.""" + + await setup_platform(hass, mock_config_entry, [Platform.SENSOR]) + assert len(entity_registry.entities) == 0 diff --git a/tests/components/openweathermap/test_weather.py b/tests/components/openweathermap/test_weather.py index e9817e739ac..9ac51afd6b3 100644 --- a/tests/components/openweathermap/test_weather.py +++ b/tests/components/openweathermap/test_weather.py @@ -1,91 +1,40 @@ """Test the OpenWeatherMap weather entity.""" +from unittest.mock import MagicMock + import pytest from syrupy import SnapshotAssertion from homeassistant.components.openweathermap.const import ( - DEFAULT_LANGUAGE, DOMAIN, OWM_MODE_FREE_CURRENT, + OWM_MODE_FREE_FORECAST, OWM_MODE_V30, ) from homeassistant.components.openweathermap.weather import SERVICE_GET_MINUTE_FORECAST -from homeassistant.config_entries import ConfigEntryState -from homeassistant.const import ( - CONF_API_KEY, - CONF_LANGUAGE, - CONF_LATITUDE, - CONF_LONGITUDE, - CONF_MODE, - CONF_NAME, -) +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ServiceValidationError +from homeassistant.helpers import entity_registry as er -from .test_config_flow import _create_static_weather_report +from . import setup_platform -from tests.common import AsyncMock, MockConfigEntry, patch +from tests.common import MockConfigEntry, snapshot_platform ENTITY_ID = "weather.openweathermap" -API_KEY = "test_api_key" -LATITUDE = 12.34 -LONGITUDE = 56.78 -NAME = "openweathermap" - -# Define test data for mocked weather report -static_weather_report = _create_static_weather_report() -def mock_config_entry(mode: str) -> MockConfigEntry: - """Create a mock OpenWeatherMap config entry.""" - return MockConfigEntry( - domain=DOMAIN, - data={ - CONF_API_KEY: API_KEY, - CONF_LATITUDE: LATITUDE, - CONF_LONGITUDE: LONGITUDE, - CONF_NAME: NAME, - }, - options={CONF_MODE: mode, CONF_LANGUAGE: DEFAULT_LANGUAGE}, - version=5, - ) - - -@pytest.fixture -def mock_config_entry_free_current() -> MockConfigEntry: - """Create a mock OpenWeatherMap FREE_CURRENT config entry.""" - return mock_config_entry(OWM_MODE_FREE_CURRENT) - - -@pytest.fixture -def mock_config_entry_v30() -> MockConfigEntry: - """Create a mock OpenWeatherMap v3.0 config entry.""" - return mock_config_entry(OWM_MODE_V30) - - -async def setup_mock_config_entry( - hass: HomeAssistant, mock_config_entry: MockConfigEntry -): - """Set up the MockConfigEntry and assert it is loaded correctly.""" - mock_config_entry.add_to_hass(hass) - assert await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() - assert hass.states.get(ENTITY_ID) - assert mock_config_entry.state is ConfigEntryState.LOADED - - -@patch( - "pyopenweathermap.client.onecall_client.OWMOneCallClient.get_weather", - AsyncMock(return_value=static_weather_report), -) +@pytest.mark.parametrize("mode", [OWM_MODE_V30], indirect=True) async def test_get_minute_forecast( hass: HomeAssistant, - mock_config_entry_v30: MockConfigEntry, snapshot: SnapshotAssertion, + mock_config_entry: MockConfigEntry, + owm_client_mock: MagicMock, + mode: str, ) -> None: """Test the get_minute_forecast Service call.""" - await setup_mock_config_entry(hass, mock_config_entry_v30) + await setup_platform(hass, mock_config_entry, [Platform.WEATHER]) result = await hass.services.async_call( DOMAIN, SERVICE_GET_MINUTE_FORECAST, @@ -96,18 +45,19 @@ async def test_get_minute_forecast( assert result == snapshot(name="mock_service_response") -@patch( - "pyopenweathermap.client.free_client.OWMFreeClient.get_weather", - AsyncMock(return_value=static_weather_report), +@pytest.mark.parametrize( + "mode", [OWM_MODE_FREE_CURRENT, OWM_MODE_FREE_FORECAST], indirect=True ) -async def test_mode_fail( +async def test_get_minute_forecast_unavailable( hass: HomeAssistant, - mock_config_entry_free_current: MockConfigEntry, + snapshot: SnapshotAssertion, + mock_config_entry: MockConfigEntry, + owm_client_mock: MagicMock, + mode: str, ) -> None: """Test that Minute forecasting fails when mode is not v3.0.""" - await setup_mock_config_entry(hass, mock_config_entry_free_current) - # Expect a ServiceValidationError when mode is not OWM_MODE_V30 + await setup_platform(hass, mock_config_entry, [Platform.WEATHER]) with pytest.raises( ServiceValidationError, match="Minute forecast is available only when OpenWeatherMap mode is set to v3.0", @@ -119,3 +69,19 @@ async def test_mode_fail( blocking=True, return_response=True, ) + + +@pytest.mark.parametrize( + "mode", [OWM_MODE_V30, OWM_MODE_FREE_CURRENT, OWM_MODE_FREE_FORECAST], indirect=True +) +async def test_weather_states( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, + owm_client_mock: MagicMock, +) -> None: + """Test weather states are correctly collected from library with different modes and mocked function responses.""" + + await setup_platform(hass, mock_config_entry, [Platform.WEATHER]) + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)