mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Add valve platform support to google_assistant (#106139)
* Add valve platform to google_assistant * Use constant for domains set
This commit is contained in:
parent
c126022d4f
commit
e311a6835e
@ -22,6 +22,7 @@ from homeassistant.components import (
|
||||
sensor,
|
||||
switch,
|
||||
vacuum,
|
||||
valve,
|
||||
water_heater,
|
||||
)
|
||||
|
||||
@ -65,6 +66,7 @@ DEFAULT_EXPOSED_DOMAINS = [
|
||||
"sensor",
|
||||
"switch",
|
||||
"vacuum",
|
||||
"valve",
|
||||
"water_heater",
|
||||
]
|
||||
|
||||
@ -95,6 +97,7 @@ TYPE_THERMOSTAT = f"{PREFIX_TYPES}THERMOSTAT"
|
||||
TYPE_TV = f"{PREFIX_TYPES}TV"
|
||||
TYPE_WINDOW = f"{PREFIX_TYPES}WINDOW"
|
||||
TYPE_VACUUM = f"{PREFIX_TYPES}VACUUM"
|
||||
TYPE_VALVE = f"{PREFIX_TYPES}VALVE"
|
||||
TYPE_WATERHEATER = f"{PREFIX_TYPES}WATERHEATER"
|
||||
|
||||
SERVICE_REQUEST_SYNC = "request_sync"
|
||||
@ -150,6 +153,7 @@ DOMAIN_TO_GOOGLE_TYPES = {
|
||||
sensor.DOMAIN: TYPE_SENSOR,
|
||||
switch.DOMAIN: TYPE_SWITCH,
|
||||
vacuum.DOMAIN: TYPE_VACUUM,
|
||||
valve.DOMAIN: TYPE_VALVE,
|
||||
water_heater.DOMAIN: TYPE_WATERHEATER,
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ from homeassistant.components import (
|
||||
sensor,
|
||||
switch,
|
||||
vacuum,
|
||||
valve,
|
||||
water_heater,
|
||||
)
|
||||
from homeassistant.components.alarm_control_panel import AlarmControlPanelEntityFeature
|
||||
@ -41,6 +42,7 @@ from homeassistant.components.light import LightEntityFeature
|
||||
from homeassistant.components.lock import STATE_JAMMED, STATE_UNLOCKING
|
||||
from homeassistant.components.media_player import MediaPlayerEntityFeature, MediaType
|
||||
from homeassistant.components.vacuum import VacuumEntityFeature
|
||||
from homeassistant.components.valve import ValveEntityFeature
|
||||
from homeassistant.components.water_heater import WaterHeaterEntityFeature
|
||||
from homeassistant.const import (
|
||||
ATTR_ASSUMED_STATE,
|
||||
@ -180,6 +182,57 @@ TRAITS: list[type[_Trait]] = []
|
||||
|
||||
FAN_SPEED_MAX_SPEED_COUNT = 5
|
||||
|
||||
COVER_VALVE_STATES = {
|
||||
cover.DOMAIN: {
|
||||
"closed": cover.STATE_CLOSED,
|
||||
"closing": cover.STATE_CLOSING,
|
||||
"open": cover.STATE_OPEN,
|
||||
"opening": cover.STATE_OPENING,
|
||||
},
|
||||
valve.DOMAIN: {
|
||||
"closed": valve.STATE_CLOSED,
|
||||
"closing": valve.STATE_CLOSING,
|
||||
"open": valve.STATE_OPEN,
|
||||
"opening": valve.STATE_OPENING,
|
||||
},
|
||||
}
|
||||
|
||||
SERVICE_STOP_COVER_VALVE = {
|
||||
cover.DOMAIN: cover.SERVICE_STOP_COVER,
|
||||
valve.DOMAIN: valve.SERVICE_STOP_VALVE,
|
||||
}
|
||||
SERVICE_OPEN_COVER_VALVE = {
|
||||
cover.DOMAIN: cover.SERVICE_OPEN_COVER,
|
||||
valve.DOMAIN: valve.SERVICE_OPEN_VALVE,
|
||||
}
|
||||
SERVICE_CLOSE_COVER_VALVE = {
|
||||
cover.DOMAIN: cover.SERVICE_CLOSE_COVER,
|
||||
valve.DOMAIN: valve.SERVICE_CLOSE_VALVE,
|
||||
}
|
||||
SERVICE_SET_POSITION_COVER_VALVE = {
|
||||
cover.DOMAIN: cover.SERVICE_SET_COVER_POSITION,
|
||||
valve.DOMAIN: valve.SERVICE_SET_VALVE_POSITION,
|
||||
}
|
||||
|
||||
COVER_VALVE_CURRENT_POSITION = {
|
||||
cover.DOMAIN: cover.ATTR_CURRENT_POSITION,
|
||||
valve.DOMAIN: valve.ATTR_CURRENT_POSITION,
|
||||
}
|
||||
|
||||
COVER_VALVE_POSITION = {
|
||||
cover.DOMAIN: cover.ATTR_POSITION,
|
||||
valve.DOMAIN: valve.ATTR_POSITION,
|
||||
}
|
||||
|
||||
COVER_VALVE_SET_POSITION_FEATURE = {
|
||||
cover.DOMAIN: CoverEntityFeature.SET_POSITION,
|
||||
valve.DOMAIN: ValveEntityFeature.SET_POSITION,
|
||||
}
|
||||
|
||||
COVER_VALVE_DOMAINS = {cover.DOMAIN, valve.DOMAIN}
|
||||
|
||||
FRIENDLY_DOMAIN = {cover.DOMAIN: "Cover", valve.DOMAIN: "Valve"}
|
||||
|
||||
_TraitT = TypeVar("_TraitT", bound="_Trait")
|
||||
|
||||
|
||||
@ -796,6 +849,9 @@ class StartStopTrait(_Trait):
|
||||
if domain == cover.DOMAIN and features & CoverEntityFeature.STOP:
|
||||
return True
|
||||
|
||||
if domain == valve.DOMAIN and features & ValveEntityFeature.STOP:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def sync_attributes(self):
|
||||
@ -807,7 +863,7 @@ class StartStopTrait(_Trait):
|
||||
& VacuumEntityFeature.PAUSE
|
||||
!= 0
|
||||
}
|
||||
if domain == cover.DOMAIN:
|
||||
if domain in COVER_VALVE_DOMAINS:
|
||||
return {}
|
||||
|
||||
def query_attributes(self):
|
||||
@ -823,14 +879,16 @@ class StartStopTrait(_Trait):
|
||||
|
||||
if domain == cover.DOMAIN:
|
||||
return {"isRunning": state in (cover.STATE_CLOSING, cover.STATE_OPENING)}
|
||||
if domain == valve.DOMAIN:
|
||||
return {"isRunning": True}
|
||||
|
||||
async def execute(self, command, data, params, challenge):
|
||||
"""Execute a StartStop command."""
|
||||
domain = self.state.domain
|
||||
if domain == vacuum.DOMAIN:
|
||||
return await self._execute_vacuum(command, data, params, challenge)
|
||||
if domain == cover.DOMAIN:
|
||||
return await self._execute_cover(command, data, params, challenge)
|
||||
if domain in COVER_VALVE_DOMAINS:
|
||||
return await self._execute_cover_or_valve(command, data, params, challenge)
|
||||
|
||||
async def _execute_vacuum(self, command, data, params, challenge):
|
||||
"""Execute a StartStop command."""
|
||||
@ -869,28 +927,35 @@ class StartStopTrait(_Trait):
|
||||
context=data.context,
|
||||
)
|
||||
|
||||
async def _execute_cover(self, command, data, params, challenge):
|
||||
async def _execute_cover_or_valve(self, command, data, params, challenge):
|
||||
"""Execute a StartStop command."""
|
||||
domain = self.state.domain
|
||||
if command == COMMAND_STARTSTOP:
|
||||
if params["start"] is False:
|
||||
if self.state.state in (
|
||||
cover.STATE_CLOSING,
|
||||
cover.STATE_OPENING,
|
||||
) or self.state.attributes.get(ATTR_ASSUMED_STATE):
|
||||
if (
|
||||
self.state.state
|
||||
in (
|
||||
COVER_VALVE_STATES[domain]["closing"],
|
||||
COVER_VALVE_STATES[domain]["opening"],
|
||||
)
|
||||
or domain == valve.DOMAIN
|
||||
or self.state.attributes.get(ATTR_ASSUMED_STATE)
|
||||
):
|
||||
await self.hass.services.async_call(
|
||||
self.state.domain,
|
||||
cover.SERVICE_STOP_COVER,
|
||||
domain,
|
||||
SERVICE_STOP_COVER_VALVE[domain],
|
||||
{ATTR_ENTITY_ID: self.state.entity_id},
|
||||
blocking=not self.config.should_report_state,
|
||||
context=data.context,
|
||||
)
|
||||
else:
|
||||
raise SmartHomeError(
|
||||
ERR_ALREADY_STOPPED, "Cover is already stopped"
|
||||
ERR_ALREADY_STOPPED,
|
||||
f"{FRIENDLY_DOMAIN[domain]} is already stopped",
|
||||
)
|
||||
else:
|
||||
raise SmartHomeError(
|
||||
ERR_NOT_SUPPORTED, "Starting a cover is not supported"
|
||||
ERR_NOT_SUPPORTED, f"Starting a {domain} is not supported"
|
||||
)
|
||||
else:
|
||||
raise SmartHomeError(
|
||||
@ -2081,7 +2146,7 @@ class OpenCloseTrait(_Trait):
|
||||
@staticmethod
|
||||
def supported(domain, features, device_class, _):
|
||||
"""Test if state is supported."""
|
||||
if domain == cover.DOMAIN:
|
||||
if domain in COVER_VALVE_DOMAINS:
|
||||
return True
|
||||
|
||||
return domain == binary_sensor.DOMAIN and device_class in (
|
||||
@ -2116,6 +2181,17 @@ class OpenCloseTrait(_Trait):
|
||||
and features & CoverEntityFeature.CLOSE == 0
|
||||
):
|
||||
response["queryOnlyOpenClose"] = True
|
||||
elif (
|
||||
self.state.domain == valve.DOMAIN
|
||||
and features & ValveEntityFeature.SET_POSITION == 0
|
||||
):
|
||||
response["discreteOnlyOpenClose"] = True
|
||||
|
||||
if (
|
||||
features & ValveEntityFeature.OPEN == 0
|
||||
and features & ValveEntityFeature.CLOSE == 0
|
||||
):
|
||||
response["queryOnlyOpenClose"] = True
|
||||
|
||||
if self.state.attributes.get(ATTR_ASSUMED_STATE):
|
||||
response["commandOnlyOpenClose"] = True
|
||||
@ -2134,17 +2210,17 @@ class OpenCloseTrait(_Trait):
|
||||
if self.state.attributes.get(ATTR_ASSUMED_STATE):
|
||||
return response
|
||||
|
||||
if domain == cover.DOMAIN:
|
||||
if domain in COVER_VALVE_DOMAINS:
|
||||
if self.state.state == STATE_UNKNOWN:
|
||||
raise SmartHomeError(
|
||||
ERR_NOT_SUPPORTED, "Querying state is not supported"
|
||||
)
|
||||
|
||||
position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION)
|
||||
position = self.state.attributes.get(COVER_VALVE_CURRENT_POSITION[domain])
|
||||
|
||||
if position is not None:
|
||||
response["openPercent"] = position
|
||||
elif self.state.state != cover.STATE_CLOSED:
|
||||
elif self.state.state != COVER_VALVE_STATES[domain]["closed"]:
|
||||
response["openPercent"] = 100
|
||||
else:
|
||||
response["openPercent"] = 0
|
||||
@ -2162,11 +2238,13 @@ class OpenCloseTrait(_Trait):
|
||||
domain = self.state.domain
|
||||
features = self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
|
||||
if domain == cover.DOMAIN:
|
||||
if domain in COVER_VALVE_DOMAINS:
|
||||
svc_params = {ATTR_ENTITY_ID: self.state.entity_id}
|
||||
should_verify = False
|
||||
if command == COMMAND_OPENCLOSE_RELATIVE:
|
||||
position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION)
|
||||
position = self.state.attributes.get(
|
||||
COVER_VALVE_CURRENT_POSITION[domain]
|
||||
)
|
||||
if position is None:
|
||||
raise SmartHomeError(
|
||||
ERR_NOT_SUPPORTED,
|
||||
@ -2177,16 +2255,16 @@ class OpenCloseTrait(_Trait):
|
||||
position = params["openPercent"]
|
||||
|
||||
if position == 0:
|
||||
service = cover.SERVICE_CLOSE_COVER
|
||||
service = SERVICE_CLOSE_COVER_VALVE[domain]
|
||||
should_verify = False
|
||||
elif position == 100:
|
||||
service = cover.SERVICE_OPEN_COVER
|
||||
service = SERVICE_OPEN_COVER_VALVE[domain]
|
||||
should_verify = True
|
||||
elif features & CoverEntityFeature.SET_POSITION:
|
||||
service = cover.SERVICE_SET_COVER_POSITION
|
||||
elif features & COVER_VALVE_SET_POSITION_FEATURE[domain]:
|
||||
service = SERVICE_SET_POSITION_COVER_VALVE[domain]
|
||||
if position > 0:
|
||||
should_verify = True
|
||||
svc_params[cover.ATTR_POSITION] = position
|
||||
svc_params[COVER_VALVE_POSITION[domain]] = position
|
||||
else:
|
||||
raise SmartHomeError(
|
||||
ERR_NOT_SUPPORTED, "No support for partial open close"
|
||||
@ -2200,7 +2278,7 @@ class OpenCloseTrait(_Trait):
|
||||
_verify_pin_challenge(data, self.state, challenge)
|
||||
|
||||
await self.hass.services.async_call(
|
||||
cover.DOMAIN,
|
||||
domain,
|
||||
service,
|
||||
svc_params,
|
||||
blocking=not self.config.should_report_state,
|
||||
|
@ -103,6 +103,7 @@
|
||||
'sensor',
|
||||
'switch',
|
||||
'vacuum',
|
||||
'valve',
|
||||
'water_heater',
|
||||
]),
|
||||
'project_id': '1234',
|
||||
|
@ -28,6 +28,7 @@ from homeassistant.components import (
|
||||
sensor,
|
||||
switch,
|
||||
vacuum,
|
||||
valve,
|
||||
water_heater,
|
||||
)
|
||||
from homeassistant.components.alarm_control_panel import AlarmControlPanelEntityFeature
|
||||
@ -46,6 +47,7 @@ from homeassistant.components.media_player import (
|
||||
MediaType,
|
||||
)
|
||||
from homeassistant.components.vacuum import VacuumEntityFeature
|
||||
from homeassistant.components.valve import ValveEntityFeature
|
||||
from homeassistant.components.water_heater import WaterHeaterEntityFeature
|
||||
from homeassistant.config import async_process_ha_core_config
|
||||
from homeassistant.const import (
|
||||
@ -650,6 +652,48 @@ async def test_startstop_cover_assumed(hass: HomeAssistant) -> None:
|
||||
assert stop_calls[0].data == {ATTR_ENTITY_ID: "cover.bla"}
|
||||
|
||||
|
||||
async def test_startstop_valve(hass: HomeAssistant) -> None:
|
||||
"""Test startStop trait support for valve domain."""
|
||||
assert helpers.get_google_type(valve.DOMAIN, None) is not None
|
||||
assert trait.StartStopTrait.supported(
|
||||
valve.DOMAIN, ValveEntityFeature.STOP, None, None
|
||||
)
|
||||
assert not trait.StartStopTrait.supported(
|
||||
valve.DOMAIN, ValveEntityFeature.SET_POSITION, None, None
|
||||
)
|
||||
|
||||
state = State(
|
||||
"valve.water",
|
||||
valve.STATE_CLOSED,
|
||||
{ATTR_SUPPORTED_FEATURES: ValveEntityFeature.STOP},
|
||||
)
|
||||
|
||||
trt = trait.StartStopTrait(
|
||||
hass,
|
||||
state,
|
||||
BASIC_CONFIG,
|
||||
)
|
||||
|
||||
assert trt.sync_attributes() == {}
|
||||
|
||||
for state_value in (
|
||||
valve.STATE_CLOSED,
|
||||
valve.STATE_CLOSING,
|
||||
valve.STATE_OPENING,
|
||||
valve.STATE_OPEN,
|
||||
):
|
||||
state.state = state_value
|
||||
assert trt.query_attributes() == {"isRunning": True}
|
||||
|
||||
stop_calls = async_mock_service(hass, valve.DOMAIN, valve.SERVICE_STOP_VALVE)
|
||||
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": False}, {})
|
||||
assert len(stop_calls) == 1
|
||||
assert stop_calls[0].data == {ATTR_ENTITY_ID: "valve.water"}
|
||||
|
||||
with pytest.raises(SmartHomeError, match="Starting a valve is not supported"):
|
||||
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": True}, {})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("supported_color_modes", [["hs"], ["rgb"], ["xy"]])
|
||||
async def test_color_setting_color_light(
|
||||
hass: HomeAssistant, supported_color_modes
|
||||
@ -2823,21 +2867,59 @@ async def test_traits_unknown_domains(
|
||||
caplog.clear()
|
||||
|
||||
|
||||
async def test_openclose_cover(hass: HomeAssistant) -> None:
|
||||
"""Test OpenClose trait support for cover domain."""
|
||||
assert helpers.get_google_type(cover.DOMAIN, None) is not None
|
||||
assert trait.OpenCloseTrait.supported(
|
||||
cover.DOMAIN, CoverEntityFeature.SET_POSITION, None, None
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"domain",
|
||||
"set_position_service",
|
||||
"close_service",
|
||||
"open_service",
|
||||
"set_position_feature",
|
||||
"attr_position",
|
||||
"attr_current_position",
|
||||
),
|
||||
[
|
||||
(
|
||||
cover.DOMAIN,
|
||||
cover.SERVICE_SET_COVER_POSITION,
|
||||
cover.SERVICE_CLOSE_COVER,
|
||||
cover.SERVICE_OPEN_COVER,
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
cover.ATTR_POSITION,
|
||||
cover.ATTR_CURRENT_POSITION,
|
||||
),
|
||||
(
|
||||
valve.DOMAIN,
|
||||
valve.SERVICE_SET_VALVE_POSITION,
|
||||
valve.SERVICE_CLOSE_VALVE,
|
||||
valve.SERVICE_OPEN_VALVE,
|
||||
ValveEntityFeature.SET_POSITION,
|
||||
valve.ATTR_POSITION,
|
||||
valve.ATTR_CURRENT_POSITION,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_openclose_cover_valve(
|
||||
hass: HomeAssistant,
|
||||
domain: str,
|
||||
set_position_service: str,
|
||||
close_service: str,
|
||||
open_service: str,
|
||||
set_position_feature: int,
|
||||
attr_position: str,
|
||||
attr_current_position: str,
|
||||
) -> None:
|
||||
"""Test OpenClose trait support."""
|
||||
assert helpers.get_google_type(domain, None) is not None
|
||||
assert trait.OpenCloseTrait.supported(domain, set_position_service, None, None)
|
||||
|
||||
trt = trait.OpenCloseTrait(
|
||||
hass,
|
||||
State(
|
||||
"cover.bla",
|
||||
cover.STATE_OPEN,
|
||||
f"{domain}.bla",
|
||||
"open",
|
||||
{
|
||||
cover.ATTR_CURRENT_POSITION: 75,
|
||||
ATTR_SUPPORTED_FEATURES: CoverEntityFeature.SET_POSITION,
|
||||
attr_current_position: 75,
|
||||
ATTR_SUPPORTED_FEATURES: set_position_feature,
|
||||
},
|
||||
),
|
||||
BASIC_CONFIG,
|
||||
@ -2846,34 +2928,74 @@ async def test_openclose_cover(hass: HomeAssistant) -> None:
|
||||
assert trt.sync_attributes() == {}
|
||||
assert trt.query_attributes() == {"openPercent": 75}
|
||||
|
||||
calls_set = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION)
|
||||
calls_open = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_OPEN_COVER)
|
||||
calls_set = async_mock_service(hass, domain, set_position_service)
|
||||
calls_open = async_mock_service(hass, domain, open_service)
|
||||
calls_close = async_mock_service(hass, domain, close_service)
|
||||
|
||||
await trt.execute(trait.COMMAND_OPENCLOSE, BASIC_DATA, {"openPercent": 50}, {})
|
||||
await trt.execute(
|
||||
trait.COMMAND_OPENCLOSE_RELATIVE, BASIC_DATA, {"openRelativePercent": 50}, {}
|
||||
)
|
||||
assert len(calls_set) == 1
|
||||
assert calls_set[0].data == {ATTR_ENTITY_ID: "cover.bla", cover.ATTR_POSITION: 50}
|
||||
assert calls_set[0].data == {
|
||||
ATTR_ENTITY_ID: f"{domain}.bla",
|
||||
attr_position: 50,
|
||||
}
|
||||
calls_set.pop(0)
|
||||
|
||||
assert len(calls_open) == 1
|
||||
assert calls_open[0].data == {ATTR_ENTITY_ID: "cover.bla"}
|
||||
assert calls_open[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||
calls_open.pop(0)
|
||||
|
||||
assert len(calls_close) == 0
|
||||
|
||||
await trt.execute(trait.COMMAND_OPENCLOSE, BASIC_DATA, {"openPercent": 0}, {})
|
||||
await trt.execute(
|
||||
trait.COMMAND_OPENCLOSE_RELATIVE, BASIC_DATA, {"openRelativePercent": 0}, {}
|
||||
)
|
||||
assert len(calls_set) == 1
|
||||
assert len(calls_close) == 1
|
||||
assert calls_close[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||
assert len(calls_open) == 0
|
||||
|
||||
|
||||
async def test_openclose_cover_unknown_state(hass: HomeAssistant) -> None:
|
||||
"""Test OpenClose trait support for cover domain with unknown state."""
|
||||
assert helpers.get_google_type(cover.DOMAIN, None) is not None
|
||||
@pytest.mark.parametrize(
|
||||
("domain", "open_service", "set_position_feature", "open_feature"),
|
||||
[
|
||||
(
|
||||
cover.DOMAIN,
|
||||
cover.SERVICE_OPEN_COVER,
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
CoverEntityFeature.OPEN,
|
||||
),
|
||||
(
|
||||
valve.DOMAIN,
|
||||
valve.SERVICE_OPEN_VALVE,
|
||||
ValveEntityFeature.SET_POSITION,
|
||||
ValveEntityFeature.OPEN,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_openclose_cover_valve_unknown_state(
|
||||
hass: HomeAssistant,
|
||||
open_service: str,
|
||||
domain: str,
|
||||
set_position_feature: int,
|
||||
open_feature: int,
|
||||
) -> None:
|
||||
"""Test OpenClose trait support with unknown state."""
|
||||
assert helpers.get_google_type(domain, None) is not None
|
||||
assert trait.OpenCloseTrait.supported(
|
||||
cover.DOMAIN, CoverEntityFeature.SET_POSITION, None, None
|
||||
cover.DOMAIN, set_position_feature, None, None
|
||||
)
|
||||
|
||||
# No state
|
||||
trt = trait.OpenCloseTrait(
|
||||
hass,
|
||||
State(
|
||||
"cover.bla",
|
||||
f"{domain}.bla",
|
||||
STATE_UNKNOWN,
|
||||
{ATTR_SUPPORTED_FEATURES: CoverEntityFeature.OPEN},
|
||||
{ATTR_SUPPORTED_FEATURES: open_feature},
|
||||
),
|
||||
BASIC_CONFIG,
|
||||
)
|
||||
@ -2883,30 +3005,51 @@ async def test_openclose_cover_unknown_state(hass: HomeAssistant) -> None:
|
||||
with pytest.raises(helpers.SmartHomeError):
|
||||
trt.query_attributes()
|
||||
|
||||
calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_OPEN_COVER)
|
||||
calls = async_mock_service(hass, domain, open_service)
|
||||
await trt.execute(trait.COMMAND_OPENCLOSE, BASIC_DATA, {"openPercent": 100}, {})
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data == {ATTR_ENTITY_ID: "cover.bla"}
|
||||
assert calls[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||
|
||||
with pytest.raises(helpers.SmartHomeError):
|
||||
trt.query_attributes()
|
||||
|
||||
|
||||
async def test_openclose_cover_assumed_state(hass: HomeAssistant) -> None:
|
||||
"""Test OpenClose trait support for cover domain."""
|
||||
assert helpers.get_google_type(cover.DOMAIN, None) is not None
|
||||
assert trait.OpenCloseTrait.supported(
|
||||
cover.DOMAIN, CoverEntityFeature.SET_POSITION, None, None
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("domain", "set_position_service", "set_position_feature", "state_open"),
|
||||
[
|
||||
(
|
||||
cover.DOMAIN,
|
||||
cover.SERVICE_SET_COVER_POSITION,
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
cover.STATE_OPEN,
|
||||
),
|
||||
(
|
||||
valve.DOMAIN,
|
||||
valve.SERVICE_SET_VALVE_POSITION,
|
||||
ValveEntityFeature.SET_POSITION,
|
||||
valve.STATE_OPEN,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_openclose_cover_valve_assumed_state(
|
||||
hass: HomeAssistant,
|
||||
domain: str,
|
||||
set_position_service: str,
|
||||
set_position_feature: int,
|
||||
state_open: str,
|
||||
) -> None:
|
||||
"""Test OpenClose trait support."""
|
||||
assert helpers.get_google_type(domain, None) is not None
|
||||
assert trait.OpenCloseTrait.supported(domain, set_position_feature, None, None)
|
||||
|
||||
trt = trait.OpenCloseTrait(
|
||||
hass,
|
||||
State(
|
||||
"cover.bla",
|
||||
cover.STATE_OPEN,
|
||||
f"{domain}.bla",
|
||||
state_open,
|
||||
{
|
||||
ATTR_ASSUMED_STATE: True,
|
||||
ATTR_SUPPORTED_FEATURES: CoverEntityFeature.SET_POSITION,
|
||||
ATTR_SUPPORTED_FEATURES: set_position_feature,
|
||||
},
|
||||
),
|
||||
BASIC_CONFIG,
|
||||
@ -2916,20 +3059,37 @@ async def test_openclose_cover_assumed_state(hass: HomeAssistant) -> None:
|
||||
|
||||
assert trt.query_attributes() == {}
|
||||
|
||||
calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION)
|
||||
calls = async_mock_service(hass, domain, set_position_service)
|
||||
await trt.execute(trait.COMMAND_OPENCLOSE, BASIC_DATA, {"openPercent": 40}, {})
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data == {ATTR_ENTITY_ID: "cover.bla", cover.ATTR_POSITION: 40}
|
||||
assert calls[0].data == {ATTR_ENTITY_ID: f"{domain}.bla", cover.ATTR_POSITION: 40}
|
||||
|
||||
|
||||
async def test_openclose_cover_query_only(hass: HomeAssistant) -> None:
|
||||
"""Test OpenClose trait support for cover domain."""
|
||||
assert helpers.get_google_type(cover.DOMAIN, None) is not None
|
||||
assert trait.OpenCloseTrait.supported(cover.DOMAIN, 0, None, None)
|
||||
@pytest.mark.parametrize(
|
||||
("domain", "state_open"),
|
||||
[
|
||||
(
|
||||
cover.DOMAIN,
|
||||
cover.STATE_OPEN,
|
||||
),
|
||||
(
|
||||
valve.DOMAIN,
|
||||
valve.STATE_OPEN,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_openclose_cover_valve_query_only(
|
||||
hass: HomeAssistant,
|
||||
domain: str,
|
||||
state_open: str,
|
||||
) -> None:
|
||||
"""Test OpenClose trait support."""
|
||||
assert helpers.get_google_type(domain, None) is not None
|
||||
assert trait.OpenCloseTrait.supported(domain, 0, None, None)
|
||||
|
||||
state = State(
|
||||
"cover.bla",
|
||||
cover.STATE_OPEN,
|
||||
f"{domain}.bla",
|
||||
state_open,
|
||||
)
|
||||
|
||||
trt = trait.OpenCloseTrait(
|
||||
@ -2945,21 +3105,57 @@ async def test_openclose_cover_query_only(hass: HomeAssistant) -> None:
|
||||
assert trt.query_attributes() == {"openPercent": 100}
|
||||
|
||||
|
||||
async def test_openclose_cover_no_position(hass: HomeAssistant) -> None:
|
||||
"""Test OpenClose trait support for cover domain."""
|
||||
assert helpers.get_google_type(cover.DOMAIN, None) is not None
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"domain",
|
||||
"state_open",
|
||||
"state_closed",
|
||||
"supported_features",
|
||||
"open_service",
|
||||
"close_service",
|
||||
),
|
||||
[
|
||||
(
|
||||
cover.DOMAIN,
|
||||
cover.STATE_OPEN,
|
||||
cover.STATE_CLOSED,
|
||||
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE,
|
||||
cover.SERVICE_OPEN_COVER,
|
||||
cover.SERVICE_CLOSE_COVER,
|
||||
),
|
||||
(
|
||||
valve.DOMAIN,
|
||||
valve.STATE_OPEN,
|
||||
valve.STATE_CLOSED,
|
||||
ValveEntityFeature.OPEN | ValveEntityFeature.CLOSE,
|
||||
valve.SERVICE_OPEN_VALVE,
|
||||
valve.SERVICE_CLOSE_VALVE,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_openclose_cover_valve_no_position(
|
||||
hass: HomeAssistant,
|
||||
domain: str,
|
||||
state_open: str,
|
||||
state_closed: str,
|
||||
supported_features: int,
|
||||
open_service: str,
|
||||
close_service: str,
|
||||
) -> None:
|
||||
"""Test OpenClose trait support."""
|
||||
assert helpers.get_google_type(domain, None) is not None
|
||||
assert trait.OpenCloseTrait.supported(
|
||||
cover.DOMAIN,
|
||||
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE,
|
||||
domain,
|
||||
supported_features,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
state = State(
|
||||
"cover.bla",
|
||||
cover.STATE_OPEN,
|
||||
f"{domain}.bla",
|
||||
state_open,
|
||||
{
|
||||
ATTR_SUPPORTED_FEATURES: CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE,
|
||||
ATTR_SUPPORTED_FEATURES: supported_features,
|
||||
},
|
||||
)
|
||||
|
||||
@ -2972,20 +3168,20 @@ async def test_openclose_cover_no_position(hass: HomeAssistant) -> None:
|
||||
assert trt.sync_attributes() == {"discreteOnlyOpenClose": True}
|
||||
assert trt.query_attributes() == {"openPercent": 100}
|
||||
|
||||
state.state = cover.STATE_CLOSED
|
||||
state.state = state_closed
|
||||
|
||||
assert trt.sync_attributes() == {"discreteOnlyOpenClose": True}
|
||||
assert trt.query_attributes() == {"openPercent": 0}
|
||||
|
||||
calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_CLOSE_COVER)
|
||||
calls = async_mock_service(hass, domain, close_service)
|
||||
await trt.execute(trait.COMMAND_OPENCLOSE, BASIC_DATA, {"openPercent": 0}, {})
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data == {ATTR_ENTITY_ID: "cover.bla"}
|
||||
assert calls[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||
|
||||
calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_OPEN_COVER)
|
||||
calls = async_mock_service(hass, domain, open_service)
|
||||
await trt.execute(trait.COMMAND_OPENCLOSE, BASIC_DATA, {"openPercent": 100}, {})
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data == {ATTR_ENTITY_ID: "cover.bla"}
|
||||
assert calls[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||
|
||||
with pytest.raises(
|
||||
SmartHomeError, match=r"Current position not know for relative command"
|
||||
|
Loading…
x
Reference in New Issue
Block a user