From 5622103eb1eea8c19c7093262b8fe3be811dbf88 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 7 Oct 2025 08:44:34 +0200 Subject: [PATCH 1/7] Fix nintendo_parental RuntimeWarning in tests (#153884) --- tests/components/nintendo_parental/conftest.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/components/nintendo_parental/conftest.py b/tests/components/nintendo_parental/conftest.py index c6da3c8748b66b..cec07e5eb99bd8 100644 --- a/tests/components/nintendo_parental/conftest.py +++ b/tests/components/nintendo_parental/conftest.py @@ -48,6 +48,10 @@ def mock_nintendo_authenticator() -> Generator[MagicMock]: "homeassistant.components.nintendo_parental.config_flow.Authenticator", new=mock_auth_class, ), + patch( + "homeassistant.components.nintendo_parental.coordinator.NintendoParental.update", + return_value=None, + ), ): mock_auth = MagicMock() mock_auth._id_token = API_TOKEN From 34977abfecee99de478b0508734f1e518abc9ed1 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 7 Oct 2025 08:44:53 +0200 Subject: [PATCH 2/7] Remove Z-Wave JS voltage sensor overriding suggested precision (#153882) --- homeassistant/components/zwave_js/sensor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index 23b906a9d16078..aa7ba9fde3471e 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -134,7 +134,6 @@ device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfElectricPotential.VOLT, - suggested_display_precision=0, ), ( ENTITY_DESC_KEY_VOLTAGE, From 56f90e4d96fb100c7424f9e8d7110042ba294d0a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 7 Oct 2025 08:55:50 +0200 Subject: [PATCH 3/7] Update pytest warnings filter (#153881) --- pyproject.toml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index dc448a3254f0b3..cc62bb15051c8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -485,6 +485,8 @@ filterwarnings = [ "ignore:Deprecated call to `pkg_resources.declare_namespace\\(('azure'|'google.*'|'pywinusb'|'repoze'|'xbox'|'zope')\\)`:DeprecationWarning:pkg_resources", # -- tracked upstream / open PRs + # https://github.com/kbr/fritzconnection/pull/244 - v1.15.0 - 2025-05-17 + "ignore:.*invalid escape sequence:SyntaxWarning:.*fritzconnection.core.soaper", # https://github.com/hacf-fr/meteofrance-api/pull/688 - v1.4.0 - 2025-03-26 "ignore:datetime.*utcnow\\(\\) is deprecated and scheduled for removal:DeprecationWarning:meteofrance_api.model.forecast", @@ -522,8 +524,8 @@ filterwarnings = [ # https://pypi.org/project/motionblindsble/ - v0.1.3 - 2024-11-12 # https://github.com/LennP/motionblindsble/blob/0.1.3/motionblindsble/device.py#L390 "ignore:Passing additional arguments for BLEDevice is deprecated and has no effect:DeprecationWarning:motionblindsble.device", - # https://pypi.org/project/pyeconet/ - v0.1.28 - 2025-02-15 - # https://github.com/w1ll1am23/pyeconet/blob/v0.1.28/src/pyeconet/api.py#L38 + # https://pypi.org/project/pyeconet/ - v0.2.0 - 2025-10-05 + # https://github.com/w1ll1am23/pyeconet/blob/v0.2.0/src/pyeconet/api.py#L39 "ignore:ssl.PROTOCOL_TLS is deprecated:DeprecationWarning:pyeconet.api", # https://github.com/thecynic/pylutron - v0.2.18 - 2025-04-15 "ignore:setDaemon\\(\\) is deprecated, set the daemon attribute instead:DeprecationWarning:pylutron", @@ -565,7 +567,6 @@ filterwarnings = [ # - SyntaxWarning - is with literal # https://github.com/majuss/lupupy/pull/15 - >0.3.2 # https://pypi.org/project/opuslib/ - v3.0.1 - 2018-01-16 - # https://pypi.org/project/plumlightpad/ - v0.0.11 - 2018-10-16 # https://pypi.org/project/pyiss/ - v1.0.1 - 2016-12-19 "ignore:\"is.*\" with '.*' literal:SyntaxWarning:importlib._bootstrap", @@ -574,7 +575,6 @@ filterwarnings = [ "ignore:aifc was removed in Python 3.13.*'standard-aifc':DeprecationWarning:speech_recognition", "ignore:telnetlib was removed in Python 3.13.*'standard-telnetlib':DeprecationWarning:homeassistant.components.hddtemp.sensor", "ignore:telnetlib was removed in Python 3.13.*'standard-telnetlib':DeprecationWarning:ndms2_client.connection", - "ignore:telnetlib was removed in Python 3.13.*'standard-telnetlib':DeprecationWarning:plumlightpad.lightpad", "ignore:telnetlib was removed in Python 3.13.*'standard-telnetlib':DeprecationWarning:pyws66i", # -- Websockets 14.1 @@ -605,8 +605,6 @@ filterwarnings = [ "ignore:datetime.*utcnow\\(\\) is deprecated and scheduled for removal:DeprecationWarning:oauth2client.client", # https://pypi.org/project/pilight/ - v0.1.1 - 2016-10-19 "ignore:pkg_resources is deprecated as an API:UserWarning:pilight", - # https://pypi.org/project/plumlightpad/ - v0.0.11 - 2018-10-16 - "ignore:.*invalid escape sequence:SyntaxWarning:.*plumlightpad.plumdiscovery", # https://pypi.org/project/pure-python-adb/ - v0.3.0.dev0 - 2020-08-05 "ignore:.*invalid escape sequence:SyntaxWarning:.*ppadb", # https://pypi.org/project/pydub/ - v0.25.1 - 2021-03-10 From cdbe93c2890f65e8d1d72832ce3a046b3abf9967 Mon Sep 17 00:00:00 2001 From: Christopher Fenner <9592452+CFenner@users.noreply.github.com> Date: Tue, 7 Oct 2025 08:58:18 +0200 Subject: [PATCH 4/7] Set display precision for sensors in OpenWeatherMap integration (#153858) --- homeassistant/components/openweathermap/sensor.py | 2 ++ .../components/openweathermap/snapshots/test_sensor.ambr | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/openweathermap/sensor.py b/homeassistant/components/openweathermap/sensor.py index 2860abbe64cd30..9a6f5283e88bfa 100644 --- a/homeassistant/components/openweathermap/sensor.py +++ b/homeassistant/components/openweathermap/sensor.py @@ -121,6 +121,7 @@ native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=0, ), SensorEntityDescription( key=ATTR_API_CLOUDS, @@ -158,6 +159,7 @@ native_unit_of_measurement=UnitOfLength.METERS, device_class=SensorDeviceClass.DISTANCE, state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=0, ), SensorEntityDescription( key=ATTR_API_CONDITION, diff --git a/tests/components/openweathermap/snapshots/test_sensor.ambr b/tests/components/openweathermap/snapshots/test_sensor.ambr index ae80431f33c1f1..512f7b5280c38e 100644 --- a/tests/components/openweathermap/snapshots/test_sensor.ambr +++ b/tests/components/openweathermap/snapshots/test_sensor.ambr @@ -774,7 +774,7 @@ 'name': None, 'options': dict({ 'sensor': dict({ - 'suggested_display_precision': 2, + 'suggested_display_precision': 0, }), }), 'original_device_class': , @@ -1055,7 +1055,7 @@ 'name': None, 'options': dict({ 'sensor': dict({ - 'suggested_display_precision': 2, + 'suggested_display_precision': 0, }), }), 'original_device_class': , @@ -1703,7 +1703,7 @@ 'name': None, 'options': dict({ 'sensor': dict({ - 'suggested_display_precision': 2, + 'suggested_display_precision': 0, }), }), 'original_device_class': , @@ -1984,7 +1984,7 @@ 'name': None, 'options': dict({ 'sensor': dict({ - 'suggested_display_precision': 2, + 'suggested_display_precision': 0, }), }), 'original_device_class': , From 994a6ae7ed834be3969a34c3ad7e146710d837b1 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Tue, 7 Oct 2025 09:06:55 +0200 Subject: [PATCH 5/7] Fix restore cover state for Comelit SimpleHome (#153887) --- homeassistant/components/comelit/cover.py | 20 ++++++++--- tests/components/comelit/test_cover.py | 42 ++++++++++------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/comelit/cover.py b/homeassistant/components/comelit/cover.py index 70525ffe7123b1..1eff1449eb328a 100644 --- a/homeassistant/components/comelit/cover.py +++ b/homeassistant/components/comelit/cover.py @@ -7,7 +7,14 @@ from aiocomelit import ComelitSerialBridgeObject from aiocomelit.const import COVER, STATE_COVER, STATE_OFF, STATE_ON -from homeassistant.components.cover import CoverDeviceClass, CoverEntity +from homeassistant.components.cover import ( + STATE_CLOSED, + STATE_CLOSING, + STATE_OPEN, + STATE_OPENING, + CoverDeviceClass, + CoverEntity, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -62,7 +69,6 @@ def __init__( super().__init__(coordinator, device, config_entry_entry_id) # Device doesn't provide a status so we assume UNKNOWN at first startup self._last_action: int | None = None - self._last_state: str | None = None def _current_action(self, action: str) -> bool: """Return the current cover action.""" @@ -98,7 +104,6 @@ def is_opening(self) -> bool: @bridge_api_call async def _cover_set_state(self, action: int, state: int) -> None: """Set desired cover state.""" - self._last_state = self.state await self.coordinator.api.set_device_status(COVER, self._device.index, action) self.coordinator.data[COVER][self._device.index].status = state self.async_write_ha_state() @@ -124,5 +129,10 @@ async def async_added_to_hass(self) -> None: await super().async_added_to_hass() - if last_state := await self.async_get_last_state(): - self._last_state = last_state.state + if (state := await self.async_get_last_state()) is not None: + if state.state == STATE_CLOSED: + self._last_action = STATE_COVER.index(STATE_CLOSING) + if state.state == STATE_OPEN: + self._last_action = STATE_COVER.index(STATE_OPENING) + + self._attr_is_closed = state.state == STATE_CLOSED diff --git a/tests/components/comelit/test_cover.py b/tests/components/comelit/test_cover.py index 02efff1dd94a54..1e3b27e9c279a4 100644 --- a/tests/components/comelit/test_cover.py +++ b/tests/components/comelit/test_cover.py @@ -5,6 +5,7 @@ from aiocomelit.api import ComelitSerialBridgeObject from aiocomelit.const import COVER, WATT from freezegun.api import FrozenDateTimeFactory +import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.comelit.const import SCAN_INTERVAL @@ -17,14 +18,20 @@ STATE_CLOSING, STATE_OPEN, STATE_OPENING, + CoverState, ) from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, Platform -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, State from homeassistant.helpers import entity_registry as er from . import setup_integration -from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform +from tests.common import ( + MockConfigEntry, + async_fire_time_changed, + mock_restore_cache, + snapshot_platform, +) ENTITY_ID = "cover.cover0" @@ -162,37 +169,26 @@ async def test_cover_stop_if_stopped( assert state.state == STATE_UNKNOWN +@pytest.mark.parametrize( + "cover_state", + [ + CoverState.OPEN, + CoverState.CLOSED, + ], +) async def test_cover_restore_state( hass: HomeAssistant, - freezer: FrozenDateTimeFactory, mock_serial_bridge: AsyncMock, mock_serial_bridge_config_entry: MockConfigEntry, + cover_state: CoverState, ) -> None: """Test cover restore state on reload.""" - mock_serial_bridge.reset_mock() + mock_restore_cache(hass, [State(ENTITY_ID, cover_state)]) await setup_integration(hass, mock_serial_bridge_config_entry) assert (state := hass.states.get(ENTITY_ID)) - assert state.state == STATE_UNKNOWN - - # Open cover - await hass.services.async_call( - COVER_DOMAIN, - SERVICE_OPEN_COVER, - {ATTR_ENTITY_ID: ENTITY_ID}, - blocking=True, - ) - mock_serial_bridge.set_device_status.assert_called() - - assert (state := hass.states.get(ENTITY_ID)) - assert state.state == STATE_OPENING - - await hass.config_entries.async_reload(mock_serial_bridge_config_entry.entry_id) - await hass.async_block_till_done() - - assert (state := hass.states.get(ENTITY_ID)) - assert state.state == STATE_OPENING + assert state.state == cover_state async def test_cover_dynamic( From 934c0e3c4c5169a7afe500d85c277b829a674517 Mon Sep 17 00:00:00 2001 From: Christopher Fenner <9592452+CFenner@users.noreply.github.com> Date: Tue, 7 Oct 2025 10:15:01 +0200 Subject: [PATCH 6/7] fix typo in icon assignment of AccuWeather integration (#153890) --- homeassistant/components/accuweather/icons.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/accuweather/icons.json b/homeassistant/components/accuweather/icons.json index 183b4d2731dd33..2b715774bc84c0 100644 --- a/homeassistant/components/accuweather/icons.json +++ b/homeassistant/components/accuweather/icons.json @@ -1,6 +1,9 @@ { "entity": { "sensor": { + "air_quality": { + "default": "mdi:air-filter" + }, "cloud_ceiling": { "default": "mdi:weather-fog" }, @@ -34,9 +37,6 @@ "thunderstorm_probability_night": { "default": "mdi:weather-lightning" }, - "translation_key": { - "default": "mdi:air-filter" - }, "tree_pollen": { "default": "mdi:tree-outline" }, From e38ae47e7660b2f5ce8987047b9f04dd029effc4 Mon Sep 17 00:00:00 2001 From: Christopher Fenner <9592452+CFenner@users.noreply.github.com> Date: Tue, 7 Oct 2025 11:06:04 +0200 Subject: [PATCH 7/7] Add language and location selector to OpenWeatherMap config flow (#153645) Co-authored-by: Joost Lekkerkerker --- .../components/openweathermap/__init__.py | 6 +- .../components/openweathermap/config_flow.py | 39 ++++++--- .../components/openweathermap/sensor.py | 5 -- .../components/openweathermap/strings.json | 6 +- .../components/openweathermap/weather.py | 5 +- tests/components/openweathermap/conftest.py | 9 +- .../openweathermap/snapshots/test_sensor.ambr | 84 +++++++++---------- .../snapshots/test_weather.ambr | 6 +- .../openweathermap/test_config_flow.py | 76 +++++++++++------ 9 files changed, 132 insertions(+), 104 deletions(-) diff --git a/homeassistant/components/openweathermap/__init__.py b/homeassistant/components/openweathermap/__init__.py index 737e4fb8e4f65a..8b2bfb17c952ff 100644 --- a/homeassistant/components/openweathermap/__init__.py +++ b/homeassistant/components/openweathermap/__init__.py @@ -8,7 +8,7 @@ from pyopenweathermap import create_owm_client from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_API_KEY, CONF_LANGUAGE, CONF_MODE, CONF_NAME +from homeassistant.const import CONF_API_KEY, CONF_LANGUAGE, CONF_MODE from homeassistant.core import HomeAssistant from .const import CONFIG_FLOW_VERSION, DEFAULT_OWM_MODE, OWM_MODES, PLATFORMS @@ -25,7 +25,6 @@ class OpenweathermapData: """Runtime data definition.""" - name: str mode: str coordinator: OWMUpdateCoordinator @@ -34,7 +33,6 @@ async def async_setup_entry( hass: HomeAssistant, entry: OpenweathermapConfigEntry ) -> bool: """Set up OpenWeatherMap as config entry.""" - name = entry.data[CONF_NAME] api_key = entry.data[CONF_API_KEY] language = entry.options[CONF_LANGUAGE] mode = entry.options[CONF_MODE] @@ -51,7 +49,7 @@ async def async_setup_entry( entry.async_on_unload(entry.add_update_listener(async_update_options)) - entry.runtime_data = OpenweathermapData(name, mode, owm_coordinator) + entry.runtime_data = OpenweathermapData(mode, owm_coordinator) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/homeassistant/components/openweathermap/config_flow.py b/homeassistant/components/openweathermap/config_flow.py index 5805b602821505..64545726f1eb7f 100644 --- a/homeassistant/components/openweathermap/config_flow.py +++ b/homeassistant/components/openweathermap/config_flow.py @@ -14,12 +14,17 @@ CONF_API_KEY, CONF_LANGUAGE, CONF_LATITUDE, + CONF_LOCATION, CONF_LONGITUDE, CONF_MODE, - CONF_NAME, ) from homeassistant.core import callback -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.selector import ( + LanguageSelector, + LanguageSelectorConfig, + LocationSelector, + LocationSelectorConfig, +) from .const import ( CONFIG_FLOW_VERSION, @@ -34,10 +39,12 @@ USER_SCHEMA = vol.Schema( { - vol.Optional(CONF_NAME, default=DEFAULT_NAME): str, - vol.Optional(CONF_LATITUDE): cv.latitude, - vol.Optional(CONF_LONGITUDE): cv.longitude, - vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(LANGUAGES), + vol.Required(CONF_LOCATION): LocationSelector( + LocationSelectorConfig(radius=False) + ), + vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): LanguageSelector( + LanguageSelectorConfig(languages=LANGUAGES, native_name=True) + ), vol.Required(CONF_API_KEY): str, vol.Optional(CONF_MODE, default=DEFAULT_OWM_MODE): vol.In(OWM_MODES), } @@ -45,7 +52,9 @@ OPTIONS_SCHEMA = vol.Schema( { - vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(LANGUAGES), + vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): LanguageSelector( + LanguageSelectorConfig(languages=LANGUAGES, native_name=True) + ), vol.Optional(CONF_MODE, default=DEFAULT_OWM_MODE): vol.In(OWM_MODES), } ) @@ -70,8 +79,8 @@ async def async_step_user(self, user_input=None) -> ConfigFlowResult: description_placeholders = {} if user_input is not None: - latitude = user_input[CONF_LATITUDE] - longitude = user_input[CONF_LONGITUDE] + latitude = user_input[CONF_LOCATION][CONF_LATITUDE] + longitude = user_input[CONF_LOCATION][CONF_LONGITUDE] mode = user_input[CONF_MODE] await self.async_set_unique_id(f"{latitude}-{longitude}") @@ -82,15 +91,21 @@ async def async_step_user(self, user_input=None) -> ConfigFlowResult: ) if not errors: + # Flatten location + location = user_input.pop(CONF_LOCATION) + user_input[CONF_LATITUDE] = location[CONF_LATITUDE] + user_input[CONF_LONGITUDE] = location[CONF_LONGITUDE] data, options = build_data_and_options(user_input) return self.async_create_entry( - title=user_input[CONF_NAME], data=data, options=options + title=DEFAULT_NAME, data=data, options=options ) schema_data = user_input else: schema_data = { - CONF_LATITUDE: self.hass.config.latitude, - CONF_LONGITUDE: self.hass.config.longitude, + CONF_LOCATION: { + CONF_LATITUDE: self.hass.config.latitude, + CONF_LONGITUDE: self.hass.config.longitude, + }, CONF_LANGUAGE: self.hass.config.language, } diff --git a/homeassistant/components/openweathermap/sensor.py b/homeassistant/components/openweathermap/sensor.py index 9a6f5283e88bfa..4cb31b749f0042 100644 --- a/homeassistant/components/openweathermap/sensor.py +++ b/homeassistant/components/openweathermap/sensor.py @@ -229,7 +229,6 @@ async def async_setup_entry( ) -> None: """Set up OpenWeatherMap sensor entities based on a config entry.""" domain_data = config_entry.runtime_data - name = domain_data.name unique_id = config_entry.unique_id assert unique_id is not None coordinator = domain_data.coordinator @@ -244,7 +243,6 @@ async def async_setup_entry( elif domain_data.mode == OWM_MODE_AIRPOLLUTION: async_add_entities( OpenWeatherMapSensor( - name, unique_id, description, coordinator, @@ -254,7 +252,6 @@ async def async_setup_entry( else: async_add_entities( OpenWeatherMapSensor( - name, unique_id, description, coordinator, @@ -272,7 +269,6 @@ class AbstractOpenWeatherMapSensor(SensorEntity): def __init__( self, - name: str, unique_id: str, description: SensorEntityDescription, coordinator: OWMUpdateCoordinator, @@ -286,7 +282,6 @@ def __init__( entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, unique_id)}, manufacturer=MANUFACTURER, - name=name, ) @property diff --git a/homeassistant/components/openweathermap/strings.json b/homeassistant/components/openweathermap/strings.json index 718ce3e6fdd542..e3e5781882c454 100644 --- a/homeassistant/components/openweathermap/strings.json +++ b/homeassistant/components/openweathermap/strings.json @@ -12,16 +12,14 @@ "data": { "api_key": "[%key:common::config_flow::data::api_key%]", "language": "[%key:common::config_flow::data::language%]", - "latitude": "[%key:common::config_flow::data::latitude%]", - "longitude": "[%key:common::config_flow::data::longitude%]", + "location": "[%key:common::config_flow::data::location%]", "mode": "[%key:common::config_flow::data::mode%]", "name": "[%key:common::config_flow::data::name%]" }, "data_description": { "api_key": "API key for the OpenWeatherMap integration", "language": "Language for the OpenWeatherMap content", - "latitude": "Latitude of the location", - "longitude": "Longitude of the location", + "location": "Location to get the weather data for", "mode": "Mode for the OpenWeatherMap API", "name": "Name for this OpenWeatherMap location" }, diff --git a/homeassistant/components/openweathermap/weather.py b/homeassistant/components/openweathermap/weather.py index 56f44fa46fb7ec..37f8e117ee1669 100644 --- a/homeassistant/components/openweathermap/weather.py +++ b/homeassistant/components/openweathermap/weather.py @@ -57,14 +57,13 @@ async def async_setup_entry( ) -> None: """Set up OpenWeatherMap weather entity based on a config entry.""" domain_data = config_entry.runtime_data - name = domain_data.name mode = domain_data.mode if mode != OWM_MODE_AIRPOLLUTION: weather_coordinator = domain_data.coordinator unique_id = f"{config_entry.unique_id}" - owm_weather = OpenWeatherMapWeather(name, unique_id, mode, weather_coordinator) + owm_weather = OpenWeatherMapWeather(unique_id, mode, weather_coordinator) async_add_entities([owm_weather], False) @@ -93,7 +92,6 @@ class OpenWeatherMapWeather(SingleCoordinatorWeatherEntity[OWMUpdateCoordinator] def __init__( self, - name: str, unique_id: str, mode: str, weather_coordinator: OWMUpdateCoordinator, @@ -105,7 +103,6 @@ def __init__( entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, unique_id)}, manufacturer=MANUFACTURER, - name=name, ) self.mode = mode diff --git a/tests/components/openweathermap/conftest.py b/tests/components/openweathermap/conftest.py index 7c7de776acf55b..b534d8fd98c788 100644 --- a/tests/components/openweathermap/conftest.py +++ b/tests/components/openweathermap/conftest.py @@ -17,14 +17,17 @@ from pyopenweathermap.client.owm_abstract_client import OWMClient import pytest -from homeassistant.components.openweathermap.const import DEFAULT_LANGUAGE, DOMAIN +from homeassistant.components.openweathermap.const import ( + DEFAULT_LANGUAGE, + DEFAULT_NAME, + DOMAIN, +) from homeassistant.const import ( CONF_API_KEY, CONF_LANGUAGE, CONF_LATITUDE, CONF_LONGITUDE, CONF_MODE, - CONF_NAME, ) from tests.common import MockConfigEntry, patch @@ -50,7 +53,6 @@ def mock_config_entry(mode: str) -> MockConfigEntry: CONF_API_KEY: API_KEY, CONF_LATITUDE: LATITUDE, CONF_LONGITUDE: LONGITUDE, - CONF_NAME: NAME, }, options={ CONF_MODE: mode, @@ -59,6 +61,7 @@ def mock_config_entry(mode: str) -> MockConfigEntry: entry_id="test", version=5, unique_id=f"{LATITUDE}-{LONGITUDE}", + title=DEFAULT_NAME, ) diff --git a/tests/components/openweathermap/snapshots/test_sensor.ambr b/tests/components/openweathermap/snapshots/test_sensor.ambr index 512f7b5280c38e..b184aac02ba0d6 100644 --- a/tests/components/openweathermap/snapshots/test_sensor.ambr +++ b/tests/components/openweathermap/snapshots/test_sensor.ambr @@ -41,7 +41,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'aqi', - 'friendly_name': 'openweathermap Air quality index', + 'friendly_name': 'OpenWeatherMap Air quality index', 'state_class': , }), 'context': , @@ -94,7 +94,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'carbon_monoxide', - 'friendly_name': 'openweathermap Carbon monoxide', + 'friendly_name': 'OpenWeatherMap Carbon monoxide', 'state_class': , 'unit_of_measurement': 'μg/m³', }), @@ -148,7 +148,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'nitrogen_dioxide', - 'friendly_name': 'openweathermap Nitrogen dioxide', + 'friendly_name': 'OpenWeatherMap Nitrogen dioxide', 'state_class': , 'unit_of_measurement': 'μg/m³', }), @@ -202,7 +202,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'nitrogen_monoxide', - 'friendly_name': 'openweathermap Nitrogen monoxide', + 'friendly_name': 'OpenWeatherMap Nitrogen monoxide', 'state_class': , 'unit_of_measurement': 'μg/m³', }), @@ -256,7 +256,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'ozone', - 'friendly_name': 'openweathermap Ozone', + 'friendly_name': 'OpenWeatherMap Ozone', 'state_class': , 'unit_of_measurement': 'μg/m³', }), @@ -310,7 +310,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'pm10', - 'friendly_name': 'openweathermap PM10', + 'friendly_name': 'OpenWeatherMap PM10', 'state_class': , 'unit_of_measurement': 'μg/m³', }), @@ -364,7 +364,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'pm25', - 'friendly_name': 'openweathermap PM2.5', + 'friendly_name': 'OpenWeatherMap PM2.5', 'state_class': , 'unit_of_measurement': 'μg/m³', }), @@ -418,7 +418,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'sulphur_dioxide', - 'friendly_name': 'openweathermap Sulphur dioxide', + 'friendly_name': 'OpenWeatherMap Sulphur dioxide', 'state_class': , 'unit_of_measurement': 'μg/m³', }), @@ -471,7 +471,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Cloud coverage', + 'friendly_name': 'OpenWeatherMap Cloud coverage', 'state_class': , 'unit_of_measurement': '%', }), @@ -522,7 +522,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Condition', + 'friendly_name': 'OpenWeatherMap Condition', }), 'context': , 'entity_id': 'sensor.openweathermap_condition', @@ -577,7 +577,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'temperature', - 'friendly_name': 'openweathermap Dew Point', + 'friendly_name': 'OpenWeatherMap Dew Point', 'state_class': , 'unit_of_measurement': , }), @@ -634,7 +634,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'temperature', - 'friendly_name': 'openweathermap Feels like temperature', + 'friendly_name': 'OpenWeatherMap Feels like temperature', 'state_class': , 'unit_of_measurement': , }), @@ -688,7 +688,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'humidity', - 'friendly_name': 'openweathermap Humidity', + 'friendly_name': 'OpenWeatherMap Humidity', 'state_class': , 'unit_of_measurement': '%', }), @@ -739,7 +739,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Precipitation kind', + 'friendly_name': 'OpenWeatherMap Precipitation kind', }), 'context': , 'entity_id': 'sensor.openweathermap_precipitation_kind', @@ -794,7 +794,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'pressure', - 'friendly_name': 'openweathermap Pressure', + 'friendly_name': 'OpenWeatherMap Pressure', 'state_class': , 'unit_of_measurement': , }), @@ -851,7 +851,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'precipitation_intensity', - 'friendly_name': 'openweathermap Rain', + 'friendly_name': 'OpenWeatherMap Rain', 'state_class': , 'unit_of_measurement': , }), @@ -908,7 +908,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'precipitation_intensity', - 'friendly_name': 'openweathermap Snow', + 'friendly_name': 'OpenWeatherMap Snow', 'state_class': , 'unit_of_measurement': , }), @@ -965,7 +965,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'temperature', - 'friendly_name': 'openweathermap Temperature', + 'friendly_name': 'OpenWeatherMap Temperature', 'state_class': , 'unit_of_measurement': , }), @@ -1018,7 +1018,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap UV Index', + 'friendly_name': 'OpenWeatherMap UV Index', 'state_class': , 'unit_of_measurement': 'UV index', }), @@ -1075,7 +1075,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'distance', - 'friendly_name': 'openweathermap Visibility', + 'friendly_name': 'OpenWeatherMap Visibility', 'state_class': , 'unit_of_measurement': , }), @@ -1126,7 +1126,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Weather', + 'friendly_name': 'OpenWeatherMap Weather', }), 'context': , 'entity_id': 'sensor.openweathermap_weather', @@ -1175,7 +1175,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Weather Code', + 'friendly_name': 'OpenWeatherMap Weather Code', }), 'context': , 'entity_id': 'sensor.openweathermap_weather_code', @@ -1227,7 +1227,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'wind_direction', - 'friendly_name': 'openweathermap Wind bearing', + 'friendly_name': 'OpenWeatherMap Wind bearing', 'state_class': , 'unit_of_measurement': '°', }), @@ -1287,7 +1287,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'wind_speed', - 'friendly_name': 'openweathermap Wind gust', + 'friendly_name': 'OpenWeatherMap Wind gust', 'state_class': , 'unit_of_measurement': , }), @@ -1347,7 +1347,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'wind_speed', - 'friendly_name': 'openweathermap Wind speed', + 'friendly_name': 'OpenWeatherMap Wind speed', 'state_class': , 'unit_of_measurement': , }), @@ -1400,7 +1400,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Cloud coverage', + 'friendly_name': 'OpenWeatherMap Cloud coverage', 'state_class': , 'unit_of_measurement': '%', }), @@ -1451,7 +1451,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Condition', + 'friendly_name': 'OpenWeatherMap Condition', }), 'context': , 'entity_id': 'sensor.openweathermap_condition', @@ -1506,7 +1506,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'temperature', - 'friendly_name': 'openweathermap Dew Point', + 'friendly_name': 'OpenWeatherMap Dew Point', 'state_class': , 'unit_of_measurement': , }), @@ -1563,7 +1563,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'temperature', - 'friendly_name': 'openweathermap Feels like temperature', + 'friendly_name': 'OpenWeatherMap Feels like temperature', 'state_class': , 'unit_of_measurement': , }), @@ -1617,7 +1617,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'humidity', - 'friendly_name': 'openweathermap Humidity', + 'friendly_name': 'OpenWeatherMap Humidity', 'state_class': , 'unit_of_measurement': '%', }), @@ -1668,7 +1668,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Precipitation kind', + 'friendly_name': 'OpenWeatherMap Precipitation kind', }), 'context': , 'entity_id': 'sensor.openweathermap_precipitation_kind', @@ -1723,7 +1723,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'pressure', - 'friendly_name': 'openweathermap Pressure', + 'friendly_name': 'OpenWeatherMap Pressure', 'state_class': , 'unit_of_measurement': , }), @@ -1780,7 +1780,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'precipitation_intensity', - 'friendly_name': 'openweathermap Rain', + 'friendly_name': 'OpenWeatherMap Rain', 'state_class': , 'unit_of_measurement': , }), @@ -1837,7 +1837,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'precipitation_intensity', - 'friendly_name': 'openweathermap Snow', + 'friendly_name': 'OpenWeatherMap Snow', 'state_class': , 'unit_of_measurement': , }), @@ -1894,7 +1894,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'temperature', - 'friendly_name': 'openweathermap Temperature', + 'friendly_name': 'OpenWeatherMap Temperature', 'state_class': , 'unit_of_measurement': , }), @@ -1947,7 +1947,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap UV Index', + 'friendly_name': 'OpenWeatherMap UV Index', 'state_class': , 'unit_of_measurement': 'UV index', }), @@ -2004,7 +2004,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'distance', - 'friendly_name': 'openweathermap Visibility', + 'friendly_name': 'OpenWeatherMap Visibility', 'state_class': , 'unit_of_measurement': , }), @@ -2055,7 +2055,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Weather', + 'friendly_name': 'OpenWeatherMap Weather', }), 'context': , 'entity_id': 'sensor.openweathermap_weather', @@ -2104,7 +2104,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', - 'friendly_name': 'openweathermap Weather Code', + 'friendly_name': 'OpenWeatherMap Weather Code', }), 'context': , 'entity_id': 'sensor.openweathermap_weather_code', @@ -2156,7 +2156,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'wind_direction', - 'friendly_name': 'openweathermap Wind bearing', + 'friendly_name': 'OpenWeatherMap Wind bearing', 'state_class': , 'unit_of_measurement': '°', }), @@ -2216,7 +2216,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'wind_speed', - 'friendly_name': 'openweathermap Wind gust', + 'friendly_name': 'OpenWeatherMap Wind gust', 'state_class': , 'unit_of_measurement': , }), @@ -2276,7 +2276,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Data provided by OpenWeatherMap', 'device_class': 'wind_speed', - 'friendly_name': 'openweathermap Wind speed', + 'friendly_name': 'OpenWeatherMap Wind speed', 'state_class': , 'unit_of_measurement': , }), diff --git a/tests/components/openweathermap/snapshots/test_weather.ambr b/tests/components/openweathermap/snapshots/test_weather.ambr index be3db7bc59434a..733545a3f43b84 100644 --- a/tests/components/openweathermap/snapshots/test_weather.ambr +++ b/tests/components/openweathermap/snapshots/test_weather.ambr @@ -65,7 +65,7 @@ 'attribution': 'Data provided by OpenWeatherMap', 'cloud_coverage': 75, 'dew_point': 4.0, - 'friendly_name': 'openweathermap', + 'friendly_name': 'OpenWeatherMap', 'humidity': 82, 'precipitation_unit': , 'pressure': 1000.0, @@ -129,7 +129,7 @@ 'attribution': 'Data provided by OpenWeatherMap', 'cloud_coverage': 75, 'dew_point': 4.0, - 'friendly_name': 'openweathermap', + 'friendly_name': 'OpenWeatherMap', 'humidity': 82, 'precipitation_unit': , 'pressure': 1000.0, @@ -194,7 +194,7 @@ 'attribution': 'Data provided by OpenWeatherMap', 'cloud_coverage': 75, 'dew_point': 4.0, - 'friendly_name': 'openweathermap', + 'friendly_name': 'OpenWeatherMap', 'humidity': 82, 'precipitation_unit': , 'pressure': 1000.0, diff --git a/tests/components/openweathermap/test_config_flow.py b/tests/components/openweathermap/test_config_flow.py index 0315ca91010465..039498e5ec3562 100644 --- a/tests/components/openweathermap/test_config_flow.py +++ b/tests/components/openweathermap/test_config_flow.py @@ -7,6 +7,7 @@ from homeassistant.components.openweathermap.const import ( DEFAULT_LANGUAGE, + DEFAULT_NAME, DEFAULT_OWM_MODE, DOMAIN, OWM_MODE_V30, @@ -16,9 +17,9 @@ CONF_API_KEY, CONF_LANGUAGE, CONF_LATITUDE, + CONF_LOCATION, CONF_LONGITUDE, CONF_MODE, - CONF_NAME, ) from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -28,7 +29,6 @@ from tests.common import MockConfigEntry CONFIG = { - CONF_NAME: "openweathermap", CONF_API_KEY: "foo", CONF_LATITUDE: LATITUDE, CONF_LONGITUDE: LONGITUDE, @@ -36,6 +36,13 @@ CONF_MODE: OWM_MODE_V30, } +USER_INPUT = { + CONF_API_KEY: "foo", + CONF_LOCATION: {CONF_LATITUDE: LATITUDE, CONF_LONGITUDE: LONGITUDE}, + CONF_LANGUAGE: DEFAULT_LANGUAGE, + CONF_MODE: OWM_MODE_V30, +} + VALID_YAML_CONFIG = {CONF_API_KEY: "foo"} @@ -47,31 +54,32 @@ async def test_successful_config_flow( result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) - assert result["type"] is FlowResultType.FORM assert result["step_id"] == "user" assert result["errors"] == {} - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONFIG + # create entry + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + USER_INPUT, ) - await hass.async_block_till_done() + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == DEFAULT_NAME + assert result["data"][CONF_LATITUDE] == USER_INPUT[CONF_LOCATION][CONF_LATITUDE] + assert result["data"][CONF_LONGITUDE] == USER_INPUT[CONF_LOCATION][CONF_LONGITUDE] + assert result["data"][CONF_API_KEY] == USER_INPUT[CONF_API_KEY] + # validate entry state conf_entries = hass.config_entries.async_entries(DOMAIN) entry = conf_entries[0] assert entry.state is ConfigEntryState.LOADED + # unload entry await hass.config_entries.async_unload(conf_entries[0].entry_id) await hass.async_block_till_done() assert entry.state is ConfigEntryState.NOT_LOADED - assert result["type"] is FlowResultType.CREATE_ENTRY - assert result["title"] == CONFIG[CONF_NAME] - assert result["data"][CONF_LATITUDE] == CONFIG[CONF_LATITUDE] - assert result["data"][CONF_LONGITUDE] == CONFIG[CONF_LONGITUDE] - 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( @@ -84,13 +92,14 @@ async def test_abort_config_flow( result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) - 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) - + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + USER_INPUT, + ) assert result["type"] is FlowResultType.ABORT @@ -156,19 +165,26 @@ async def test_form_invalid_api_key( owm_client_mock: AsyncMock, ) -> None: """Test that the form is served with no input.""" - owm_client_mock.validate_key.return_value = False result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONFIG + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + # invalid api key + owm_client_mock.validate_key.return_value = False + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + USER_INPUT, ) - assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": "invalid_api_key"} - + # valid api key owm_client_mock.validate_key.return_value = True result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=CONFIG + result["flow_id"], + USER_INPUT, ) - assert result["type"] is FlowResultType.CREATE_ENTRY @@ -177,17 +193,23 @@ async def test_form_api_call_error( owm_client_mock: AsyncMock, ) -> None: """Test setting up with api call error.""" - owm_client_mock.validate_key.side_effect = RequestError("oops") result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONFIG + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {} + # simulate api call error + owm_client_mock.validate_key.side_effect = RequestError("oops") + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + USER_INPUT, ) - assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": "cannot_connect"} - + # simulate successful api call owm_client_mock.validate_key.side_effect = None result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=CONFIG + result["flow_id"], + USER_INPUT, ) - assert result["type"] is FlowResultType.CREATE_ENTRY