Add Fan platform to Switch as X (#68122)

This commit is contained in:
Franck Nijhof 2022-03-14 15:07:21 +01:00 committed by GitHub
parent ed6c86a281
commit 344537d0a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 232 additions and 7 deletions

View File

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

View File

@ -0,0 +1,64 @@
"""Fan support for switch entities."""
from __future__ import annotations
from homeassistant.components.fan import FanEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import BaseToggleEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Initialize Fan 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(
[
FanSwitch(
config_entry.title,
entity_id,
config_entry.entry_id,
device_id,
)
]
)
class FanSwitch(BaseToggleEntity, FanEntity):
"""Represents a Switch as a Fan."""
@property
def is_on(self) -> bool | None:
"""Return true if the entity is on.
Fan logic uses speed percentage or preset mode to determine
its it on or off, however, when using a wrapped switch, we
just use the wrapped switch's state.
"""
return self._attr_is_on
# pylint: disable=arguments-differ
async def async_turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs,
) -> None:
"""Turn on the fan.
Arguments of the turn_on methods fan entity differ,
thus we need to override them here.
"""
await super().async_turn_on()

View File

@ -0,0 +1,117 @@
"""Tests for the Switch as X Fan platform."""
from homeassistant.components.fan import DOMAIN as FAN_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_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
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 fan switch default state."""
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
CONF_ENTITY_ID: "switch.test",
CONF_TARGET_DOMAIN: Platform.FAN,
},
title="Wind Machine",
)
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("fan.wind_machine")
assert state is not None
assert state.state == "unavailable"
assert state.attributes["supported_features"] == 0
async def test_service_calls(hass: HomeAssistant) -> None:
"""Test service calls affecting the switch as fan 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.FAN,
},
title="wind_machine",
)
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("fan.wind_machine").state == STATE_ON
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TOGGLE,
{CONF_ENTITY_ID: "fan.wind_machine"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_OFF
assert hass.states.get("fan.wind_machine").state == STATE_OFF
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_ON,
{CONF_ENTITY_ID: "fan.wind_machine"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_ON
assert hass.states.get("fan.wind_machine").state == STATE_ON
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_OFF,
{CONF_ENTITY_ID: "fan.wind_machine"},
blocking=True,
)
assert hass.states.get("switch.decorative_lights").state == STATE_OFF
assert hass.states.get("fan.wind_machine").state == STATE_OFF
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("fan.wind_machine").state == STATE_ON
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("fan.wind_machine").state == STATE_OFF
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("fan.wind_machine").state == STATE_ON

View File

@ -12,7 +12,13 @@ from tests.common import MockConfigEntry
@pytest.mark.parametrize(
"target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN)
"target_domain",
(
Platform.COVER,
Platform.FAN,
Platform.LIGHT,
Platform.SIREN,
),
)
async def test_config_entry_unregistered_uuid(
hass: HomeAssistant, target_domain: str
@ -38,7 +44,14 @@ async def test_config_entry_unregistered_uuid(
assert len(hass.states.async_all()) == 0
@pytest.mark.parametrize("target_domain", (Platform.LIGHT, Platform.SIREN))
@pytest.mark.parametrize(
"target_domain",
(
Platform.FAN,
Platform.LIGHT,
Platform.SIREN,
),
)
async def test_entity_registry_events(hass: HomeAssistant, target_domain: str) -> None:
"""Test entity registry events are tracked."""
registry = er.async_get(hass)
@ -96,7 +109,13 @@ async def test_entity_registry_events(hass: HomeAssistant, target_domain: str) -
@pytest.mark.parametrize(
"target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN)
"target_domain",
(
Platform.COVER,
Platform.FAN,
Platform.LIGHT,
Platform.SIREN,
),
)
async def test_device_registry_config_entry_1(
hass: HomeAssistant, target_domain: str
@ -156,7 +175,13 @@ async def test_device_registry_config_entry_1(
@pytest.mark.parametrize(
"target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN)
"target_domain",
(
Platform.COVER,
Platform.FAN,
Platform.LIGHT,
Platform.SIREN,
),
)
async def test_device_registry_config_entry_2(
hass: HomeAssistant, target_domain: str
@ -209,7 +234,13 @@ async def test_device_registry_config_entry_2(
@pytest.mark.parametrize(
"target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN)
"target_domain",
(
Platform.COVER,
Platform.FAN,
Platform.LIGHT,
Platform.SIREN,
),
)
async def test_config_entry_entity_id(
hass: HomeAssistant, target_domain: Platform
@ -246,7 +277,13 @@ async def test_config_entry_entity_id(
@pytest.mark.parametrize(
"target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN)
"target_domain",
(
Platform.COVER,
Platform.FAN,
Platform.LIGHT,
Platform.SIREN,
),
)
async def test_config_entry_uuid(hass: HomeAssistant, target_domain: Platform) -> None:
"""Test light switch setup from config entry with entity registry id."""
@ -272,7 +309,13 @@ async def test_config_entry_uuid(hass: HomeAssistant, target_domain: Platform) -
@pytest.mark.parametrize(
"target_domain", (Platform.LIGHT, Platform.COVER, Platform.SIREN)
"target_domain",
(
Platform.COVER,
Platform.FAN,
Platform.LIGHT,
Platform.SIREN,
),
)
async def test_device(hass: HomeAssistant, target_domain: Platform) -> None:
"""Test the entity is added to the wrapped entity's device."""