mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
Fan support in WiZ (#146440)
This commit is contained in:
parent
4f938d032d
commit
db45f46c8a
4
CODEOWNERS
generated
4
CODEOWNERS
generated
@ -1758,8 +1758,8 @@ build.json @home-assistant/supervisor
|
|||||||
/homeassistant/components/wirelesstag/ @sergeymaysak
|
/homeassistant/components/wirelesstag/ @sergeymaysak
|
||||||
/homeassistant/components/withings/ @joostlek
|
/homeassistant/components/withings/ @joostlek
|
||||||
/tests/components/withings/ @joostlek
|
/tests/components/withings/ @joostlek
|
||||||
/homeassistant/components/wiz/ @sbidy
|
/homeassistant/components/wiz/ @sbidy @arturpragacz
|
||||||
/tests/components/wiz/ @sbidy
|
/tests/components/wiz/ @sbidy @arturpragacz
|
||||||
/homeassistant/components/wled/ @frenck
|
/homeassistant/components/wled/ @frenck
|
||||||
/tests/components/wled/ @frenck
|
/tests/components/wled/ @frenck
|
||||||
/homeassistant/components/wmspro/ @mback2k
|
/homeassistant/components/wmspro/ @mback2k
|
||||||
|
@ -37,6 +37,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
|
Platform.FAN,
|
||||||
Platform.LIGHT,
|
Platform.LIGHT,
|
||||||
Platform.NUMBER,
|
Platform.NUMBER,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
|
139
homeassistant/components/wiz/fan.py
Normal file
139
homeassistant/components/wiz/fan.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
"""WiZ integration fan platform."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
from typing import Any, ClassVar
|
||||||
|
|
||||||
|
from pywizlight.bulblibrary import BulbType, Features
|
||||||
|
|
||||||
|
from homeassistant.components.fan import (
|
||||||
|
DIRECTION_FORWARD,
|
||||||
|
DIRECTION_REVERSE,
|
||||||
|
FanEntity,
|
||||||
|
FanEntityFeature,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
|
from homeassistant.util.percentage import (
|
||||||
|
percentage_to_ranged_value,
|
||||||
|
ranged_value_to_percentage,
|
||||||
|
)
|
||||||
|
|
||||||
|
from . import WizConfigEntry
|
||||||
|
from .entity import WizEntity
|
||||||
|
from .models import WizData
|
||||||
|
|
||||||
|
PRESET_MODE_BREEZE = "breeze"
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: WizConfigEntry,
|
||||||
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the WiZ Platform from config_flow."""
|
||||||
|
if entry.runtime_data.bulb.bulbtype.features.fan:
|
||||||
|
async_add_entities([WizFanEntity(entry.runtime_data, entry.title)])
|
||||||
|
|
||||||
|
|
||||||
|
class WizFanEntity(WizEntity, FanEntity):
|
||||||
|
"""Representation of WiZ Light bulb."""
|
||||||
|
|
||||||
|
_attr_name = None
|
||||||
|
|
||||||
|
# We want the implementation of is_on to be the same as in ToggleEntity,
|
||||||
|
# but it is being overridden in FanEntity, so we need to restore it here.
|
||||||
|
is_on: ClassVar = ToggleEntity.is_on
|
||||||
|
|
||||||
|
def __init__(self, wiz_data: WizData, name: str) -> None:
|
||||||
|
"""Initialize a WiZ fan."""
|
||||||
|
super().__init__(wiz_data, name)
|
||||||
|
bulb_type: BulbType = self._device.bulbtype
|
||||||
|
features: Features = bulb_type.features
|
||||||
|
|
||||||
|
supported_features = (
|
||||||
|
FanEntityFeature.TURN_ON
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.SET_SPEED
|
||||||
|
)
|
||||||
|
if features.fan_reverse:
|
||||||
|
supported_features |= FanEntityFeature.DIRECTION
|
||||||
|
if features.fan_breeze_mode:
|
||||||
|
supported_features |= FanEntityFeature.PRESET_MODE
|
||||||
|
self._attr_preset_modes = [PRESET_MODE_BREEZE]
|
||||||
|
|
||||||
|
self._attr_supported_features = supported_features
|
||||||
|
self._attr_speed_count = bulb_type.fan_speed_range
|
||||||
|
|
||||||
|
self._async_update_attrs()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_update_attrs(self) -> None:
|
||||||
|
"""Handle updating _attr values."""
|
||||||
|
state = self._device.state
|
||||||
|
|
||||||
|
self._attr_is_on = state.get_fan_state() > 0
|
||||||
|
self._attr_percentage = ranged_value_to_percentage(
|
||||||
|
(1, self.speed_count), state.get_fan_speed()
|
||||||
|
)
|
||||||
|
if FanEntityFeature.PRESET_MODE in self.supported_features:
|
||||||
|
fan_mode = state.get_fan_mode()
|
||||||
|
self._attr_preset_mode = PRESET_MODE_BREEZE if fan_mode == 2 else None
|
||||||
|
if FanEntityFeature.DIRECTION in self.supported_features:
|
||||||
|
fan_reverse = state.get_fan_reverse()
|
||||||
|
self._attr_current_direction = None
|
||||||
|
if fan_reverse == 0:
|
||||||
|
self._attr_current_direction = DIRECTION_FORWARD
|
||||||
|
elif fan_reverse == 1:
|
||||||
|
self._attr_current_direction = DIRECTION_REVERSE
|
||||||
|
|
||||||
|
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||||
|
"""Set the preset mode of the fan."""
|
||||||
|
# preset_mode == PRESET_MODE_BREEZE
|
||||||
|
await self._device.fan_set_state(mode=2)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
async def async_set_percentage(self, percentage: int) -> None:
|
||||||
|
"""Set the speed percentage of the fan."""
|
||||||
|
if percentage == 0:
|
||||||
|
await self.async_turn_off()
|
||||||
|
return
|
||||||
|
|
||||||
|
speed = math.ceil(percentage_to_ranged_value((1, self.speed_count), percentage))
|
||||||
|
await self._device.fan_set_state(mode=1, speed=speed)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
async def async_turn_on(
|
||||||
|
self,
|
||||||
|
percentage: int | None = None,
|
||||||
|
preset_mode: str | None = None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
"""Turn on the fan."""
|
||||||
|
mode: int | None = None
|
||||||
|
speed: int | None = None
|
||||||
|
if preset_mode is not None:
|
||||||
|
self._valid_preset_mode_or_raise(preset_mode)
|
||||||
|
if preset_mode == PRESET_MODE_BREEZE:
|
||||||
|
mode = 2
|
||||||
|
if percentage is not None:
|
||||||
|
speed = math.ceil(
|
||||||
|
percentage_to_ranged_value((1, self.speed_count), percentage)
|
||||||
|
)
|
||||||
|
if mode is None:
|
||||||
|
mode = 1
|
||||||
|
await self._device.fan_turn_on(mode=mode, speed=speed)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn off the fan."""
|
||||||
|
await self._device.fan_turn_off(**kwargs)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
async def async_set_direction(self, direction: str) -> None:
|
||||||
|
"""Set the direction of the fan."""
|
||||||
|
reverse = 1 if direction == DIRECTION_REVERSE else 0
|
||||||
|
await self._device.fan_set_state(reverse=reverse)
|
||||||
|
await self.coordinator.async_request_refresh()
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "wiz",
|
"domain": "wiz",
|
||||||
"name": "WiZ",
|
"name": "WiZ",
|
||||||
"codeowners": ["@sbidy"],
|
"codeowners": ["@sbidy", "@arturpragacz"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"dependencies": ["network"],
|
"dependencies": ["network"],
|
||||||
"dhcp": [
|
"dhcp": [
|
||||||
|
@ -33,6 +33,10 @@ FAKE_STATE = PilotParser(
|
|||||||
"c": 0,
|
"c": 0,
|
||||||
"w": 0,
|
"w": 0,
|
||||||
"dimming": 100,
|
"dimming": 100,
|
||||||
|
"fanState": 0,
|
||||||
|
"fanMode": 1,
|
||||||
|
"fanSpeed": 1,
|
||||||
|
"fanRevrs": 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
FAKE_IP = "1.1.1.1"
|
FAKE_IP = "1.1.1.1"
|
||||||
@ -173,6 +177,25 @@ FAKE_OLD_FIRMWARE_DIMMABLE_BULB = BulbType(
|
|||||||
white_channels=1,
|
white_channels=1,
|
||||||
white_to_color_ratio=80,
|
white_to_color_ratio=80,
|
||||||
)
|
)
|
||||||
|
FAKE_DIMMABLE_FAN = BulbType(
|
||||||
|
bulb_type=BulbClass.FANDIM,
|
||||||
|
name="ESP03_FANDIMS_31",
|
||||||
|
features=Features(
|
||||||
|
color=False,
|
||||||
|
color_tmp=False,
|
||||||
|
effect=True,
|
||||||
|
brightness=True,
|
||||||
|
dual_head=False,
|
||||||
|
fan=True,
|
||||||
|
fan_breeze_mode=True,
|
||||||
|
fan_reverse=True,
|
||||||
|
),
|
||||||
|
kelvin_range=KelvinRange(max=2700, min=2700),
|
||||||
|
fw_version="1.31.32",
|
||||||
|
white_channels=1,
|
||||||
|
white_to_color_ratio=20,
|
||||||
|
fan_speed_range=6,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def setup_integration(hass: HomeAssistant) -> MockConfigEntry:
|
async def setup_integration(hass: HomeAssistant) -> MockConfigEntry:
|
||||||
@ -220,6 +243,9 @@ def _mocked_wizlight(
|
|||||||
bulb.async_close = AsyncMock()
|
bulb.async_close = AsyncMock()
|
||||||
bulb.set_speed = AsyncMock()
|
bulb.set_speed = AsyncMock()
|
||||||
bulb.set_ratio = AsyncMock()
|
bulb.set_ratio = AsyncMock()
|
||||||
|
bulb.fan_set_state = AsyncMock()
|
||||||
|
bulb.fan_turn_on = AsyncMock()
|
||||||
|
bulb.fan_turn_off = AsyncMock()
|
||||||
bulb.diagnostics = {
|
bulb.diagnostics = {
|
||||||
"mocked": "mocked",
|
"mocked": "mocked",
|
||||||
"roomId": 123,
|
"roomId": 123,
|
||||||
|
61
tests/components/wiz/snapshots/test_fan.ambr
Normal file
61
tests/components/wiz/snapshots/test_fan.ambr
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_entity[fan.mock_title-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'preset_modes': list([
|
||||||
|
'breeze',
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'fan',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'fan.mock_title',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': None,
|
||||||
|
'platform': 'wiz',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': <FanEntityFeature: 61>,
|
||||||
|
'translation_key': None,
|
||||||
|
'unique_id': 'abcabcabcabc',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entity[fan.mock_title-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'direction': 'forward',
|
||||||
|
'friendly_name': 'Mock Title',
|
||||||
|
'percentage': 16,
|
||||||
|
'percentage_step': 16.666666666666668,
|
||||||
|
'preset_mode': None,
|
||||||
|
'preset_modes': list([
|
||||||
|
'breeze',
|
||||||
|
]),
|
||||||
|
'supported_features': <FanEntityFeature: 61>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'fan.mock_title',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
232
tests/components/wiz/test_fan.py
Normal file
232
tests/components/wiz/test_fan.py
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
"""Tests for fan platform."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.fan import (
|
||||||
|
ATTR_DIRECTION,
|
||||||
|
ATTR_PERCENTAGE,
|
||||||
|
ATTR_PRESET_MODE,
|
||||||
|
DIRECTION_FORWARD,
|
||||||
|
DIRECTION_REVERSE,
|
||||||
|
DOMAIN as FAN_DOMAIN,
|
||||||
|
SERVICE_SET_DIRECTION,
|
||||||
|
SERVICE_SET_PERCENTAGE,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
)
|
||||||
|
from homeassistant.components.wiz.fan import PRESET_MODE_BREEZE
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
Platform,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from . import FAKE_DIMMABLE_FAN, FAKE_MAC, async_push_update, async_setup_integration
|
||||||
|
|
||||||
|
from tests.common import snapshot_platform
|
||||||
|
|
||||||
|
ENTITY_ID = "fan.mock_title"
|
||||||
|
|
||||||
|
INITIAL_PARAMS = {
|
||||||
|
"mac": FAKE_MAC,
|
||||||
|
"fanState": 0,
|
||||||
|
"fanMode": 1,
|
||||||
|
"fanSpeed": 1,
|
||||||
|
"fanRevrs": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@patch("homeassistant.components.wiz.PLATFORMS", [Platform.FAN])
|
||||||
|
async def test_entity(
|
||||||
|
hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry
|
||||||
|
) -> None:
|
||||||
|
"""Test the fan entity."""
|
||||||
|
entry = (await async_setup_integration(hass, bulb_type=FAKE_DIMMABLE_FAN))[1]
|
||||||
|
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
def _update_params(
|
||||||
|
params: dict[str, Any],
|
||||||
|
state: int | None = None,
|
||||||
|
mode: int | None = None,
|
||||||
|
speed: int | None = None,
|
||||||
|
reverse: int | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Get the parameters for the update."""
|
||||||
|
if state is not None:
|
||||||
|
params["fanState"] = state
|
||||||
|
if mode is not None:
|
||||||
|
params["fanMode"] = mode
|
||||||
|
if speed is not None:
|
||||||
|
params["fanSpeed"] = speed
|
||||||
|
if reverse is not None:
|
||||||
|
params["fanRevrs"] = reverse
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
async def test_turn_on_off(hass: HomeAssistant) -> None:
|
||||||
|
"""Test turning the fan on and off."""
|
||||||
|
device, _ = await async_setup_integration(hass, bulb_type=FAKE_DIMMABLE_FAN)
|
||||||
|
|
||||||
|
params = INITIAL_PARAMS.copy()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True
|
||||||
|
)
|
||||||
|
calls = device.fan_turn_on.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
args = calls[0][2]
|
||||||
|
assert args == {"mode": None, "speed": None}
|
||||||
|
await async_push_update(hass, device, _update_params(params, state=1, **args))
|
||||||
|
device.fan_turn_on.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_MODE_BREEZE},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
calls = device.fan_turn_on.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
args = calls[0][2]
|
||||||
|
assert args == {"mode": 2, "speed": None}
|
||||||
|
await async_push_update(hass, device, _update_params(params, state=1, **args))
|
||||||
|
device.fan_turn_on.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_MODE_BREEZE
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PERCENTAGE: 50},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
calls = device.fan_turn_on.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
args = calls[0][2]
|
||||||
|
assert args == {"mode": 1, "speed": 3}
|
||||||
|
await async_push_update(hass, device, _update_params(params, state=1, **args))
|
||||||
|
device.fan_turn_on.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes[ATTR_PERCENTAGE] == 50
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] is None
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True
|
||||||
|
)
|
||||||
|
calls = device.fan_turn_off.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
await async_push_update(hass, device, _update_params(params, state=0))
|
||||||
|
device.fan_turn_off.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
|
async def test_fan_set_preset_mode(hass: HomeAssistant) -> None:
|
||||||
|
"""Test setting the fan preset mode."""
|
||||||
|
device, _ = await async_setup_integration(hass, bulb_type=FAKE_DIMMABLE_FAN)
|
||||||
|
|
||||||
|
params = INITIAL_PARAMS.copy()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_MODE_BREEZE},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
calls = device.fan_set_state.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
args = calls[0][2]
|
||||||
|
assert args == {"mode": 2}
|
||||||
|
await async_push_update(hass, device, _update_params(params, state=1, **args))
|
||||||
|
device.fan_set_state.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_MODE_BREEZE
|
||||||
|
|
||||||
|
|
||||||
|
async def test_fan_set_percentage(hass: HomeAssistant) -> None:
|
||||||
|
"""Test setting the fan percentage."""
|
||||||
|
device, _ = await async_setup_integration(hass, bulb_type=FAKE_DIMMABLE_FAN)
|
||||||
|
|
||||||
|
params = INITIAL_PARAMS.copy()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN,
|
||||||
|
SERVICE_SET_PERCENTAGE,
|
||||||
|
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PERCENTAGE: 50},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
calls = device.fan_set_state.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
args = calls[0][2]
|
||||||
|
assert args == {"mode": 1, "speed": 3}
|
||||||
|
await async_push_update(hass, device, _update_params(params, state=1, **args))
|
||||||
|
device.fan_set_state.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes[ATTR_PERCENTAGE] == 50
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN,
|
||||||
|
SERVICE_SET_PERCENTAGE,
|
||||||
|
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PERCENTAGE: 0},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
calls = device.fan_turn_off.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
await async_push_update(hass, device, _update_params(params, state=0))
|
||||||
|
device.fan_set_state.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes[ATTR_PERCENTAGE] == 50
|
||||||
|
|
||||||
|
|
||||||
|
async def test_fan_set_direction(hass: HomeAssistant) -> None:
|
||||||
|
"""Test setting the fan direction."""
|
||||||
|
device, _ = await async_setup_integration(hass, bulb_type=FAKE_DIMMABLE_FAN)
|
||||||
|
|
||||||
|
params = INITIAL_PARAMS.copy()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN,
|
||||||
|
SERVICE_SET_DIRECTION,
|
||||||
|
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_DIRECTION: DIRECTION_REVERSE},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
calls = device.fan_set_state.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
args = calls[0][2]
|
||||||
|
assert args == {"reverse": 1}
|
||||||
|
await async_push_update(hass, device, _update_params(params, **args))
|
||||||
|
device.fan_set_state.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes[ATTR_DIRECTION] == DIRECTION_REVERSE
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
FAN_DOMAIN,
|
||||||
|
SERVICE_SET_DIRECTION,
|
||||||
|
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_DIRECTION: DIRECTION_FORWARD},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
calls = device.fan_set_state.mock_calls
|
||||||
|
assert len(calls) == 1
|
||||||
|
args = calls[0][2]
|
||||||
|
assert args == {"reverse": 0}
|
||||||
|
await async_push_update(hass, device, _update_params(params, **args))
|
||||||
|
device.fan_set_state.reset_mock()
|
||||||
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes[ATTR_DIRECTION] == DIRECTION_FORWARD
|
Loading…
x
Reference in New Issue
Block a user