Add Lock platform to Switch as X (#68123)

Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
Franck Nijhof 2022-03-14 18:45:45 +01:00 committed by GitHub
parent 4506d45345
commit ed6466f706
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 201 additions and 1 deletions

View File

@ -25,6 +25,7 @@ CONFIG_FLOW = {
{"value": Platform.COVER, "label": "Cover"}, {"value": Platform.COVER, "label": "Cover"},
{"value": Platform.FAN, "label": "Fan"}, {"value": Platform.FAN, "label": "Fan"},
{"value": Platform.LIGHT, "label": "Light"}, {"value": Platform.LIGHT, "label": "Light"},
{"value": Platform.LOCK, "label": "Lock"},
{"value": Platform.SIREN, "label": "Siren"}, {"value": Platform.SIREN, "label": "Siren"},
] ]
} }

View File

@ -44,7 +44,7 @@ class FanSwitch(BaseToggleEntity, FanEntity):
"""Return true if the entity is on. """Return true if the entity is on.
Fan logic uses speed percentage or preset mode to determine Fan logic uses speed percentage or preset mode to determine
its it on or off, however, when using a wrapped switch, we if it's on or off, however, when using a wrapped switch, we
just use the wrapped switch's state. just use the wrapped switch's state.
""" """
return self._attr_is_on return self._attr_is_on

View File

@ -0,0 +1,83 @@
"""Lock support for switch entities."""
from __future__ import annotations
from typing import Any
from homeassistant.components.lock import LockEntity
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 Lock 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(
[
LockSwitch(
config_entry.title,
entity_id,
config_entry.entry_id,
device_id,
)
]
)
class LockSwitch(BaseEntity, LockEntity):
"""Represents a Switch as a Lock."""
async def async_lock(self, **kwargs: Any) -> None:
"""Lock the lock."""
await self.hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: self._switch_entity_id},
blocking=True,
context=self._context,
)
async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the lock."""
await self.hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{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
# Logic is the same as the lock device class for binary sensors
# on means open (unlocked), off means closed (locked)
self._attr_is_locked = state.state != STATE_ON

View File

@ -17,6 +17,7 @@ from tests.common import MockConfigEntry
Platform.COVER, Platform.COVER,
Platform.FAN, Platform.FAN,
Platform.LIGHT, Platform.LIGHT,
Platform.LOCK,
Platform.SIREN, Platform.SIREN,
), ),
) )
@ -114,6 +115,7 @@ async def test_entity_registry_events(hass: HomeAssistant, target_domain: str) -
Platform.COVER, Platform.COVER,
Platform.FAN, Platform.FAN,
Platform.LIGHT, Platform.LIGHT,
Platform.LOCK,
Platform.SIREN, Platform.SIREN,
), ),
) )
@ -180,6 +182,7 @@ async def test_device_registry_config_entry_1(
Platform.COVER, Platform.COVER,
Platform.FAN, Platform.FAN,
Platform.LIGHT, Platform.LIGHT,
Platform.LOCK,
Platform.SIREN, Platform.SIREN,
), ),
) )
@ -239,6 +242,7 @@ async def test_device_registry_config_entry_2(
Platform.COVER, Platform.COVER,
Platform.FAN, Platform.FAN,
Platform.LIGHT, Platform.LIGHT,
Platform.LOCK,
Platform.SIREN, Platform.SIREN,
), ),
) )
@ -282,6 +286,7 @@ async def test_config_entry_entity_id(
Platform.COVER, Platform.COVER,
Platform.FAN, Platform.FAN,
Platform.LIGHT, Platform.LIGHT,
Platform.LOCK,
Platform.SIREN, Platform.SIREN,
), ),
) )
@ -314,6 +319,7 @@ async def test_config_entry_uuid(hass: HomeAssistant, target_domain: Platform) -
Platform.COVER, Platform.COVER,
Platform.FAN, Platform.FAN,
Platform.LIGHT, Platform.LIGHT,
Platform.LOCK,
Platform.SIREN, Platform.SIREN,
), ),
) )

View File

@ -0,0 +1,110 @@
"""Tests for the Switch as X Lock platform."""
from homeassistant.components.lock import DOMAIN as LOCK_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_LOCK,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
SERVICE_UNLOCK,
STATE_LOCKED,
STATE_OFF,
STATE_ON,
STATE_UNLOCKED,
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 lock switch default state."""
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: "switch.test",
CONF_TARGET_DOMAIN: Platform.LOCK,
},
title="candy_jar",
)
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("lock.candy_jar")
assert state is not None
assert state.state == "unavailable"
async def test_service_calls(hass: HomeAssistant) -> None:
"""Test service calls affecting the switch as lock entity."""
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.LOCK,
},
title="candy_jar",
)
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("lock.candy_jar").state == STATE_UNLOCKED
await hass.services.async_call(
LOCK_DOMAIN,
SERVICE_LOCK,
{CONF_ENTITY_ID: "lock.candy_jar"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_OFF
assert hass.states.get("lock.candy_jar").state == STATE_LOCKED
await hass.services.async_call(
LOCK_DOMAIN,
SERVICE_UNLOCK,
{CONF_ENTITY_ID: "lock.candy_jar"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_ON
assert hass.states.get("lock.candy_jar").state == STATE_UNLOCKED
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("lock.candy_jar").state == STATE_LOCKED
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("lock.candy_jar").state == STATE_UNLOCKED
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_OFF
assert hass.states.get("lock.candy_jar").state == STATE_LOCKED