diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index eda61e44d84..09ee133589b 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -6,38 +6,22 @@ from dataclasses import dataclass from typing import Any, cast from aioshelly.block_device import Block -from aioshelly.const import ( - MODEL_2, - MODEL_25, - MODEL_GAS, - MODEL_WALL_DISPLAY, - RPC_GENERATIONS, -) +from aioshelly.const import MODEL_2, MODEL_25, MODEL_WALL_DISPLAY, RPC_GENERATIONS -from homeassistant.components.automation import automations_with_entity -from homeassistant.components.script import scripts_with_entity -from homeassistant.components.switch import ( - DOMAIN as SWITCH_DOMAIN, - SwitchEntity, - SwitchEntityDescription, -) -from homeassistant.components.valve import DOMAIN as VALVE_DOMAIN +from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.const import STATE_ON, EntityCategory from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_registry import RegistryEntry -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.restore_state import RestoreEntity -from .const import CONF_SLEEP_PERIOD, DOMAIN, GAS_VALVE_OPEN_STATES, MOTION_MODELS +from .const import CONF_SLEEP_PERIOD, MOTION_MODELS from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator from .entity import ( BlockEntityDescription, - ShellyBlockAttributeEntity, ShellyBlockEntity, ShellyRpcEntity, ShellySleepingBlockAttributeEntity, - async_setup_block_attribute_entities, async_setup_entry_attribute_entities, ) from .utils import ( @@ -56,15 +40,6 @@ class BlockSwitchDescription(BlockEntityDescription, SwitchEntityDescription): """Class to describe a BLOCK switch.""" -# This entity description is deprecated and will be removed in Home Assistant 2024.7.0. -GAS_VALVE_SWITCH = BlockSwitchDescription( - key="valve|valve", - name="Valve", - available=lambda block: block.valve not in ("failure", "checking"), - removal_condition=lambda _, block: block.valve in ("not_connected", "unknown"), - entity_registry_enabled_default=False, -) - MOTION_SWITCH = BlockSwitchDescription( key="sensor|motionActive", name="Motion detection", @@ -94,17 +69,6 @@ def async_setup_block_entry( coordinator = config_entry.runtime_data.block assert coordinator - # Add Shelly Gas Valve as a switch - if coordinator.model == MODEL_GAS: - async_setup_block_attribute_entities( - hass, - async_add_entities, - coordinator, - {("valve", "valve"): GAS_VALVE_SWITCH}, - BlockValveSwitch, - ) - return - # Add Shelly Motion as a switch if coordinator.model in MOTION_MODELS: async_setup_entry_attribute_entities( @@ -238,99 +202,6 @@ class BlockSleepingMotionSwitch( self.last_state = last_state -class BlockValveSwitch(ShellyBlockAttributeEntity, SwitchEntity): - """Entity that controls a Gas Valve on Block based Shelly devices. - - This class is deprecated and will be removed in Home Assistant 2024.7.0. - """ - - entity_description: BlockSwitchDescription - _attr_translation_key = "valve_switch" - - def __init__( - self, - coordinator: ShellyBlockCoordinator, - block: Block, - attribute: str, - description: BlockSwitchDescription, - ) -> None: - """Initialize valve.""" - super().__init__(coordinator, block, attribute, description) - self.control_result: dict[str, Any] | None = None - - @property - def is_on(self) -> bool: - """If valve is open.""" - if self.control_result: - return self.control_result["state"] in GAS_VALVE_OPEN_STATES - - return self.attribute_value in GAS_VALVE_OPEN_STATES - - async def async_turn_on(self, **kwargs: Any) -> None: - """Open valve.""" - async_create_issue( - self.hass, - DOMAIN, - "deprecated_valve_switch", - breaks_in_ha_version="2024.7.0", - is_fixable=True, - severity=IssueSeverity.WARNING, - translation_key="deprecated_valve_switch", - translation_placeholders={ - "entity": f"{VALVE_DOMAIN}.{cast(str, self.name).lower().replace(' ', '_')}", - "service": f"{VALVE_DOMAIN}.open_valve", - }, - ) - self.control_result = await self.set_state(go="open") - self.async_write_ha_state() - - async def async_turn_off(self, **kwargs: Any) -> None: - """Close valve.""" - async_create_issue( - self.hass, - DOMAIN, - "deprecated_valve_switch", - breaks_in_ha_version="2024.7.0", - is_fixable=True, - severity=IssueSeverity.WARNING, - translation_key="deprecated_valve_switche", - translation_placeholders={ - "entity": f"{VALVE_DOMAIN}.{cast(str, self.name).lower().replace(' ', '_')}", - "service": f"{VALVE_DOMAIN}.close_valve", - }, - ) - self.control_result = await self.set_state(go="close") - self.async_write_ha_state() - - async def async_added_to_hass(self) -> None: - """Set up a listener when this entity is added to HA.""" - await super().async_added_to_hass() - - entity_automations = automations_with_entity(self.hass, self.entity_id) - entity_scripts = scripts_with_entity(self.hass, self.entity_id) - for item in entity_automations + entity_scripts: - async_create_issue( - self.hass, - DOMAIN, - f"deprecated_valve_{self.entity_id}_{item}", - breaks_in_ha_version="2024.7.0", - is_fixable=True, - severity=IssueSeverity.WARNING, - translation_key="deprecated_valve_switch_entity", - translation_placeholders={ - "entity": f"{SWITCH_DOMAIN}.{cast(str, self.name).lower().replace(' ', '_')}", - "info": item, - }, - ) - - @callback - def _update_callback(self) -> None: - """When device updates, clear control result that overrides state.""" - self.control_result = None - - super()._update_callback() - - class BlockRelaySwitch(ShellyBlockEntity, SwitchEntity): """Entity that controls a relay on Block based Shelly devices.""" diff --git a/homeassistant/components/shelly/valve.py b/homeassistant/components/shelly/valve.py index 83c1f577439..ea6feaabe69 100644 --- a/homeassistant/components/shelly/valve.py +++ b/homeassistant/components/shelly/valve.py @@ -23,7 +23,7 @@ from .entity import ( ShellyBlockAttributeEntity, async_setup_block_attribute_entities, ) -from .utils import get_device_entry_gen +from .utils import async_remove_shelly_entity, get_device_entry_gen @dataclass(kw_only=True, frozen=True) @@ -67,6 +67,9 @@ def async_setup_block_entry( {("valve", "valve"): GAS_VALVE}, BlockShellyValve, ) + # Remove deprecated switch entity for gas valve + unique_id = f"{coordinator.mac}-valve_0-valve" + async_remove_shelly_entity(hass, "switch", unique_id) class BlockShellyValve(ShellyBlockAttributeEntity, ValveEntity): diff --git a/tests/components/shelly/test_switch.py b/tests/components/shelly/test_switch.py index daaf03b081b..637a92a7fbe 100644 --- a/tests/components/shelly/test_switch.py +++ b/tests/components/shelly/test_switch.py @@ -7,10 +7,7 @@ from aioshelly.const import MODEL_GAS from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError import pytest -from homeassistant.components import automation, script -from homeassistant.components.automation import automations_with_entity from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN -from homeassistant.components.script import scripts_with_entity from homeassistant.components.shelly.const import ( DOMAIN, MODEL_WALL_DISPLAY, @@ -30,8 +27,6 @@ from homeassistant.core import HomeAssistant, State from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.device_registry import DeviceRegistry from homeassistant.helpers.entity_registry import EntityRegistry -import homeassistant.helpers.issue_registry as ir -from homeassistant.setup import async_setup_component from . import get_entity_state, init_integration, register_device, register_entity @@ -388,13 +383,12 @@ async def test_rpc_auth_error( assert flow["context"].get("entry_id") == entry.entry_id -async def test_block_device_gas_valve( +async def test_remove_gas_valve_switch( hass: HomeAssistant, mock_block_device: Mock, entity_registry: EntityRegistry, - monkeypatch: pytest.MonkeyPatch, ) -> None: - """Test block device Shelly Gas with Valve addon.""" + """Test removing deprecated switch entity for Shelly Gas Valve.""" entity_id = register_entity( hass, SWITCH_DOMAIN, @@ -403,41 +397,7 @@ async def test_block_device_gas_valve( ) await init_integration(hass, 1, MODEL_GAS) - entry = entity_registry.async_get(entity_id) - assert entry - assert entry.unique_id == "123456789ABC-valve_0-valve" - - assert hass.states.get(entity_id).state == STATE_OFF # valve is closed - - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - {ATTR_ENTITY_ID: entity_id}, - blocking=True, - ) - - state = hass.states.get(entity_id) - assert state - assert state.state == STATE_ON # valve is open - - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: entity_id}, - blocking=True, - ) - - state = hass.states.get(entity_id) - assert state - assert state.state == STATE_OFF # valve is closed - - monkeypatch.setattr(mock_block_device.blocks[GAS_VALVE_BLOCK_ID], "valve", "opened") - mock_block_device.mock_update() - await hass.async_block_till_done() - - state = hass.states.get(entity_id) - assert state - assert state.state == STATE_ON # valve is open + assert entity_registry.async_get(entity_id) is None async def test_wall_display_relay_mode( @@ -470,63 +430,3 @@ async def test_wall_display_relay_mode( entry = entity_registry.async_get(switch_entity_id) assert entry assert entry.unique_id == "123456789ABC-switch:0" - - -@pytest.mark.usefixtures("entity_registry_enabled_by_default") -async def test_create_issue_valve_switch( - hass: HomeAssistant, - mock_block_device: Mock, - monkeypatch: pytest.MonkeyPatch, - issue_registry: ir.IssueRegistry, -) -> None: - """Test we create an issue when an automation or script is using a deprecated entity.""" - monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False}) - entity_id = register_entity( - hass, - SWITCH_DOMAIN, - "test_name_valve", - "valve_0-valve", - ) - - assert await async_setup_component( - hass, - automation.DOMAIN, - { - automation.DOMAIN: { - "alias": "test", - "trigger": {"platform": "state", "entity_id": entity_id}, - "action": {"service": "switch.turn_on", "entity_id": entity_id}, - } - }, - ) - assert await async_setup_component( - hass, - script.DOMAIN, - { - script.DOMAIN: { - "test": { - "sequence": [ - { - "service": "switch.turn_on", - "data": {"entity_id": entity_id}, - }, - ], - } - } - }, - ) - - await init_integration(hass, 1, MODEL_GAS) - - assert automations_with_entity(hass, entity_id)[0] == "automation.test" - assert scripts_with_entity(hass, entity_id)[0] == "script.test" - - assert issue_registry.async_get_issue(DOMAIN, "deprecated_valve_switch") - assert issue_registry.async_get_issue( - DOMAIN, "deprecated_valve_switch.test_name_valve_automation.test" - ) - assert issue_registry.async_get_issue( - DOMAIN, "deprecated_valve_switch.test_name_valve_script.test" - ) - - assert len(issue_registry.issues) == 3