From 81e7dd9fad1c2558d375bdf5e52f773cb61be142 Mon Sep 17 00:00:00 2001 From: fdebrus <33791533+fdebrus@users.noreply.github.com> Date: Sun, 4 Oct 2026 08:37:21 +0200 Subject: [PATCH] Bump aioaquarite to 0.13.0 and drop the Vistapool optimistic layer (#184132) Co-authored-by: Claude Fable 5.1 --- homeassistant/components/vistapool/button.py | 21 +++-- .../components/vistapool/coordinator.py | 76 +++++----------- homeassistant/components/vistapool/light.py | 1 - .../components/vistapool/manifest.json | 2 +- homeassistant/components/vistapool/number.py | 1 - .../components/vistapool/quality_scale.yaml | 8 +- homeassistant/components/vistapool/select.py | 2 - homeassistant/components/vistapool/switch.py | 1 - homeassistant/components/vistapool/time.py | 1 - requirements_all.txt | 2 +- tests/components/vistapool/conftest.py | 3 - tests/components/vistapool/test_button.py | 74 +++++++-------- tests/components/vistapool/test_init.py | 43 ++++----- tests/components/vistapool/test_light.py | 37 -------- tests/components/vistapool/test_number.py | 1 - tests/components/vistapool/test_select.py | 91 +------------------ tests/components/vistapool/test_time.py | 1 - 17 files changed, 90 insertions(+), 275 deletions(-) diff --git a/homeassistant/components/vistapool/button.py b/homeassistant/components/vistapool/button.py index 0524f84226d1..a8c3e900566c 100644 --- a/homeassistant/components/vistapool/button.py +++ b/homeassistant/components/vistapool/button.py @@ -1,6 +1,5 @@ """Vistapool Button entities.""" -import asyncio from typing import override from aioaquarite import AquariteError @@ -60,7 +59,8 @@ class VistapoolLEDPulseButton(VistapoolEntity, ButtonEntity): Mirrors the "Next" button under LED Color in the Vistapool app's Illumination screen. If the light is on, sends light.status=0, waits a moment, then light.status=1; the physical LED fixture advances to the - next color on power-on. If the light is off, just turns it on. + next color on power-on. If the light is off, just turns it on. The + library runs the sequence, so the light never shows the intermediate off. """ _attr_translation_key = "led_pulse" @@ -74,18 +74,21 @@ class VistapoolLEDPulseButton(VistapoolEntity, ButtonEntity): async def async_press(self) -> None: """Send a color-advance pulse to the pool LED fixture.""" try: - if self.coordinator.get_value(_LIGHT_STATUS_PATH) in (True, "1"): + if self.coordinator.get_value(_LIGHT_STATUS_PATH) == 1: + await self.coordinator.api.pulse( + self.coordinator.pool_id, + _LIGHT_STATUS_PATH, + 0, + 1, + _LED_PULSE_DELAY_SECONDS, + ) + else: await self.coordinator.api.set_value( - self.coordinator.pool_id, _LIGHT_STATUS_PATH, 0 + self.coordinator.pool_id, _LIGHT_STATUS_PATH, 1 ) - await asyncio.sleep(_LED_PULSE_DELAY_SECONDS) - await self.coordinator.api.set_value( - self.coordinator.pool_id, _LIGHT_STATUS_PATH, 1 - ) except AquariteError as err: raise HomeAssistantError( translation_domain=DOMAIN, translation_key="set_failed", translation_placeholders={"entity": self.entity_id}, ) from err - self.coordinator.apply_optimistic(_LIGHT_STATUS_PATH, 1) diff --git a/homeassistant/components/vistapool/coordinator.py b/homeassistant/components/vistapool/coordinator.py index 6a59cc11dcfb..91a0426d6db2 100644 --- a/homeassistant/components/vistapool/coordinator.py +++ b/homeassistant/components/vistapool/coordinator.py @@ -68,41 +68,38 @@ class VistapoolDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): return self._push_connected async def subscribe(self) -> None: - """Subscribe to Firestore real-time updates via the library.""" - - def _on_data(data: dict[str, Any]) -> None: - """Callback from the Firestore thread; push data to the HA loop.""" - self.hass.loop.call_soon_threadsafe(self._async_handle_push, data) + """Subscribe to Firestore real-time updates via the library. + The library invokes the data callback on the event loop, with + acknowledged writes already reflected, so deliveries are published + as they come. + """ self.subscription = await self.api.subscribe_pool_resilient( - self.pool_id, _on_data, on_health=self._async_on_subscription_health + self.pool_id, + self.async_set_updated_data, + on_health=self._async_on_subscription_health, ) - @callback - def _async_handle_push(self, data: dict[str, Any]) -> None: - """Apply a snapshot; its arrival is what proves the connection is up.""" - if not self._push_connected: - self._push_connected = True - _LOGGER.info("Reconnected to %s, entities are available again", self.name) - self.async_set_updated_data(data) - @callback def _async_on_subscription_health(self, healthy: bool) -> None: - """Mark entities unavailable while the push connection is down. + """Mirror the push connection state into entity availability. - Tracked separately from last_update_success: an optimistic update + Tracked separately from last_update_success: an acknowledged write or a manual refresh sets that flag back to True while the - subscription is still down, and the health callback only fires on - transitions, so it would not correct it. Only an incoming snapshot - clears this. + subscription is still down. The library reports healthy only once a + reconnected stream has delivered a consistent snapshot, so data + arriving in between must not fake availability either. """ - if healthy or not self._push_connected: + if healthy == self._push_connected: return - self._push_connected = False - _LOGGER.warning( - "Lost the connection to %s, entities are unavailable until it recovers", - self.name, - ) + self._push_connected = healthy + if healthy: + _LOGGER.info("Reconnected to %s, entities are available again", self.name) + else: + _LOGGER.warning( + "Lost the connection to %s, entities are unavailable until it recovers", + self.name, + ) self.async_update_listeners() @override @@ -116,32 +113,3 @@ class VistapoolDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): def get_value(self, path: str, default: Any = None) -> Any: """Get nested data using dot-notation path.""" return AquariteClient.get_value(self.data, path, default) - - def apply_optimistic(self, value_path: str, value: Any) -> None: - """Reflect a just-written value before the Firestore push round-trips. - - Hayward's cloud takes several seconds to acknowledge a write back - through Firestore, which would make the UI feel laggy. Writing into - coordinator.data after a successful REST call gives entities instant - feedback; the next snapshot from Firestore overwrites it harmlessly. - """ - self.apply_optimistic_values({value_path: value}) - - def apply_optimistic_values(self, updates: dict[str, Any]) -> None: - """Reflect several just-written values as a single update. - - Applying them one at a time would publish a state where only part - of the write has landed, which entities derived from more than one - path briefly read as a different value. - """ - for value_path, value in updates.items(): - keys = value_path.split(".") - target: dict[str, Any] = self.data - for key in keys[:-1]: - child = target.get(key) - if not isinstance(child, dict): - child = {} - target[key] = child - target = child - target[keys[-1]] = value - self.async_set_updated_data(self.data) diff --git a/homeassistant/components/vistapool/light.py b/homeassistant/components/vistapool/light.py index 2cd08043fc2c..9cfe4e4223b6 100644 --- a/homeassistant/components/vistapool/light.py +++ b/homeassistant/components/vistapool/light.py @@ -86,4 +86,3 @@ class VistapoolLight(VistapoolEntity, LightEntity): translation_key="set_failed", translation_placeholders={"entity": self.entity_id}, ) from err - self.coordinator.apply_optimistic(_VALUE_PATH, value) diff --git a/homeassistant/components/vistapool/manifest.json b/homeassistant/components/vistapool/manifest.json index 2164d715a111..adf11e60345d 100644 --- a/homeassistant/components/vistapool/manifest.json +++ b/homeassistant/components/vistapool/manifest.json @@ -13,5 +13,5 @@ "iot_class": "cloud_push", "loggers": ["aioaquarite"], "quality_scale": "gold", - "requirements": ["aioaquarite==0.11.0"] + "requirements": ["aioaquarite==0.13.0"] } diff --git a/homeassistant/components/vistapool/number.py b/homeassistant/components/vistapool/number.py index 55993774452a..68fa6552982e 100644 --- a/homeassistant/components/vistapool/number.py +++ b/homeassistant/components/vistapool/number.py @@ -260,4 +260,3 @@ class VistapoolNumber(VistapoolEntity, NumberEntity): translation_key="set_failed", translation_placeholders={"entity": self.entity_id}, ) from err - self.coordinator.apply_optimistic(self.entity_description.value_path, raw) diff --git a/homeassistant/components/vistapool/quality_scale.yaml b/homeassistant/components/vistapool/quality_scale.yaml index 0e5e6f23b5a1..6f35ab6c4e1f 100644 --- a/homeassistant/components/vistapool/quality_scale.yaml +++ b/homeassistant/components/vistapool/quality_scale.yaml @@ -77,12 +77,6 @@ rules: stale-devices: done # Platinum - async-dependency: - status: todo - comment: >- - Commands and Firestore reads are native async, but the real-time - listener runs the synchronous google-cloud-firestore client in a - thread: AsyncDocumentReference.on_snapshot raises NotImplementedError - upstream, so the push subscription cannot be native async today. + async-dependency: todo inject-websession: done strict-typing: done diff --git a/homeassistant/components/vistapool/select.py b/homeassistant/components/vistapool/select.py index 42e3160803ed..f133a2277709 100644 --- a/homeassistant/components/vistapool/select.py +++ b/homeassistant/components/vistapool/select.py @@ -186,7 +186,6 @@ class VistapoolSelect(VistapoolEntity, SelectEntity): translation_key="set_failed", translation_placeholders={"entity": self.entity_id}, ) from err - self.coordinator.apply_optimistic(self.entity_description.value_path, value) class VistapoolLightModeSelect(VistapoolEntity, SelectEntity): @@ -231,4 +230,3 @@ class VistapoolLightModeSelect(VistapoolEntity, SelectEntity): translation_key="set_failed", translation_placeholders={"entity": self.entity_id}, ) from err - self.coordinator.apply_optimistic_values(updates) diff --git a/homeassistant/components/vistapool/switch.py b/homeassistant/components/vistapool/switch.py index f2fc99bb8963..181a8f8b1793 100644 --- a/homeassistant/components/vistapool/switch.py +++ b/homeassistant/components/vistapool/switch.py @@ -170,4 +170,3 @@ class VistapoolSwitch(VistapoolEntity, SwitchEntity): translation_key="set_failed", translation_placeholders={"entity": self.entity_id}, ) from err - self.coordinator.apply_optimistic(self.entity_description.value_path, value) diff --git a/homeassistant/components/vistapool/time.py b/homeassistant/components/vistapool/time.py index f0da17b8c07e..1e01a3484a73 100644 --- a/homeassistant/components/vistapool/time.py +++ b/homeassistant/components/vistapool/time.py @@ -146,4 +146,3 @@ class VistapoolTime(VistapoolEntity, TimeEntity): translation_key="set_failed", translation_placeholders={"entity": self.entity_id}, ) from err - self.coordinator.apply_optimistic(self.entity_description.value_path, seconds) diff --git a/requirements_all.txt b/requirements_all.txt index cd454624cbe9..a9a5c35d6cc9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -203,7 +203,7 @@ aioapcaccess==1.0.0 aioaquacell==1.0.0 # homeassistant.components.vistapool -aioaquarite==0.11.0 +aioaquarite==0.13.0 # homeassistant.components.aseko_pool_live aioaseko==1.0.0 diff --git a/tests/components/vistapool/conftest.py b/tests/components/vistapool/conftest.py index cc5ad5dd8e12..cb44033ae005 100644 --- a/tests/components/vistapool/conftest.py +++ b/tests/components/vistapool/conftest.py @@ -73,9 +73,6 @@ def mock_vistapool_client( # default to an empty dict so always-on sensors come up with # `native_value=None` and module-gated sensors are skipped. client.fetch_pool_data = AsyncMock(return_value={}) - # The token-refresh loop awaits `auth.get_client()` and expects - # `(client, refreshed)`. - mock_vistapool_auth.get_client = AsyncMock(return_value=(client, False)) with ( patch("homeassistant.components.vistapool.AquariteClient", return_value=client), patch( diff --git a/tests/components/vistapool/test_button.py b/tests/components/vistapool/test_button.py index eb0816b87a3a..d9ec0501ad35 100644 --- a/tests/components/vistapool/test_button.py +++ b/tests/components/vistapool/test_button.py @@ -10,6 +10,7 @@ import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.components.vistapool.button import _LED_PULSE_DELAY_SECONDS from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError @@ -28,13 +29,6 @@ def _only_button_platform() -> Generator[None]: yield -@pytest.fixture(autouse=True) -def _skip_pulse_delay() -> Generator[None]: - """Skip the LED pulse delay so tests don't actually sleep.""" - with patch("homeassistant.components.vistapool.button._LED_PULSE_DELAY_SECONDS", 0): - yield - - async def test_all_entities( hass: HomeAssistant, snapshot: SnapshotAssertion, @@ -97,7 +91,7 @@ async def test_button_press_when_light_on( mock_config_entry: MockConfigEntry, mock_vistapool_client: AsyncMock, ) -> None: - """Test pressing the button when the light is on power-cycles it.""" + """Test pressing the button when the light is on runs the library pulse.""" mock_vistapool_client.fetch_pool_data.return_value = { "main": {"hasLED": 1, "version": 1}, "light": {"status": 1}, @@ -114,17 +108,10 @@ async def test_button_press_when_light_on( blocking=True, ) - assert mock_vistapool_client.set_value.await_count == 2 - assert mock_vistapool_client.set_value.await_args_list[0].args == ( - "ABCDEF1234567890", - "light.status", - 0, - ) - assert mock_vistapool_client.set_value.await_args_list[1].args == ( - "ABCDEF1234567890", - "light.status", - 1, + mock_vistapool_client.pulse.assert_awaited_once_with( + "ABCDEF1234567890", "light.status", 0, 1, _LED_PULSE_DELAY_SECONDS ) + mock_vistapool_client.set_value.assert_not_awaited() async def test_button_press_rapid_repeat_after_off( @@ -132,11 +119,11 @@ async def test_button_press_rapid_repeat_after_off( mock_config_entry: MockConfigEntry, mock_vistapool_client: AsyncMock, ) -> None: - """Test a second press lands the off/on pulse instead of repeating turn-on. + """Test a second press lands the pulse instead of repeating turn-on. - Without the optimistic update, the second press would read the stale - off-state (the Firestore push hasn't round-tripped yet) and send another - bare light.status=1 — a no-op on the wire that doesn't advance the color. + The library delivers the acknowledged turn-on through the data callback + before the Firestore push round-trips, so the second press reads the + light as on and pulses it instead of sending another bare on. """ mock_vistapool_client.fetch_pool_data.return_value = deepcopy(_LED_DATA) mock_config_entry.add_to_hass(hass) @@ -144,12 +131,21 @@ async def test_button_press_rapid_repeat_after_off( assert await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done() + on_data = mock_vistapool_client.subscribe_pool_resilient.call_args.args[1] + await hass.services.async_call( BUTTON_DOMAIN, SERVICE_PRESS, {ATTR_ENTITY_ID: _BUTTON}, blocking=True, ) + mock_vistapool_client.set_value.assert_awaited_once_with( + "ABCDEF1234567890", "light.status", 1 + ) + + on_data({"main": {"hasLED": 1, "version": 1}, "light": {"status": 1}}) + await hass.async_block_till_done() + await hass.services.async_call( BUTTON_DOMAIN, SERVICE_PRESS, @@ -157,32 +153,32 @@ async def test_button_press_rapid_repeat_after_off( blocking=True, ) - assert mock_vistapool_client.set_value.await_count == 3 - assert mock_vistapool_client.set_value.await_args_list[0].args == ( - "ABCDEF1234567890", - "light.status", - 1, - ) - assert mock_vistapool_client.set_value.await_args_list[1].args == ( - "ABCDEF1234567890", - "light.status", - 0, - ) - assert mock_vistapool_client.set_value.await_args_list[2].args == ( - "ABCDEF1234567890", - "light.status", - 1, + mock_vistapool_client.pulse.assert_awaited_once_with( + "ABCDEF1234567890", "light.status", 0, 1, _LED_PULSE_DELAY_SECONDS ) + assert mock_vistapool_client.set_value.await_count == 1 +@pytest.mark.parametrize( + ("light_status", "failing_method"), + [ + pytest.param(0, "set_value", id="turn_on_fails"), + pytest.param(1, "pulse", id="pulse_fails"), + ], +) async def test_button_press_raises_on_api_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_vistapool_client: AsyncMock, + light_status: int, + failing_method: str, ) -> None: """Test the button re-raises HomeAssistantError when the library fails.""" - mock_vistapool_client.fetch_pool_data.return_value = deepcopy(_LED_DATA) - mock_vistapool_client.set_value.side_effect = AquariteError("boom") + mock_vistapool_client.fetch_pool_data.return_value = { + "main": {"hasLED": 1, "version": 1}, + "light": {"status": light_status}, + } + getattr(mock_vistapool_client, failing_method).side_effect = AquariteError("boom") mock_config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(mock_config_entry.entry_id) diff --git a/tests/components/vistapool/test_init.py b/tests/components/vistapool/test_init.py index 24a64c5a689d..035f4dd7901e 100644 --- a/tests/components/vistapool/test_init.py +++ b/tests/components/vistapool/test_init.py @@ -298,26 +298,6 @@ async def test_setup_prunes_devices_removed_while_offline( ) -async def test_apply_optimistic_creates_missing_intermediate_dicts( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - mock_vistapool_client: AsyncMock, -) -> None: - """Test apply_optimistic walks through and creates missing intermediate dicts.""" - mock_vistapool_client.fetch_pool_data.return_value = {"existing": "scalar"} - 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() - - coordinator = next(iter(mock_config_entry.runtime_data.coordinators.values())) - coordinator.apply_optimistic("filtration.intel.temp", 27) - coordinator.apply_optimistic("existing.nested.key", 1) - - assert coordinator.data["filtration"]["intel"]["temp"] == 27 - assert coordinator.data["existing"] == {"nested": {"key": 1}} - - async def test_entities_unavailable_while_push_connection_is_down( hass: HomeAssistant, mock_config_entry: MockConfigEntry, @@ -326,7 +306,10 @@ async def test_entities_unavailable_while_push_connection_is_down( """Test entities go unavailable when the Firestore subscription drops. The integration has no polling interval, so without this the last - snapshot would stay on display as if it were still current. + snapshot would stay on display as if it were still current. Only the + library's health transition restores availability: data it delivers in + between (an acknowledged write, a reconcile fetch) is not proof the + stream is back. """ mock_config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(mock_config_entry.entry_id) @@ -343,10 +326,14 @@ async def test_entities_unavailable_while_push_connection_is_down( assert hass.states.get(_TEMPERATURE_ENTITY).state == STATE_UNAVAILABLE - # Only an incoming snapshot proves the connection is back. on_data({"main": {"temperature": 25}}) await hass.async_block_till_done() + assert hass.states.get(_TEMPERATURE_ENTITY).state == STATE_UNAVAILABLE + + on_health(True) + await hass.async_block_till_done() + assert hass.states.get(_TEMPERATURE_ENTITY).state == "25.0" @@ -357,7 +344,7 @@ async def test_entities_stay_unavailable_on_local_updates_during_outage( ) -> None: """Test updates that are not push snapshots do not fake availability. - Both an optimistic write and a manual refresh set the coordinator's + Both an acknowledged write and a manual refresh set the coordinator's success flag, so availability cannot ride on that flag alone. """ mock_vistapool_client.fetch_pool_data.return_value = {"light": {"status": 0}} @@ -366,20 +353,22 @@ async def test_entities_stay_unavailable_on_local_updates_during_outage( await hass.async_block_till_done() assert await async_setup_component(hass, "homeassistant", {}) - on_health = mock_vistapool_client.subscribe_pool_resilient.call_args.kwargs[ - "on_health" - ] + call = mock_vistapool_client.subscribe_pool_resilient.call_args + on_data = call.args[1] + on_health = call.kwargs["on_health"] on_health(False) await hass.async_block_till_done() assert hass.states.get(_LIGHT_ENTITY).state == STATE_UNAVAILABLE - # An optimistic write updates coordinator data while the push is down. + # The library delivers an acknowledged write through the data callback + # while the push is down. await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: _LIGHT_ENTITY}, blocking=True, ) + on_data({"light": {"status": 1}}) await hass.async_block_till_done() assert hass.states.get(_LIGHT_ENTITY).state == STATE_UNAVAILABLE diff --git a/tests/components/vistapool/test_light.py b/tests/components/vistapool/test_light.py index 72f14e895f99..5429ac14e1e3 100644 --- a/tests/components/vistapool/test_light.py +++ b/tests/components/vistapool/test_light.py @@ -98,43 +98,6 @@ async def test_light_set_value( ) -@pytest.mark.parametrize( - ("service", "initial_status", "initial_state", "expected_state"), - [ - pytest.param(SERVICE_TURN_ON, 0, STATE_OFF, STATE_ON, id="turn_on"), - pytest.param(SERVICE_TURN_OFF, 1, STATE_ON, STATE_OFF, id="turn_off"), - ], -) -async def test_light_optimistic_state( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - mock_vistapool_client: AsyncMock, - mock_pool_data: dict[str, Any], - service: str, - initial_status: int, - initial_state: str, - expected_state: str, -) -> None: - """Test the entity state reflects the just-written value before the Firestore push.""" - mock_pool_data["light"] = {"status": initial_status} - mock_vistapool_client.fetch_pool_data.return_value = mock_pool_data - 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("light.my_pool_light").state == initial_state - - await hass.services.async_call( - LIGHT_DOMAIN, - service, - {ATTR_ENTITY_ID: "light.my_pool_light"}, - blocking=True, - ) - - assert hass.states.get("light.my_pool_light").state == expected_state - - async def test_light_set_value_raises_on_api_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry, diff --git a/tests/components/vistapool/test_number.py b/tests/components/vistapool/test_number.py index 21be9b11b959..7ad6ae4f2fb8 100644 --- a/tests/components/vistapool/test_number.py +++ b/tests/components/vistapool/test_number.py @@ -270,7 +270,6 @@ async def test_number_set_value( mock_vistapool_client.set_value.assert_awaited_once_with( "ABCDEF1234567890", expected_path, expected_raw ) - assert hass.states.get(entity_id).state == str(float(user_value)) value_arg = mock_vistapool_client.set_value.await_args.args[2] assert isinstance(value_arg, int) diff --git a/tests/components/vistapool/test_select.py b/tests/components/vistapool/test_select.py index f379946e0b59..c24581d45344 100644 --- a/tests/components/vistapool/test_select.py +++ b/tests/components/vistapool/test_select.py @@ -14,17 +14,12 @@ from homeassistant.components.select import ( DOMAIN as SELECT_DOMAIN, SERVICE_SELECT_OPTION, ) -from homeassistant.const import ( - ATTR_ENTITY_ID, - EVENT_STATE_CHANGED, - STATE_UNKNOWN, - Platform, -) +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from tests.common import MockConfigEntry, async_capture_events, snapshot_platform +from tests.common import MockConfigEntry, snapshot_platform @pytest.fixture(autouse=True) @@ -420,88 +415,6 @@ async def test_select_reflects_choice_before_push( assert hass.states.get("select.my_pool_pump_speed").state == "high" -async def test_light_mode_reflects_choice_before_push( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - mock_vistapool_client: AsyncMock, -) -> None: - """Test the light mode select applies every field of the chosen option.""" - mock_vistapool_client.fetch_pool_data.return_value = deepcopy(_LIGHT_SCHEDULE_DATA) - 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("select.my_pool_light_mode").state == "auto" - - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - {ATTR_ENTITY_ID: "select.my_pool_light_mode", ATTR_OPTION: "on"}, - blocking=True, - ) - - # Reads back as on only if both light.mode and light.status were applied. - assert hass.states.get("select.my_pool_light_mode").state == "on" - - -async def test_light_schedule_frequency_reflects_choice_before_push( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - mock_vistapool_client: AsyncMock, -) -> None: - """Test the frequency select shows the chosen option immediately.""" - mock_vistapool_client.fetch_pool_data.return_value = deepcopy(_LIGHT_SCHEDULE_DATA) - 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() - - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.my_pool_light_schedule_frequency", - ATTR_OPTION: "weekly", - }, - blocking=True, - ) - - assert hass.states.get("select.my_pool_light_schedule_frequency").state == "weekly" - - -async def test_light_mode_never_publishes_partial_state( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - mock_vistapool_client: AsyncMock, -) -> None: - """Test leaving auto does not briefly read as another option. - - light.mode and light.status both feed current_option, so applying them - one at a time would publish an off state between the two writes. - """ - mock_vistapool_client.fetch_pool_data.return_value = deepcopy(_LIGHT_SCHEDULE_DATA) - 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() - - events = async_capture_events(hass, EVENT_STATE_CHANGED) - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - {ATTR_ENTITY_ID: "select.my_pool_light_mode", ATTR_OPTION: "on"}, - blocking=True, - ) - await hass.async_block_till_done() - - states = [ - event.data["new_state"].state - for event in events - if event.data["entity_id"] == "select.my_pool_light_mode" - ] - assert states == ["on"] - - async def test_light_mode_raises_on_api_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry, diff --git a/tests/components/vistapool/test_time.py b/tests/components/vistapool/test_time.py index 605f35e8cc2e..6cb631ec7c5b 100644 --- a/tests/components/vistapool/test_time.py +++ b/tests/components/vistapool/test_time.py @@ -150,7 +150,6 @@ async def test_time_set_value( mock_vistapool_client.set_value.assert_awaited_once_with( "ABCDEF1234567890", expected_path, expected_seconds ) - assert hass.states.get(entity_id).state == time_value async def test_time_set_value_raises_on_api_error(