276 lines
9.0 KiB
Python
276 lines
9.0 KiB
Python
"""Test Matter Fan platform."""
|
|
|
|
from unittest.mock import MagicMock, call
|
|
|
|
from matter_server.client.models.node import MatterNode
|
|
import pytest
|
|
|
|
from homeassistant.components.fan import (
|
|
ATTR_DIRECTION,
|
|
ATTR_OSCILLATING,
|
|
ATTR_PERCENTAGE,
|
|
ATTR_PRESET_MODE,
|
|
DIRECTION_FORWARD,
|
|
DIRECTION_REVERSE,
|
|
DOMAIN as FAN_DOMAIN,
|
|
SERVICE_OSCILLATE,
|
|
SERVICE_SET_DIRECTION,
|
|
FanEntityFeature,
|
|
)
|
|
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from .common import (
|
|
set_node_attribute,
|
|
setup_integration_with_node_fixture,
|
|
trigger_subscription_callback,
|
|
)
|
|
|
|
|
|
@pytest.fixture(name="air_purifier")
|
|
async def air_purifier_fixture(
|
|
hass: HomeAssistant, matter_client: MagicMock
|
|
) -> MatterNode:
|
|
"""Fixture for a Air Purifier node (containing Fan cluster)."""
|
|
return await setup_integration_with_node_fixture(
|
|
hass, "air-purifier", matter_client
|
|
)
|
|
|
|
|
|
# This tests needs to be adjusted to remove lingering tasks
|
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
|
async def test_fan_base(
|
|
hass: HomeAssistant,
|
|
matter_client: MagicMock,
|
|
air_purifier: MatterNode,
|
|
) -> None:
|
|
"""Test Fan platform."""
|
|
entity_id = "fan.air_purifier_fan"
|
|
state = hass.states.get(entity_id)
|
|
assert state
|
|
assert state.attributes["preset_modes"] == [
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"auto",
|
|
"natural_wind",
|
|
"sleep_wind",
|
|
]
|
|
assert state.attributes["direction"] == "forward"
|
|
assert state.attributes["oscillating"] is False
|
|
assert state.attributes["percentage"] is None
|
|
assert state.attributes["percentage_step"] == 10
|
|
assert state.attributes["preset_mode"] == "auto"
|
|
mask = (
|
|
FanEntityFeature.DIRECTION
|
|
| FanEntityFeature.OSCILLATE
|
|
| FanEntityFeature.PRESET_MODE
|
|
| FanEntityFeature.SET_SPEED
|
|
)
|
|
assert state.attributes["supported_features"] & mask == mask
|
|
# handle fan mode update
|
|
set_node_attribute(air_purifier, 1, 514, 0, 1)
|
|
await trigger_subscription_callback(hass, matter_client)
|
|
state = hass.states.get(entity_id)
|
|
assert state.attributes["preset_mode"] == "low"
|
|
# handle direction update
|
|
set_node_attribute(air_purifier, 1, 514, 11, 1)
|
|
await trigger_subscription_callback(hass, matter_client)
|
|
state = hass.states.get(entity_id)
|
|
assert state.attributes["direction"] == "reverse"
|
|
# handle rock/oscillation update
|
|
set_node_attribute(air_purifier, 1, 514, 8, 1)
|
|
await trigger_subscription_callback(hass, matter_client)
|
|
state = hass.states.get(entity_id)
|
|
assert state.attributes["oscillating"] is True
|
|
# handle wind mode active translates to correct preset
|
|
set_node_attribute(air_purifier, 1, 514, 10, 2)
|
|
await trigger_subscription_callback(hass, matter_client)
|
|
state = hass.states.get(entity_id)
|
|
assert state.attributes["preset_mode"] == "natural_wind"
|
|
set_node_attribute(air_purifier, 1, 514, 10, 1)
|
|
await trigger_subscription_callback(hass, matter_client)
|
|
state = hass.states.get(entity_id)
|
|
assert state.attributes["preset_mode"] == "sleep_wind"
|
|
|
|
|
|
async def test_fan_turn_on_with_percentage(
|
|
hass: HomeAssistant,
|
|
matter_client: MagicMock,
|
|
air_purifier: MatterNode,
|
|
) -> None:
|
|
"""Test turning on the fan with a specific percentage."""
|
|
entity_id = "fan.air_purifier_fan"
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_PERCENTAGE: 50},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 1
|
|
assert matter_client.write_attribute.call_args == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/2",
|
|
value=50,
|
|
)
|
|
|
|
|
|
async def test_fan_turn_on_with_preset_mode(
|
|
hass: HomeAssistant,
|
|
matter_client: MagicMock,
|
|
air_purifier: MatterNode,
|
|
) -> None:
|
|
"""Test turning on the fan with a specific preset mode."""
|
|
entity_id = "fan.air_purifier_fan"
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: "medium"},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 1
|
|
assert matter_client.write_attribute.call_args == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/0",
|
|
value=2,
|
|
)
|
|
# test again with wind feature as preset mode
|
|
for preset_mode, value in (("natural_wind", 2), ("sleep_wind", 1)):
|
|
matter_client.write_attribute.reset_mock()
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: preset_mode},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 1
|
|
assert matter_client.write_attribute.call_args == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/10",
|
|
value=value,
|
|
)
|
|
# test again where preset_mode is omitted in the service call
|
|
# which should select a default preset mode
|
|
matter_client.write_attribute.reset_mock()
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: entity_id},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 1
|
|
assert matter_client.write_attribute.call_args == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/0",
|
|
value=5,
|
|
)
|
|
# test again if wind mode is explicitly turned off when we set a new preset mode
|
|
matter_client.write_attribute.reset_mock()
|
|
set_node_attribute(air_purifier, 1, 514, 10, 2)
|
|
await trigger_subscription_callback(hass, matter_client)
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: "medium"},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 2
|
|
assert matter_client.write_attribute.call_args_list[0] == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/10",
|
|
value=0,
|
|
)
|
|
assert matter_client.write_attribute.call_args == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/0",
|
|
value=2,
|
|
)
|
|
|
|
|
|
async def test_fan_turn_off(
|
|
hass: HomeAssistant,
|
|
matter_client: MagicMock,
|
|
air_purifier: MatterNode,
|
|
) -> None:
|
|
"""Test turning off the fan."""
|
|
entity_id = "fan.air_purifier_fan"
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: entity_id},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 1
|
|
assert matter_client.write_attribute.call_args == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/0",
|
|
value=0,
|
|
)
|
|
matter_client.write_attribute.reset_mock()
|
|
# test again if wind mode is turned off
|
|
set_node_attribute(air_purifier, 1, 514, 10, 2)
|
|
await trigger_subscription_callback(hass, matter_client)
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: entity_id},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 2
|
|
assert matter_client.write_attribute.call_args_list[0] == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/10",
|
|
value=0,
|
|
)
|
|
assert matter_client.write_attribute.call_args_list[1] == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/0",
|
|
value=0,
|
|
)
|
|
|
|
|
|
async def test_fan_oscillate(
|
|
hass: HomeAssistant,
|
|
matter_client: MagicMock,
|
|
air_purifier: MatterNode,
|
|
) -> None:
|
|
"""Test oscillating the fan."""
|
|
entity_id = "fan.air_purifier_fan"
|
|
for oscillating, value in ((True, 1), (False, 0)):
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_OSCILLATE,
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_OSCILLATING: oscillating},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 1
|
|
assert matter_client.write_attribute.call_args == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/8",
|
|
value=value,
|
|
)
|
|
matter_client.write_attribute.reset_mock()
|
|
|
|
|
|
async def test_fan_set_direction(
|
|
hass: HomeAssistant,
|
|
matter_client: MagicMock,
|
|
air_purifier: MatterNode,
|
|
) -> None:
|
|
"""Test oscillating the fan."""
|
|
entity_id = "fan.air_purifier_fan"
|
|
for direction, value in ((DIRECTION_FORWARD, 0), (DIRECTION_REVERSE, 1)):
|
|
await hass.services.async_call(
|
|
FAN_DOMAIN,
|
|
SERVICE_SET_DIRECTION,
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_DIRECTION: direction},
|
|
blocking=True,
|
|
)
|
|
assert matter_client.write_attribute.call_count == 1
|
|
assert matter_client.write_attribute.call_args == call(
|
|
node_id=air_purifier.node_id,
|
|
attribute_path="1/514/11",
|
|
value=value,
|
|
)
|
|
matter_client.write_attribute.reset_mock()
|