Add WiLight Fan (#39541)

* Add WiLight Fan

Add fan to WiLigt integration

* Updated fan.py and test_fan.py

* Creating new fan test

* Update homeassistant/components/wilight/__init__.py

OK!
Done!

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/wilight/fan.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/wilight/fan.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/wilight/fan.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* As MartinHjelmare requested

* Update fan.py

* Update tests/components/wilight/test_fan.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update tests/components/wilight/test_fan.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update test_fan.py

As Martin Hjelmare suggested

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Leonardo Figueiro 2021-01-25 10:03:11 -03:00 committed by GitHub
parent edfb8c3423
commit d174c8265e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 352 additions and 14 deletions

View File

@ -10,7 +10,7 @@ from .const import DOMAIN
from .parent_device import WiLightParent
# List the platforms that you want to support.
PLATFORMS = ["light"]
PLATFORMS = ["fan", "light"]
async def async_setup(hass: HomeAssistant, config: dict):

View File

@ -15,7 +15,7 @@ CONF_MODEL_NAME = "model_name"
WILIGHT_MANUFACTURER = "All Automacao Ltda"
# List the components supported by this integration.
ALLOWED_WILIGHT_COMPONENTS = ["light"]
ALLOWED_WILIGHT_COMPONENTS = ["light", "fan"]
class WiLightFlowHandler(ConfigFlow, domain=DOMAIN):

View File

@ -0,0 +1,122 @@
"""Support for WiLight Fan."""
from pywilight.const import (
DOMAIN,
FAN_V1,
ITEM_FAN,
WL_DIRECTION_FORWARD,
WL_DIRECTION_OFF,
WL_DIRECTION_REVERSE,
WL_SPEED_HIGH,
WL_SPEED_LOW,
WL_SPEED_MEDIUM,
)
from homeassistant.components.fan import (
DIRECTION_FORWARD,
SPEED_HIGH,
SPEED_LOW,
SPEED_MEDIUM,
SUPPORT_DIRECTION,
SUPPORT_SET_SPEED,
FanEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from . import WiLightDevice
SUPPORTED_SPEEDS = [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
SUPPORTED_FEATURES = SUPPORT_SET_SPEED | SUPPORT_DIRECTION
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities
):
"""Set up WiLight lights from a config entry."""
parent = hass.data[DOMAIN][entry.entry_id]
# Handle a discovered WiLight device.
entities = []
for item in parent.api.items:
if item["type"] != ITEM_FAN:
continue
index = item["index"]
item_name = item["name"]
if item["sub_type"] != FAN_V1:
continue
entity = WiLightFan(parent.api, index, item_name)
entities.append(entity)
async_add_entities(entities)
class WiLightFan(WiLightDevice, FanEntity):
"""Representation of a WiLights fan."""
def __init__(self, api_device, index, item_name):
"""Initialize the device."""
super().__init__(api_device, index, item_name)
# Initialize the WiLights fan.
self._direction = WL_DIRECTION_FORWARD
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORTED_FEATURES
@property
def icon(self):
"""Return the icon of device based on its type."""
return "mdi:fan"
@property
def is_on(self):
"""Return true if device is on."""
return self._status.get("direction", WL_DIRECTION_OFF) != WL_DIRECTION_OFF
@property
def speed(self) -> str:
"""Return the current speed."""
return self._status.get("speed", SPEED_HIGH)
@property
def speed_list(self) -> list:
"""Get the list of available speeds."""
return SUPPORTED_SPEEDS
@property
def current_direction(self) -> str:
"""Return the current direction of the fan."""
if "direction" in self._status:
if self._status["direction"] != WL_DIRECTION_OFF:
self._direction = self._status["direction"]
return self._direction
async def async_turn_on(self, speed: str = None, **kwargs):
"""Turn on the fan."""
if speed is None:
await self._client.set_fan_direction(self._index, self._direction)
else:
await self.async_set_speed(speed)
async def async_set_speed(self, speed: str):
"""Set the speed of the fan."""
wl_speed = WL_SPEED_HIGH
if speed == SPEED_LOW:
wl_speed = WL_SPEED_LOW
if speed == SPEED_MEDIUM:
wl_speed = WL_SPEED_MEDIUM
await self._client.set_fan_speed(self._index, wl_speed)
async def async_set_direction(self, direction: str):
"""Set the direction of the fan."""
wl_direction = WL_DIRECTION_REVERSE
if direction == DIRECTION_FORWARD:
wl_direction = WL_DIRECTION_FORWARD
await self._client.set_fan_direction(self._index, wl_direction)
async def async_turn_off(self, **kwargs):
"""Turn the fan off."""
await self._client.set_fan_direction(self._index, WL_DIRECTION_OFF)

View File

@ -3,7 +3,7 @@
"name": "WiLight",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/wilight",
"requirements": ["pywilight==0.0.65"],
"requirements": ["pywilight==0.0.66"],
"ssdp": [
{
"manufacturer": "All Automacao Ltda"

View File

@ -84,11 +84,9 @@ class WiLightParent:
async def async_reset(self):
"""Reset api."""
# If the initialization was wrong.
if self._api is None:
return True
self._api.client.stop()
# If the initialization was not wrong.
if self._api is not None:
self._api.client.stop()
def create_api_device(host):

View File

@ -1892,7 +1892,7 @@ pywebpush==1.9.2
pywemo==0.6.1
# homeassistant.components.wilight
pywilight==0.0.65
pywilight==0.0.66
# homeassistant.components.xeoma
pyxeoma==1.4.1

View File

@ -939,7 +939,7 @@ pywebpush==1.9.2
pywemo==0.6.1
# homeassistant.components.wilight
pywilight==0.0.65
pywilight==0.0.66
# homeassistant.components.zerproc
pyzerproc==0.4.7

View File

@ -21,7 +21,6 @@ from tests.common import MockConfigEntry
from tests.components.wilight import (
CONF_COMPONENTS,
HOST,
MOCK_SSDP_DISCOVERY_INFO_LIGHT_FAN,
MOCK_SSDP_DISCOVERY_INFO_MISSING_MANUFACTORER,
MOCK_SSDP_DISCOVERY_INFO_P_B,
MOCK_SSDP_DISCOVERY_INFO_WRONG_MANUFACTORER,
@ -32,7 +31,7 @@ from tests.components.wilight import (
@pytest.fixture(name="dummy_get_components_from_model_clear")
def mock_dummy_get_components_from_model():
def mock_dummy_get_components_from_model_clear():
"""Mock a clear components list."""
components = []
with patch(
@ -42,6 +41,17 @@ def mock_dummy_get_components_from_model():
yield components
@pytest.fixture(name="dummy_get_components_from_model_wrong")
def mock_dummy_get_components_from_model_wrong():
"""Mock a clear components list."""
components = ["wrong"]
with patch(
"pywilight.get_components_from_model",
return_value=components,
):
yield components
async def test_show_ssdp_form(hass: HomeAssistantType) -> None:
"""Test that the ssdp confirmation form is served."""
@ -96,10 +106,12 @@ async def test_ssdp_not_wilight_abort_3(
assert result["reason"] == "not_wilight_device"
async def test_ssdp_not_supported_abort(hass: HomeAssistantType) -> None:
async def test_ssdp_not_supported_abort(
hass: HomeAssistantType, dummy_get_components_from_model_wrong
) -> None:
"""Test that the ssdp aborts not_supported."""
discovery_info = MOCK_SSDP_DISCOVERY_INFO_LIGHT_FAN.copy()
discovery_info = MOCK_SSDP_DISCOVERY_INFO_P_B.copy()
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_SSDP}, data=discovery_info
)

View File

@ -0,0 +1,206 @@
"""Tests for the WiLight integration."""
from unittest.mock import patch
import pytest
import pywilight
from homeassistant.components.fan import (
ATTR_DIRECTION,
ATTR_SPEED,
DIRECTION_FORWARD,
DIRECTION_REVERSE,
DOMAIN as FAN_DOMAIN,
SERVICE_SET_DIRECTION,
SERVICE_SET_SPEED,
SPEED_HIGH,
SPEED_LOW,
SPEED_MEDIUM,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
)
from homeassistant.helpers.typing import HomeAssistantType
from . import (
HOST,
UPNP_MAC_ADDRESS,
UPNP_MODEL_NAME_LIGHT_FAN,
UPNP_MODEL_NUMBER,
UPNP_SERIAL,
WILIGHT_ID,
setup_integration,
)
@pytest.fixture(name="dummy_device_from_host_light_fan")
def mock_dummy_device_from_host_light_fan():
"""Mock a valid api_devce."""
device = pywilight.wilight_from_discovery(
f"http://{HOST}:45995/wilight.xml",
UPNP_MAC_ADDRESS,
UPNP_MODEL_NAME_LIGHT_FAN,
UPNP_SERIAL,
UPNP_MODEL_NUMBER,
)
device.set_dummy(True)
with patch(
"pywilight.device_from_host",
return_value=device,
):
yield device
async def test_loading_light_fan(
hass: HomeAssistantType,
dummy_device_from_host_light_fan,
) -> None:
"""Test the WiLight configuration entry loading."""
entry = await setup_integration(hass)
assert entry
assert entry.unique_id == WILIGHT_ID
entity_registry = await hass.helpers.entity_registry.async_get_registry()
# First segment of the strip
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.state == STATE_OFF
entry = entity_registry.async_get("fan.wl000000000099_2")
assert entry
assert entry.unique_id == "WL000000000099_1"
async def test_on_off_fan_state(
hass: HomeAssistantType, dummy_device_from_host_light_fan
) -> None:
"""Test the change of state of the fan switches."""
await setup_integration(hass)
# Turn on
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "fan.wl000000000099_2"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.state == STATE_ON
# Turn on with speed
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_ON,
{ATTR_SPEED: SPEED_LOW, ATTR_ENTITY_ID: "fan.wl000000000099_2"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.state == STATE_ON
assert state.attributes.get(ATTR_SPEED) == SPEED_LOW
# Turn off
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "fan.wl000000000099_2"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.state == STATE_OFF
async def test_speed_fan_state(
hass: HomeAssistantType, dummy_device_from_host_light_fan
) -> None:
"""Test the change of speed of the fan switches."""
await setup_integration(hass)
# Set speed Low
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_SPEED,
{ATTR_SPEED: SPEED_LOW, ATTR_ENTITY_ID: "fan.wl000000000099_2"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.attributes.get(ATTR_SPEED) == SPEED_LOW
# Set speed Medium
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_SPEED,
{ATTR_SPEED: SPEED_MEDIUM, ATTR_ENTITY_ID: "fan.wl000000000099_2"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.attributes.get(ATTR_SPEED) == SPEED_MEDIUM
# Set speed High
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_SPEED,
{ATTR_SPEED: SPEED_HIGH, ATTR_ENTITY_ID: "fan.wl000000000099_2"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.attributes.get(ATTR_SPEED) == SPEED_HIGH
async def test_direction_fan_state(
hass: HomeAssistantType, dummy_device_from_host_light_fan
) -> None:
"""Test the change of direction of the fan switches."""
await setup_integration(hass)
# Set direction Forward
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_DIRECTION,
{ATTR_DIRECTION: DIRECTION_FORWARD, ATTR_ENTITY_ID: "fan.wl000000000099_2"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.state == STATE_ON
assert state.attributes.get(ATTR_DIRECTION) == DIRECTION_FORWARD
# Set direction Reverse
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_SET_DIRECTION,
{ATTR_DIRECTION: DIRECTION_REVERSE, ATTR_ENTITY_ID: "fan.wl000000000099_2"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("fan.wl000000000099_2")
assert state
assert state.attributes.get(ATTR_DIRECTION) == DIRECTION_REVERSE