From d02248c2192a2448ba00c207601d923e8c33ab11 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 08:56:00 +0000 Subject: [PATCH 1/9] Add button platform to Vistapool Exposes a single LED-pulse button gated on main.hasLED. Pressing the button advances the physical LED fixture's colour: - If light.status is currently 1, the button writes light.status=0, waits a moment, then writes light.status=1. The brief power-cycle is what advances the LED to its next colour. - If the light is off, the button just writes light.status=1. Mirrors the "Next" button under LED Color in the Vistapool app's Illumination screen. AquariteError becomes a translated HomeAssistantError using the existing set_failed translation key. --- .../components/vistapool/__init__.py | 2 +- homeassistant/components/vistapool/button.py | 69 ++++++++ .../components/vistapool/quality_scale.yaml | 8 +- .../components/vistapool/strings.json | 8 + .../vistapool/snapshots/test_button.ambr | 51 ++++++ tests/components/vistapool/test_button.py | 151 ++++++++++++++++++ 6 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/vistapool/button.py create mode 100644 tests/components/vistapool/snapshots/test_button.ambr create mode 100644 tests/components/vistapool/test_button.py diff --git a/homeassistant/components/vistapool/__init__.py b/homeassistant/components/vistapool/__init__.py index 51e0eb585330da..50230ab7686e86 100644 --- a/homeassistant/components/vistapool/__init__.py +++ b/homeassistant/components/vistapool/__init__.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) -PLATFORMS: list[Platform] = [Platform.SENSOR] +PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.SENSOR] @dataclass diff --git a/homeassistant/components/vistapool/button.py b/homeassistant/components/vistapool/button.py new file mode 100644 index 00000000000000..24f29645570224 --- /dev/null +++ b/homeassistant/components/vistapool/button.py @@ -0,0 +1,69 @@ +"""Vistapool Button entities.""" + +import asyncio + +from aioaquarite import AquariteError + +from homeassistant.components.button import ButtonEntity +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from . import VistapoolConfigEntry +from .const import DOMAIN +from .coordinator import VistapoolDataUpdateCoordinator +from .entity import VistapoolEntity + +PARALLEL_UPDATES = 1 + +_HASLED_PATH = "main.hasLED" +_LIGHT_STATUS_PATH = "light.status" +_LED_PULSE_DELAY_SECONDS = 1.0 + + +async def async_setup_entry( + hass: HomeAssistant, + entry: VistapoolConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up Vistapool buttons for every pool that has an LED fixture.""" + async_add_entities( + VistapoolLEDPulseButton(coordinator) + for coordinator in entry.runtime_data.coordinators.values() + if coordinator.get_value(_HASLED_PATH) + ) + + +class VistapoolLEDPulseButton(VistapoolEntity, ButtonEntity): + """Power-cycle the pool light to advance the LED fixture's colour. + + 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 colour on power-on. If the light is off, just turns it on. + """ + + _attr_translation_key = "led_pulse" + + def __init__(self, coordinator: VistapoolDataUpdateCoordinator) -> None: + """Initialize the LED pulse button.""" + super().__init__(coordinator) + self._attr_unique_id = self.build_unique_id("led_pulse") + + async def async_press(self) -> None: + """Send a colour-advance pulse to the pool LED fixture.""" + try: + if self.coordinator.get_value(_LIGHT_STATUS_PATH) in (True, "1", 1): + await self.coordinator.api.set_value( + self.coordinator.pool_id, _LIGHT_STATUS_PATH, 0 + ) + 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 diff --git a/homeassistant/components/vistapool/quality_scale.yaml b/homeassistant/components/vistapool/quality_scale.yaml index 72ca9758594650..4ffcc19d98ae7f 100644 --- a/homeassistant/components/vistapool/quality_scale.yaml +++ b/homeassistant/components/vistapool/quality_scale.yaml @@ -2,7 +2,7 @@ rules: # Bronze action-setup: status: exempt - comment: No service actions in initial sensor-only platform + comment: No integration-specific service actions; entities use platform-standard actions only appropriate-polling: done brands: done common-modules: done @@ -11,7 +11,7 @@ rules: dependency-transparency: done docs-actions: status: exempt - comment: No service actions in initial sensor-only platform + comment: No integration-specific service actions to document docs-high-level-description: done docs-installation-instructions: done docs-removal-instructions: done @@ -24,9 +24,7 @@ rules: unique-config-entry: done # Silver - action-exceptions: - status: exempt - comment: No user actions (sensor-only platform) + action-exceptions: done config-entry-unloading: done docs-configuration-parameters: status: exempt diff --git a/homeassistant/components/vistapool/strings.json b/homeassistant/components/vistapool/strings.json index 1ef44f92ec3e5c..2836c0fca4060f 100644 --- a/homeassistant/components/vistapool/strings.json +++ b/homeassistant/components/vistapool/strings.json @@ -25,6 +25,11 @@ } }, "entity": { + "button": { + "led_pulse": { + "name": "LED next colour" + } + }, "sensor": { "chlorine": { "name": "Chlorine" @@ -59,6 +64,9 @@ "no_pools": { "message": "No pools were found on this account." }, + "set_failed": { + "message": "Failed to set {entity}." + }, "update_failed": { "message": "Error fetching data from Vistapool." } diff --git a/tests/components/vistapool/snapshots/test_button.ambr b/tests/components/vistapool/snapshots/test_button.ambr new file mode 100644 index 00000000000000..02d9507fb63119 --- /dev/null +++ b/tests/components/vistapool/snapshots/test_button.ambr @@ -0,0 +1,51 @@ +# serializer version: 1 +# name: test_all_entities[button.my_pool_led_next_colour-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': None, + 'entity_id': 'button.my_pool_led_next_colour', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'LED next colour', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'LED next colour', + 'platform': 'vistapool', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'led_pulse', + 'unique_id': 'ABCDEF1234567890-led_pulse', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[button.my_pool_led_next_colour-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'My Pool LED next colour', + }), + 'context': , + 'entity_id': 'button.my_pool_led_next_colour', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/vistapool/test_button.py b/tests/components/vistapool/test_button.py new file mode 100644 index 00000000000000..b8777965b3e716 --- /dev/null +++ b/tests/components/vistapool/test_button.py @@ -0,0 +1,151 @@ +"""Tests for the Vistapool button platform.""" + +from collections.abc import Generator +from typing import Any +from unittest.mock import AsyncMock, patch + +from aioaquarite import AquariteError +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.const import ATTR_ENTITY_ID, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er + +from tests.common import MockConfigEntry, snapshot_platform + +_BUTTON = "button.my_pool_led_next_colour" +_LED_DATA = {"main": {"hasLED": 1, "version": 1}, "light": {"status": 0}} + + +@pytest.fixture(autouse=True) +def _only_button_platform() -> Generator[None]: + """Restrict integration setup to the button platform for these tests.""" + with patch("homeassistant.components.vistapool.PLATFORMS", [Platform.BUTTON]): + 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.asyncio.sleep", + new=AsyncMock(), + ): + yield + + +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, + mock_vistapool_client: AsyncMock, +) -> None: + """Test the LED-pulse button when hasLED is set.""" + mock_vistapool_client.fetch_pool_data.return_value = _LED_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 snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +async def test_button_not_created_without_led( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_vistapool_client: AsyncMock, + mock_pool_data: dict[str, Any], +) -> None: + """Test the LED-pulse button is not created when hasLED is 0.""" + 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(_BUTTON) is None + + +async def test_button_press_when_light_off( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_vistapool_client: AsyncMock, +) -> None: + """Test pressing the button when the light is off just turns it on.""" + mock_vistapool_client.fetch_pool_data.return_value = _LED_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( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: _BUTTON}, + blocking=True, + ) + + mock_vistapool_client.set_value.assert_awaited_once_with( + "ABCDEF1234567890", "light.status", 1 + ) + + +async def test_button_press_when_light_on( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_vistapool_client: AsyncMock, +) -> None: + """Test pressing the button when the light is on power-cycles it.""" + mock_vistapool_client.fetch_pool_data.return_value = { + "main": {"hasLED": 1, "version": 1}, + "light": {"status": 1}, + } + 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( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: _BUTTON}, + 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, + ) + + +async def test_button_press_raises_on_api_error( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_vistapool_client: AsyncMock, +) -> None: + """Test the button re-raises HomeAssistantError when the library fails.""" + mock_vistapool_client.fetch_pool_data.return_value = _LED_DATA + mock_vistapool_client.set_value.side_effect = AquariteError("boom") + 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() + + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: _BUTTON}, + blocking=True, + ) From a1f7c143bad6fd6c26c9532270fcbb4c1fb35146 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 30 May 2026 15:48:55 +0000 Subject: [PATCH 2/9] Vistapool: assert translation_key on the button press error Confirms the HomeAssistantError raised by the press handler is the expected set_failed one rather than an unrelated HomeAssistantError. --- tests/components/vistapool/test_button.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/components/vistapool/test_button.py b/tests/components/vistapool/test_button.py index b8777965b3e716..039a7422c604a2 100644 --- a/tests/components/vistapool/test_button.py +++ b/tests/components/vistapool/test_button.py @@ -142,10 +142,11 @@ async def test_button_press_raises_on_api_error( assert await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done() - with pytest.raises(HomeAssistantError): + with pytest.raises(HomeAssistantError) as excinfo: await hass.services.async_call( BUTTON_DOMAIN, SERVICE_PRESS, {ATTR_ENTITY_ID: _BUTTON}, blocking=True, ) + assert excinfo.value.translation_key == "set_failed" From f0fbd4d913969cb88f9f3cd4dd199c971bac1cb1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 30 May 2026 16:07:15 +0000 Subject: [PATCH 3/9] Vistapool: drop redundant 1 from button on-value tuple (True, "1", 1) is equivalent to (True, "1") because 1 == True in Python, so the explicit 1 is matched by True. Aligns with the (True, "1") form used in binary_sensor.py and light.py. --- homeassistant/components/vistapool/button.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/vistapool/button.py b/homeassistant/components/vistapool/button.py index 24f29645570224..02f31e19412897 100644 --- a/homeassistant/components/vistapool/button.py +++ b/homeassistant/components/vistapool/button.py @@ -53,7 +53,7 @@ def __init__(self, coordinator: VistapoolDataUpdateCoordinator) -> None: async def async_press(self) -> None: """Send a colour-advance pulse to the pool LED fixture.""" try: - if self.coordinator.get_value(_LIGHT_STATUS_PATH) in (True, "1", 1): + if self.coordinator.get_value(_LIGHT_STATUS_PATH) in (True, "1"): await self.coordinator.api.set_value( self.coordinator.pool_id, _LIGHT_STATUS_PATH, 0 ) From 956ab0540378bc507a09711ba49d7c3c649284a1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 30 May 2026 17:56:59 +0000 Subject: [PATCH 4/9] Vistapool: patch the LED pulse delay constant to 0 in tests Patches _LED_PULSE_DELAY_SECONDS to 0 instead of mocking asyncio.sleep, so the real sleep(0) call still runs (yields to the loop) and only the wall-clock delay is removed. --- tests/components/vistapool/test_button.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/components/vistapool/test_button.py b/tests/components/vistapool/test_button.py index 039a7422c604a2..74c20a9fa673f1 100644 --- a/tests/components/vistapool/test_button.py +++ b/tests/components/vistapool/test_button.py @@ -31,8 +31,7 @@ def _only_button_platform() -> Generator[None]: def _skip_pulse_delay() -> Generator[None]: """Skip the LED pulse delay so tests don't actually sleep.""" with patch( - "homeassistant.components.vistapool.button.asyncio.sleep", - new=AsyncMock(), + "homeassistant.components.vistapool.button._LED_PULSE_DELAY_SECONDS", 0 ): yield From 1009e78f1cf0f70be826d8bb86693136d2e9b0ad Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 30 May 2026 18:00:56 +0000 Subject: [PATCH 5/9] Vistapool: use American "color" instead of "colour" for LED button Home Assistant standardises on American English. Renames the friendly name "LED next colour" -> "LED next color"; the auto-derived entity_id cascades to button.my_pool_led_next_color. Also updates the docstring/comment spellings in button.py. --- homeassistant/components/vistapool/button.py | 6 +++--- homeassistant/components/vistapool/strings.json | 2 +- .../vistapool/snapshots/test_button.ambr | 14 +++++++------- tests/components/vistapool/test_button.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/vistapool/button.py b/homeassistant/components/vistapool/button.py index 02f31e19412897..c60ae187751153 100644 --- a/homeassistant/components/vistapool/button.py +++ b/homeassistant/components/vistapool/button.py @@ -35,12 +35,12 @@ async def async_setup_entry( class VistapoolLEDPulseButton(VistapoolEntity, ButtonEntity): - """Power-cycle the pool light to advance the LED fixture's colour. + """Power-cycle the pool light to advance the LED fixture's color. 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 colour 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. """ _attr_translation_key = "led_pulse" @@ -51,7 +51,7 @@ def __init__(self, coordinator: VistapoolDataUpdateCoordinator) -> None: self._attr_unique_id = self.build_unique_id("led_pulse") async def async_press(self) -> None: - """Send a colour-advance pulse to the pool LED fixture.""" + """Send a color-advance pulse to the pool LED fixture.""" try: if self.coordinator.get_value(_LIGHT_STATUS_PATH) in (True, "1"): await self.coordinator.api.set_value( diff --git a/homeassistant/components/vistapool/strings.json b/homeassistant/components/vistapool/strings.json index 2836c0fca4060f..5ceceab013be61 100644 --- a/homeassistant/components/vistapool/strings.json +++ b/homeassistant/components/vistapool/strings.json @@ -27,7 +27,7 @@ "entity": { "button": { "led_pulse": { - "name": "LED next colour" + "name": "LED next color" } }, "sensor": { diff --git a/tests/components/vistapool/snapshots/test_button.ambr b/tests/components/vistapool/snapshots/test_button.ambr index 02d9507fb63119..a79bb0822310d7 100644 --- a/tests/components/vistapool/snapshots/test_button.ambr +++ b/tests/components/vistapool/snapshots/test_button.ambr @@ -1,5 +1,5 @@ # serializer version: 1 -# name: test_all_entities[button.my_pool_led_next_colour-entry] +# name: test_all_entities[button.my_pool_led_next_color-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -13,7 +13,7 @@ 'disabled_by': None, 'domain': 'button', 'entity_category': None, - 'entity_id': 'button.my_pool_led_next_colour', + 'entity_id': 'button.my_pool_led_next_color', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -21,12 +21,12 @@ 'labels': set({ }), 'name': None, - 'object_id_base': 'LED next colour', + 'object_id_base': 'LED next color', 'options': dict({ }), 'original_device_class': None, 'original_icon': None, - 'original_name': 'LED next colour', + 'original_name': 'LED next color', 'platform': 'vistapool', 'previous_unique_id': None, 'suggested_object_id': None, @@ -36,13 +36,13 @@ 'unit_of_measurement': None, }) # --- -# name: test_all_entities[button.my_pool_led_next_colour-state] +# name: test_all_entities[button.my_pool_led_next_color-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'My Pool LED next colour', + 'friendly_name': 'My Pool LED next color', }), 'context': , - 'entity_id': 'button.my_pool_led_next_colour', + 'entity_id': 'button.my_pool_led_next_color', 'last_changed': , 'last_reported': , 'last_updated': , diff --git a/tests/components/vistapool/test_button.py b/tests/components/vistapool/test_button.py index 74c20a9fa673f1..83e8587bf106d6 100644 --- a/tests/components/vistapool/test_button.py +++ b/tests/components/vistapool/test_button.py @@ -16,7 +16,7 @@ from tests.common import MockConfigEntry, snapshot_platform -_BUTTON = "button.my_pool_led_next_colour" +_BUTTON = "button.my_pool_led_next_color" _LED_DATA = {"main": {"hasLED": 1, "version": 1}, "light": {"status": 0}} From b6b425811a652ffab69a26b18341ff07b3400d10 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 1 Jun 2026 12:12:18 +0000 Subject: [PATCH 6/9] Vistapool: optimistically update light.status after LED button press A rapid second press inside the Firestore push round-trip window otherwise reads the stale off-state and just sends another light.status=1, a no-op on the wire that doesn't advance the color. Writing the new value into coordinator.data and notifying listeners makes the next press immediately see the just-written value. --- homeassistant/components/vistapool/button.py | 4 ++ tests/components/vistapool/test_button.py | 48 ++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/homeassistant/components/vistapool/button.py b/homeassistant/components/vistapool/button.py index c60ae187751153..2432dc505ad044 100644 --- a/homeassistant/components/vistapool/button.py +++ b/homeassistant/components/vistapool/button.py @@ -67,3 +67,7 @@ async def async_press(self) -> None: translation_key="set_failed", translation_placeholders={"entity": self.entity_id}, ) from err + # Optimistically reflect the just-written value so a rapid second press + # doesn't read the stale off-state before the Firestore push round-trips. + self.coordinator.data.setdefault("light", {})["status"] = 1 + self.coordinator.async_set_updated_data(self.coordinator.data) diff --git a/tests/components/vistapool/test_button.py b/tests/components/vistapool/test_button.py index 83e8587bf106d6..d5024d5bb1dc9c 100644 --- a/tests/components/vistapool/test_button.py +++ b/tests/components/vistapool/test_button.py @@ -128,6 +128,54 @@ async def test_button_press_when_light_on( ) +async def test_button_press_rapid_repeat_after_off( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_vistapool_client: AsyncMock, +) -> None: + """Test a second press lands the off/on 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. + """ + mock_vistapool_client.fetch_pool_data.return_value = _LED_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( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: _BUTTON}, + blocking=True, + ) + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: _BUTTON}, + 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, + ) + + async def test_button_press_raises_on_api_error( hass: HomeAssistant, mock_config_entry: MockConfigEntry, From ab5148c861b85ca2648281033e33e4f2e9a9cac1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 1 Jun 2026 12:47:07 +0000 Subject: [PATCH 7/9] Vistapool: bump aioaquarite to 0.5.1 0.5.1 wraps aiohttp.ClientError and asyncio.TimeoutError from both the REST send_command path and the upstream auth client refresh in ConnectionError (an AquariteError subclass), so transport failures surface as the translated set_failed HomeAssistantError via the existing except AquariteError clause. --- homeassistant/components/vistapool/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/vistapool/manifest.json b/homeassistant/components/vistapool/manifest.json index 1dc6f481784c8a..4f2c74d962a2be 100644 --- a/homeassistant/components/vistapool/manifest.json +++ b/homeassistant/components/vistapool/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_push", "loggers": ["aioaquarite"], "quality_scale": "bronze", - "requirements": ["aioaquarite==0.4.0"] + "requirements": ["aioaquarite==0.5.1"] } diff --git a/requirements_all.txt b/requirements_all.txt index ac80d78b42b3c5..0e75d33b567523 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.4.0 +aioaquarite==0.5.1 # homeassistant.components.aseko_pool_live aioaseko==1.0.0 From 23672744ff650e6de12c0f735edc991fdf5f9e77 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 1 Jun 2026 15:13:03 +0000 Subject: [PATCH 8/9] Revert "Vistapool: bump aioaquarite to 0.5.1" This reverts commit ab5148c861b85ca2648281033e33e4f2e9a9cac1. --- homeassistant/components/vistapool/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/vistapool/manifest.json b/homeassistant/components/vistapool/manifest.json index 4f2c74d962a2be..1dc6f481784c8a 100644 --- a/homeassistant/components/vistapool/manifest.json +++ b/homeassistant/components/vistapool/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_push", "loggers": ["aioaquarite"], "quality_scale": "bronze", - "requirements": ["aioaquarite==0.5.1"] + "requirements": ["aioaquarite==0.4.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 0e75d33b567523..ac80d78b42b3c5 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.5.1 +aioaquarite==0.4.0 # homeassistant.components.aseko_pool_live aioaseko==1.0.0 From 2c69814fb96fc8af86a9704a7fac36db855a66f7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 1 Jun 2026 20:57:08 +0000 Subject: [PATCH 9/9] Vistapool: collapse patch() call to one line per ruff format --- tests/components/vistapool/test_button.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/components/vistapool/test_button.py b/tests/components/vistapool/test_button.py index d5024d5bb1dc9c..30a0e1122dd6c2 100644 --- a/tests/components/vistapool/test_button.py +++ b/tests/components/vistapool/test_button.py @@ -30,9 +30,7 @@ def _only_button_platform() -> Generator[None]: @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 - ): + with patch("homeassistant.components.vistapool.button._LED_PULSE_DELAY_SECONDS", 0): yield