mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 00:37:53 +00:00
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:
parent
edfb8c3423
commit
d174c8265e
@ -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):
|
||||
|
@ -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):
|
||||
|
122
homeassistant/components/wilight/fan.py
Normal file
122
homeassistant/components/wilight/fan.py
Normal 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)
|
@ -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"
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
206
tests/components/wilight/test_fan.py
Normal file
206
tests/components/wilight/test_fan.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user