Add cover platform to Switch as X (#68107)

This commit is contained in:
Franck Nijhof 2022-03-14 11:59:55 +01:00 committed by GitHub
parent 6aacaa744e
commit c70c699af0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 300 additions and 98 deletions

View File

@ -22,6 +22,7 @@ CONFIG_FLOW = {
{
"select": {
"options": [
{"value": Platform.COVER, "label": "Cover"},
{"value": Platform.LIGHT, "label": "Light"},
]
}

View File

@ -0,0 +1,83 @@
"""Cover support for switch entities."""
from __future__ import annotations
from typing import Any
from homeassistant.components.cover import SUPPORT_CLOSE, SUPPORT_OPEN, CoverEntity
from homeassistant.components.switch.const import DOMAIN as SWITCH_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import BaseEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Initialize Cover Switch config entry."""
registry = er.async_get(hass)
entity_id = er.async_validate_entity_id(
registry, config_entry.options[CONF_ENTITY_ID]
)
wrapped_switch = registry.async_get(entity_id)
device_id = wrapped_switch.device_id if wrapped_switch else None
async_add_entities(
[
CoverSwitch(
config_entry.title,
entity_id,
config_entry.entry_id,
device_id,
)
]
)
class CoverSwitch(BaseEntity, CoverEntity):
"""Represents a Switch as a Cover."""
_attr_supported_features = SUPPORT_OPEN | SUPPORT_CLOSE
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
await self.hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: self._switch_entity_id},
blocking=True,
context=self._context,
)
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close cover."""
await self.hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: self._switch_entity_id},
blocking=True,
context=self._context,
)
@callback
def async_state_changed_listener(self, event: Event | None = None) -> None:
"""Handle child updates."""
super().async_state_changed_listener(event)
if (
not self.available
or (state := self.hass.states.get(self._switch_entity_id)) is None
):
return
self._attr_is_closed = state.state != STATE_ON

View File

@ -0,0 +1,121 @@
"""Tests for the Switch as X Cover platform."""
from homeassistant.components.cover import DOMAIN as COVER_DOMAIN
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.components.switch_as_x.const import CONF_TARGET_DOMAIN, DOMAIN
from homeassistant.const import (
CONF_ENTITY_ID,
SERVICE_CLOSE_COVER,
SERVICE_OPEN_COVER,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_CLOSED,
STATE_OFF,
STATE_ON,
STATE_OPEN,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
async def test_default_state(hass: HomeAssistant) -> None:
"""Test cover switch default state."""
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: "switch.test",
CONF_TARGET_DOMAIN: Platform.COVER,
},
title="Garage Door",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get("cover.garage_door")
assert state is not None
assert state.state == "unavailable"
assert state.attributes["supported_features"] == 3
async def test_service_calls(hass: HomeAssistant) -> None:
"""Test service calls to cover."""
await async_setup_component(hass, "switch", {"switch": [{"platform": "demo"}]})
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: "switch.decorative_lights",
CONF_TARGET_DOMAIN: Platform.COVER,
},
title="garage_door",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get("cover.garage_door").state == STATE_OPEN
await hass.services.async_call(
COVER_DOMAIN,
SERVICE_TOGGLE,
{CONF_ENTITY_ID: "cover.garage_door"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_OFF
assert hass.states.get("cover.garage_door").state == STATE_CLOSED
await hass.services.async_call(
COVER_DOMAIN,
SERVICE_OPEN_COVER,
{CONF_ENTITY_ID: "cover.garage_door"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_ON
assert hass.states.get("cover.garage_door").state == STATE_OPEN
await hass.services.async_call(
COVER_DOMAIN,
SERVICE_CLOSE_COVER,
{CONF_ENTITY_ID: "cover.garage_door"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_OFF
assert hass.states.get("cover.garage_door").state == STATE_CLOSED
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{CONF_ENTITY_ID: "switch.decorative_lights"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_ON
assert hass.states.get("cover.garage_door").state == STATE_OPEN
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{CONF_ENTITY_ID: "switch.decorative_lights"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_OFF
assert hass.states.get("cover.garage_door").state == STATE_CLOSED
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TOGGLE,
{CONF_ENTITY_ID: "switch.decorative_lights"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_ON
assert hass.states.get("cover.garage_door").state == STATE_OPEN

View File

@ -200,3 +200,98 @@ async def test_device_registry_config_entry_2(
# Check that the switch_as_x config entry is removed from the device
device_entry = device_registry.async_get(device_entry.id)
assert switch_as_x_config_entry.entry_id not in device_entry.config_entries
@pytest.mark.parametrize("target_domain", (Platform.LIGHT, Platform.COVER))
async def test_config_entry_entity_id(
hass: HomeAssistant, target_domain: Platform
) -> None:
"""Test light switch setup from config entry with entity id."""
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: "switch.abc",
CONF_TARGET_DOMAIN: target_domain,
},
title="ABC",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert DOMAIN in hass.config.components
state = hass.states.get(f"{target_domain}.abc")
assert state
assert state.state == "unavailable"
# Name copied from config entry title
assert state.name == "ABC"
# Check the light is added to the entity registry
registry = er.async_get(hass)
entity_entry = registry.async_get(f"{target_domain}.abc")
assert entity_entry
assert entity_entry.unique_id == config_entry.entry_id
@pytest.mark.parametrize("target_domain", (Platform.LIGHT, Platform.COVER))
async def test_config_entry_uuid(hass: HomeAssistant, target_domain: Platform) -> None:
"""Test light switch setup from config entry with entity registry id."""
registry = er.async_get(hass)
registry_entry = registry.async_get_or_create("switch", "test", "unique")
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: registry_entry.id,
CONF_TARGET_DOMAIN: target_domain,
},
title="ABC",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get(f"{target_domain}.abc")
@pytest.mark.parametrize("target_domain", (Platform.LIGHT, Platform.COVER))
async def test_device(hass: HomeAssistant, target_domain: Platform) -> None:
"""Test the entity is added to the wrapped entity's device."""
device_registry = dr.async_get(hass)
entity_registry = er.async_get(hass)
test_config_entry = MockConfigEntry()
device_entry = device_registry.async_get_or_create(
config_entry_id=test_config_entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
)
switch_entity_entry = entity_registry.async_get_or_create(
"switch", "test", "unique", device_id=device_entry.id
)
switch_as_x_config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: switch_entity_entry.id,
CONF_TARGET_DOMAIN: target_domain,
},
title="ABC",
)
switch_as_x_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(switch_as_x_config_entry.entry_id)
await hass.async_block_till_done()
entity_entry = entity_registry.async_get(f"{target_domain}.abc")
assert entity_entry
assert entity_entry.device_id == switch_entity_entry.device_id

View File

@ -1,6 +1,4 @@
"""Tests for the Switch as X Light platform."""
import pytest
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
@ -25,7 +23,6 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
@ -150,98 +147,3 @@ async def test_switch_service_calls(hass: HomeAssistant) -> None:
assert hass.states.get("switch.decorative_lights").state == STATE_ON
assert hass.states.get("light.decorative_lights").state == STATE_ON
@pytest.mark.parametrize("target_domain", (Platform.LIGHT,))
async def test_config_entry_entity_id(
hass: HomeAssistant, target_domain: Platform
) -> None:
"""Test light switch setup from config entry with entity id."""
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: "switch.abc",
CONF_TARGET_DOMAIN: target_domain,
},
title="ABC",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert DOMAIN in hass.config.components
state = hass.states.get(f"{target_domain}.abc")
assert state
assert state.state == "unavailable"
# Name copied from config entry title
assert state.name == "ABC"
# Check the light is added to the entity registry
registry = er.async_get(hass)
entity_entry = registry.async_get(f"{target_domain}.abc")
assert entity_entry
assert entity_entry.unique_id == config_entry.entry_id
@pytest.mark.parametrize("target_domain", (Platform.LIGHT,))
async def test_config_entry_uuid(hass: HomeAssistant, target_domain: Platform) -> None:
"""Test light switch setup from config entry with entity registry id."""
registry = er.async_get(hass)
registry_entry = registry.async_get_or_create("switch", "test", "unique")
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: registry_entry.id,
CONF_TARGET_DOMAIN: target_domain,
},
title="ABC",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get(f"{target_domain}.abc")
@pytest.mark.parametrize("target_domain", (Platform.LIGHT,))
async def test_device(hass: HomeAssistant, target_domain: Platform) -> None:
"""Test the entity is added to the wrapped entity's device."""
device_registry = dr.async_get(hass)
entity_registry = er.async_get(hass)
test_config_entry = MockConfigEntry()
device_entry = device_registry.async_get_or_create(
config_entry_id=test_config_entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
)
switch_entity_entry = entity_registry.async_get_or_create(
"switch", "test", "unique", device_id=device_entry.id
)
switch_as_x_config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: switch_entity_entry.id,
CONF_TARGET_DOMAIN: target_domain,
},
title="ABC",
)
switch_as_x_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(switch_as_x_config_entry.entry_id)
await hass.async_block_till_done()
entity_entry = entity_registry.async_get(f"{target_domain}.abc")
assert entity_entry
assert entity_entry.device_id == switch_entity_entry.device_id