| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- """Tests for the valve entity"""
- from unittest.mock import AsyncMock, Mock
- import pytest
- from homeassistant.components.valve import (
- ValveEntityFeature,
- ValveState,
- )
- from homeassistant.components.valve.const import ValveEntityStateAttribute
- from pytest_homeassistant_custom_component.common import MockConfigEntry
- from custom_components.tuya_local.const import (
- CONF_DEVICE_ID,
- CONF_PROTOCOL_VERSION,
- CONF_TYPE,
- DOMAIN,
- )
- from custom_components.tuya_local.helpers.device_config import get_config
- from custom_components.tuya_local.valve import TuyaLocalValve, async_setup_entry
- from .helpers import assert_device_properties_set, mock_device
- FRANKEVER_DPS = {
- "1": True,
- "9": 0,
- "38": "memory",
- "101": 100,
- "102": 100,
- }
- def _make_frankever_valve(mocker, dps=None):
- """Create a valve using the FrankEver position-controlled profile."""
- config = get_config("frankever_watervalve")
- entity_config = next(
- entity for entity in config.all_entities() if entity.entity == "valve"
- )
- device = mock_device(dps or FRANKEVER_DPS, mocker)
- return TuyaLocalValve(device, entity_config), device
- @pytest.mark.asyncio
- async def test_init_entry(hass):
- """Test initialisation"""
- entry = MockConfigEntry(
- domain=DOMAIN,
- data={
- CONF_TYPE: "ble_water_valve",
- CONF_DEVICE_ID: "dummy",
- CONF_PROTOCOL_VERSION: "auto",
- },
- )
- m_add_entities = Mock()
- m_device = AsyncMock()
- hass.data[DOMAIN] = {
- "dummy": {
- "device": m_device,
- },
- }
- await async_setup_entry(hass, entry, m_add_entities)
- assert type(hass.data[DOMAIN]["dummy"]["valve_water"]) is TuyaLocalValve
- m_add_entities.assert_called_once()
- @pytest.mark.asyncio
- async def test_init_entry_fails_if_device_has_no_valve(hass):
- """Test initialisation when device has no matching entity"""
- entry = MockConfigEntry(
- domain=DOMAIN,
- data={
- CONF_TYPE: "kogan_heater",
- CONF_DEVICE_ID: "dummy",
- CONF_PROTOCOL_VERSION: "auto",
- },
- )
- m_add_entities = Mock()
- m_device = AsyncMock()
- hass.data[DOMAIN] = {
- "dummy": {
- "device": m_device,
- },
- }
- try:
- await async_setup_entry(hass, entry, m_add_entities)
- assert False
- except ValueError:
- pass
- m_add_entities.assert_not_called()
- @pytest.mark.parametrize(
- ("position", "expected_state"),
- [
- (0, ValveState.CLOSED),
- (20, ValveState.OPEN),
- (100, ValveState.OPEN),
- ],
- )
- def test_separate_current_position(mocker, position, expected_state):
- """Current position is reported by the read-only feedback DP."""
- dps = {**FRANKEVER_DPS, "101": position, "102": position}
- valve, _ = _make_frankever_valve(mocker, dps)
- assert valve.current_valve_position == position
- assert valve.is_closed is (position == 0)
- assert valve.state == expected_state
- assert valve.state_attributes == {
- ValveEntityStateAttribute.IS_CLOSED: position == 0,
- ValveEntityStateAttribute.CURRENT_POSITION: position,
- }
- assert valve.supported_features == (
- ValveEntityFeature.OPEN
- | ValveEntityFeature.CLOSE
- | ValveEntityFeature.SET_POSITION
- )
- @pytest.mark.asyncio
- async def test_set_position_writes_target_only(mocker):
- """Setting a target position does not change the separate switch."""
- valve, device = _make_frankever_valve(mocker)
- async with assert_device_properties_set(device, {"101": 50}):
- await valve.async_set_valve_position(50)
- @pytest.mark.asyncio
- async def test_set_zero_writes_target_only(mocker):
- """Setting a zero target position does not change the separate switch."""
- valve, device = _make_frankever_valve(mocker)
- async with assert_device_properties_set(device, {"101": 0}):
- await valve.async_set_valve_position(0)
- @pytest.mark.asyncio
- async def test_open_uses_switch(mocker):
- """Opening uses the switch without changing a non-zero target position."""
- valve, device = _make_frankever_valve(mocker)
- async with assert_device_properties_set(device, {"1": True}):
- await valve.async_open_valve()
- @pytest.mark.asyncio
- async def test_close_uses_switch(mocker):
- """Closing uses the switch without changing the target position."""
- valve, device = _make_frankever_valve(mocker)
- async with assert_device_properties_set(device, {"1": False}):
- await valve.async_close_valve()
- def test_frankever_product_match_is_preferred():
- """The explicit product match is stronger than the Tellur DPS match."""
- frankever = get_config("frankever_watervalve")
- tellur = get_config("tellur_tll331501_watervalve")
- product_ids = ["nzx0kku9d6eq59nt"]
- assert frankever.matches_product(product_ids[0])
- assert frankever.match_quality(FRANKEVER_DPS, product_ids) == 101
- assert tellur.match_quality(FRANKEVER_DPS) == 100
- def test_frankever_auxiliary_entity_values(mocker):
- """Countdown and initial-state mappings retain their observed values."""
- config = get_config("frankever_watervalve")
- entities = {entity.entity: entity for entity in config.all_entities()}
- device = mock_device(FRANKEVER_DPS, mocker)
- assert entities["time"].find_dps("second").get_value(device) == 0
- initial_state = entities["select"].find_dps("option")
- assert initial_state.get_value(device) == "memory"
- assert initial_state.values(device) == ["off", "on", "memory"]
- @pytest.mark.asyncio
- async def test_init_entry_fails_if_config_is_missing(hass):
- """Test initialisation when device has no matching entity"""
- entry = MockConfigEntry(
- domain=DOMAIN,
- data={
- CONF_TYPE: "non_existing",
- CONF_DEVICE_ID: "dummy",
- CONF_PROTOCOL_VERSION: "auto",
- },
- )
- # although async, the async_add_entities function passed to
- # async_setup_entry is called truly asynchronously. If we use
- # AsyncMock, it expects us to await the result.
- m_add_entities = Mock()
- m_device = AsyncMock()
- hass.data[DOMAIN] = {}
- hass.data[DOMAIN]["dummy"] = {}
- hass.data[DOMAIN]["dummy"]["device"] = m_device
- try:
- await async_setup_entry(hass, entry, m_add_entities)
- assert False
- except ValueError:
- pass
- m_add_entities.assert_not_called()
|