Remove the switch entity for Shelly Gas Valve (#119817)

Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
pull/118664/head^2
Maciej Bieniek 2024-06-17 19:17:06 +02:00 committed by GitHub
parent 57308599cd
commit 87c1d5a6a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 10 additions and 236 deletions

View File

@ -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."""

View File

@ -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):

View File

@ -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