diff --git a/homeassistant/components/hdfury/__init__.py b/homeassistant/components/hdfury/__init__.py index b3921d0e849..8eaf51a3aa0 100644 --- a/homeassistant/components/hdfury/__init__.py +++ b/homeassistant/components/hdfury/__init__.py @@ -8,6 +8,7 @@ from .coordinator import HDFuryConfigEntry, HDFuryCoordinator PLATFORMS = [ Platform.BUTTON, Platform.SELECT, + Platform.SWITCH, ] diff --git a/homeassistant/components/hdfury/icons.json b/homeassistant/components/hdfury/icons.json index d5e9fbfbd63..9088ee37eaa 100644 --- a/homeassistant/components/hdfury/icons.json +++ b/homeassistant/components/hdfury/icons.json @@ -15,6 +15,38 @@ "portseltx1": { "default": "mdi:hdmi-port" } + }, + "switch": { + "autosw": { + "default": "mdi:import" + }, + "htpcmode0": { + "default": "mdi:desktop-classic" + }, + "htpcmode1": { + "default": "mdi:desktop-classic" + }, + "htpcmode2": { + "default": "mdi:desktop-classic" + }, + "htpcmode3": { + "default": "mdi:desktop-classic" + }, + "iractive": { + "default": "mdi:remote" + }, + "mutetx0": { + "default": "mdi:volume-mute" + }, + "mutetx1": { + "default": "mdi:volume-mute" + }, + "oled": { + "default": "mdi:cellphone-information" + }, + "relay": { + "default": "mdi:electric-switch" + } } } } diff --git a/homeassistant/components/hdfury/strings.json b/homeassistant/components/hdfury/strings.json index dfc92b93230..0aa0d5241a2 100644 --- a/homeassistant/components/hdfury/strings.json +++ b/homeassistant/components/hdfury/strings.json @@ -56,6 +56,38 @@ "4": "Copy TX0" } } + }, + "switch": { + "autosw": { + "name": "Auto switch inputs" + }, + "htpcmode0": { + "name": "HTPC mode RX0" + }, + "htpcmode1": { + "name": "HTPC mode RX1" + }, + "htpcmode2": { + "name": "HTPC mode RX2" + }, + "htpcmode3": { + "name": "HTPC mode RX3" + }, + "iractive": { + "name": "Infrared" + }, + "mutetx0": { + "name": "Mute audio TX0" + }, + "mutetx1": { + "name": "Mute audio TX1" + }, + "oled": { + "name": "OLED display" + }, + "relay": { + "name": "Relay" + } } }, "exceptions": { diff --git a/homeassistant/components/hdfury/switch.py b/homeassistant/components/hdfury/switch.py new file mode 100644 index 00000000000..717aa345f02 --- /dev/null +++ b/homeassistant/components/hdfury/switch.py @@ -0,0 +1,142 @@ +"""Switch platform for HDFury Integration.""" + +from collections.abc import Awaitable, Callable +from dataclasses import dataclass +from typing import Any + +from hdfury import HDFuryAPI, HDFuryError + +from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from .const import DOMAIN +from .coordinator import HDFuryConfigEntry +from .entity import HDFuryEntity + + +@dataclass(kw_only=True, frozen=True) +class HDFurySwitchEntityDescription(SwitchEntityDescription): + """Description for HDFury switch entities.""" + + set_value_fn: Callable[[HDFuryAPI, str], Awaitable[None]] + + +SWITCHES: tuple[HDFurySwitchEntityDescription, ...] = ( + HDFurySwitchEntityDescription( + key="autosw", + translation_key="autosw", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_auto_switch_inputs(value), + ), + HDFurySwitchEntityDescription( + key="htpcmode0", + translation_key="htpcmode0", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_htpc_mode_rx0(value), + ), + HDFurySwitchEntityDescription( + key="htpcmode1", + translation_key="htpcmode1", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_htpc_mode_rx1(value), + ), + HDFurySwitchEntityDescription( + key="htpcmode2", + translation_key="htpcmode2", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_htpc_mode_rx2(value), + ), + HDFurySwitchEntityDescription( + key="htpcmode3", + translation_key="htpcmode3", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_htpc_mode_rx3(value), + ), + HDFurySwitchEntityDescription( + key="mutetx0", + translation_key="mutetx0", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_mute_tx0_audio(value), + ), + HDFurySwitchEntityDescription( + key="mutetx1", + translation_key="mutetx1", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_mute_tx1_audio(value), + ), + HDFurySwitchEntityDescription( + key="oled", + translation_key="oled", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_oled(value), + ), + HDFurySwitchEntityDescription( + key="iractive", + translation_key="iractive", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_ir_active(value), + ), + HDFurySwitchEntityDescription( + key="relay", + translation_key="relay", + entity_category=EntityCategory.CONFIG, + set_value_fn=lambda client, value: client.set_relay(value), + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: HDFuryConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up switches using the platform schema.""" + + coordinator = entry.runtime_data + + async_add_entities( + HDFurySwitch(coordinator, description) + for description in SWITCHES + if description.key in coordinator.data.config + ) + + +class HDFurySwitch(HDFuryEntity, SwitchEntity): + """Base HDFury Switch Class.""" + + entity_description: HDFurySwitchEntityDescription + + @property + def is_on(self) -> bool: + """Set Switch State.""" + + return self.coordinator.data.config.get(self.entity_description.key) == "1" + + async def async_turn_on(self, **kwargs: Any) -> None: + """Handle Switch On Event.""" + + try: + await self.entity_description.set_value_fn(self.coordinator.client, "on") + except HDFuryError as error: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="communication_error", + ) from error + + await self.coordinator.async_request_refresh() + + async def async_turn_off(self, **kwargs: Any) -> None: + """Handle Switch Off Event.""" + + try: + await self.entity_description.set_value_fn(self.coordinator.client, "off") + except HDFuryError as error: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="communication_error", + ) from error + + await self.coordinator.async_request_refresh() diff --git a/tests/components/hdfury/conftest.py b/tests/components/hdfury/conftest.py index 8478f7abe82..7f7b760856d 100644 --- a/tests/components/hdfury/conftest.py +++ b/tests/components/hdfury/conftest.py @@ -72,7 +72,17 @@ def mock_hdfury_client() -> Generator[AsyncMock]: ) coord_client.get_config = AsyncMock( return_value={ + "autosw": "1", + "iractive": "1", + "htpcmode0": "0", + "htpcmode1": "0", + "htpcmode2": "0", + "htpcmode3": "0", + "mutetx0": "1", + "mutetx1": "1", + "relay": "0", "macaddr": "c7:1c:df:9d:f6:40", + "oled": "1", } ) diff --git a/tests/components/hdfury/snapshots/test_switch.ambr b/tests/components/hdfury/snapshots/test_switch.ambr new file mode 100644 index 00000000000..f8666f7c3ad --- /dev/null +++ b/tests/components/hdfury/snapshots/test_switch.ambr @@ -0,0 +1,481 @@ +# serializer version: 1 +# name: test_switch_entities[switch.hdfury_vrroom_02_auto_switch_inputs-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_auto_switch_inputs', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Auto switch inputs', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'autosw', + 'unique_id': '000123456789_autosw', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_auto_switch_inputs-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 Auto switch inputs', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_auto_switch_inputs', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_htpc_mode_rx0-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_htpc_mode_rx0', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HTPC mode RX0', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'htpcmode0', + 'unique_id': '000123456789_htpcmode0', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_htpc_mode_rx0-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 HTPC mode RX0', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_htpc_mode_rx0', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_htpc_mode_rx1-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_htpc_mode_rx1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HTPC mode RX1', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'htpcmode1', + 'unique_id': '000123456789_htpcmode1', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_htpc_mode_rx1-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 HTPC mode RX1', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_htpc_mode_rx1', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_htpc_mode_rx2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_htpc_mode_rx2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HTPC mode RX2', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'htpcmode2', + 'unique_id': '000123456789_htpcmode2', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_htpc_mode_rx2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 HTPC mode RX2', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_htpc_mode_rx2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_htpc_mode_rx3-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_htpc_mode_rx3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'HTPC mode RX3', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'htpcmode3', + 'unique_id': '000123456789_htpcmode3', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_htpc_mode_rx3-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 HTPC mode RX3', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_htpc_mode_rx3', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_infrared-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_infrared', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Infrared', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'iractive', + 'unique_id': '000123456789_iractive', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_infrared-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 Infrared', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_infrared', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_mute_audio_tx0-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_mute_audio_tx0', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Mute audio TX0', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'mutetx0', + 'unique_id': '000123456789_mutetx0', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_mute_audio_tx0-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 Mute audio TX0', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_mute_audio_tx0', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_mute_audio_tx1-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_mute_audio_tx1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Mute audio TX1', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'mutetx1', + 'unique_id': '000123456789_mutetx1', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_mute_audio_tx1-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 Mute audio TX1', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_mute_audio_tx1', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_oled_display-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_oled_display', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'OLED display', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'oled', + 'unique_id': '000123456789_oled', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_oled_display-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 OLED display', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_oled_display', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_relay-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.hdfury_vrroom_02_relay', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Relay', + 'platform': 'hdfury', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay', + 'unique_id': '000123456789_relay', + 'unit_of_measurement': None, + }) +# --- +# name: test_switch_entities[switch.hdfury_vrroom_02_relay-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'HDFury VRROOM-02 Relay', + }), + 'context': , + 'entity_id': 'switch.hdfury_vrroom_02_relay', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- diff --git a/tests/components/hdfury/test_switch.py b/tests/components/hdfury/test_switch.py new file mode 100644 index 00000000000..eaf9aa42ead --- /dev/null +++ b/tests/components/hdfury/test_switch.py @@ -0,0 +1,99 @@ +"""Tests for the HDFury switch platform.""" + +from unittest.mock import AsyncMock + +from hdfury import HDFuryError +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +import homeassistant.helpers.entity_registry as er + +from . import setup_integration + +from tests.common import MockConfigEntry, snapshot_platform + + +async def test_switch_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, +) -> None: + """Test HDFury switch entities.""" + + await setup_integration(hass, mock_config_entry, [Platform.SWITCH]) + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +@pytest.mark.parametrize( + ("entity_id", "method", "service"), + [ + ( + "switch.hdfury_vrroom_02_auto_switch_inputs", + "set_auto_switch_inputs", + "turn_on", + ), + ( + "switch.hdfury_vrroom_02_auto_switch_inputs", + "set_auto_switch_inputs", + "turn_off", + ), + ("switch.hdfury_vrroom_02_oled_display", "set_oled", "turn_on"), + ("switch.hdfury_vrroom_02_oled_display", "set_oled", "turn_off"), + ], +) +async def test_switch_turn_on_off( + hass: HomeAssistant, + mock_hdfury_client: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_id: str, + method: str, + service: str, +) -> None: + """Test turning device switches on and off.""" + + await setup_integration(hass, mock_config_entry, [Platform.SWITCH]) + + await hass.services.async_call( + "switch", + service, + {"entity_id": entity_id}, + blocking=True, + ) + + getattr(mock_hdfury_client, method).assert_awaited_once() + + +@pytest.mark.parametrize( + ("service", "method"), + [ + ("turn_on", "set_auto_switch_inputs"), + ("turn_off", "set_auto_switch_inputs"), + ], +) +async def test_switch_turn_error( + hass: HomeAssistant, + mock_hdfury_client: AsyncMock, + mock_config_entry: MockConfigEntry, + service: str, + method: str, +) -> None: + """Test switch turn on/off raises HomeAssistantError on API failure.""" + + getattr(mock_hdfury_client, method).side_effect = HDFuryError() + + await setup_integration(hass, mock_config_entry, [Platform.SWITCH]) + + with pytest.raises( + HomeAssistantError, + match="An error occurred while communicating with HDFury device", + ): + await hass.services.async_call( + "switch", + service, + {"entity_id": "switch.hdfury_vrroom_02_auto_switch_inputs"}, + blocking=True, + )