mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
Enable toggle on valve/cover start in google_assistant (#106378)
This commit is contained in:
parent
04a56eaabe
commit
3016dbc2bd
@ -209,6 +209,10 @@ SERVICE_CLOSE_COVER_VALVE = {
|
|||||||
cover.DOMAIN: cover.SERVICE_CLOSE_COVER,
|
cover.DOMAIN: cover.SERVICE_CLOSE_COVER,
|
||||||
valve.DOMAIN: valve.SERVICE_CLOSE_VALVE,
|
valve.DOMAIN: valve.SERVICE_CLOSE_VALVE,
|
||||||
}
|
}
|
||||||
|
SERVICE_TOGGLE_COVER_VALVE = {
|
||||||
|
cover.DOMAIN: cover.SERVICE_TOGGLE,
|
||||||
|
valve.DOMAIN: valve.SERVICE_TOGGLE,
|
||||||
|
}
|
||||||
SERVICE_SET_POSITION_COVER_VALVE = {
|
SERVICE_SET_POSITION_COVER_VALVE = {
|
||||||
cover.DOMAIN: cover.SERVICE_SET_COVER_POSITION,
|
cover.DOMAIN: cover.SERVICE_SET_COVER_POSITION,
|
||||||
valve.DOMAIN: valve.SERVICE_SET_VALVE_POSITION,
|
valve.DOMAIN: valve.SERVICE_SET_VALVE_POSITION,
|
||||||
@ -228,6 +232,10 @@ COVER_VALVE_SET_POSITION_FEATURE = {
|
|||||||
cover.DOMAIN: CoverEntityFeature.SET_POSITION,
|
cover.DOMAIN: CoverEntityFeature.SET_POSITION,
|
||||||
valve.DOMAIN: ValveEntityFeature.SET_POSITION,
|
valve.DOMAIN: ValveEntityFeature.SET_POSITION,
|
||||||
}
|
}
|
||||||
|
COVER_VALVE_STOP_FEATURE = {
|
||||||
|
cover.DOMAIN: CoverEntityFeature.STOP,
|
||||||
|
valve.DOMAIN: ValveEntityFeature.STOP,
|
||||||
|
}
|
||||||
|
|
||||||
COVER_VALVE_DOMAINS = {cover.DOMAIN, valve.DOMAIN}
|
COVER_VALVE_DOMAINS = {cover.DOMAIN, valve.DOMAIN}
|
||||||
|
|
||||||
@ -846,10 +854,10 @@ class StartStopTrait(_Trait):
|
|||||||
if domain == vacuum.DOMAIN:
|
if domain == vacuum.DOMAIN:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if domain == cover.DOMAIN and features & CoverEntityFeature.STOP:
|
if (
|
||||||
return True
|
domain in COVER_VALVE_DOMAINS
|
||||||
|
and features & COVER_VALVE_STOP_FEATURE[domain]
|
||||||
if domain == valve.DOMAIN and features & ValveEntityFeature.STOP:
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -877,10 +885,14 @@ class StartStopTrait(_Trait):
|
|||||||
"isPaused": state == vacuum.STATE_PAUSED,
|
"isPaused": state == vacuum.STATE_PAUSED,
|
||||||
}
|
}
|
||||||
|
|
||||||
if domain == cover.DOMAIN:
|
if domain in COVER_VALVE_DOMAINS:
|
||||||
return {"isRunning": state in (cover.STATE_CLOSING, cover.STATE_OPENING)}
|
return {
|
||||||
if domain == valve.DOMAIN:
|
"isRunning": state
|
||||||
return {"isRunning": True}
|
in (
|
||||||
|
COVER_VALVE_STATES[domain]["closing"],
|
||||||
|
COVER_VALVE_STATES[domain]["opening"],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async def execute(self, command, data, params, challenge):
|
async def execute(self, command, data, params, challenge):
|
||||||
"""Execute a StartStop command."""
|
"""Execute a StartStop command."""
|
||||||
@ -932,15 +944,10 @@ class StartStopTrait(_Trait):
|
|||||||
domain = self.state.domain
|
domain = self.state.domain
|
||||||
if command == COMMAND_STARTSTOP:
|
if command == COMMAND_STARTSTOP:
|
||||||
if params["start"] is False:
|
if params["start"] is False:
|
||||||
if (
|
if self.state.state in (
|
||||||
self.state.state
|
COVER_VALVE_STATES[domain]["closing"],
|
||||||
in (
|
COVER_VALVE_STATES[domain]["opening"],
|
||||||
COVER_VALVE_STATES[domain]["closing"],
|
) or self.state.attributes.get(ATTR_ASSUMED_STATE):
|
||||||
COVER_VALVE_STATES[domain]["opening"],
|
|
||||||
)
|
|
||||||
or domain == valve.DOMAIN
|
|
||||||
or self.state.attributes.get(ATTR_ASSUMED_STATE)
|
|
||||||
):
|
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
domain,
|
domain,
|
||||||
SERVICE_STOP_COVER_VALVE[domain],
|
SERVICE_STOP_COVER_VALVE[domain],
|
||||||
@ -954,8 +961,12 @@ class StartStopTrait(_Trait):
|
|||||||
f"{FRIENDLY_DOMAIN[domain]} is already stopped",
|
f"{FRIENDLY_DOMAIN[domain]} is already stopped",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise SmartHomeError(
|
await self.hass.services.async_call(
|
||||||
ERR_NOT_SUPPORTED, f"Starting a {domain} is not supported"
|
domain,
|
||||||
|
SERVICE_TOGGLE_COVER_VALVE[domain],
|
||||||
|
{ATTR_ENTITY_ID: self.state.entity_id},
|
||||||
|
blocking=not self.config.should_report_state,
|
||||||
|
context=data.context,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise SmartHomeError(
|
raise SmartHomeError(
|
||||||
|
@ -584,17 +584,71 @@ async def test_startstop_vacuum(hass: HomeAssistant) -> None:
|
|||||||
assert unpause_calls[0].data == {ATTR_ENTITY_ID: "vacuum.bla"}
|
assert unpause_calls[0].data == {ATTR_ENTITY_ID: "vacuum.bla"}
|
||||||
|
|
||||||
|
|
||||||
async def test_startstop_cover(hass: HomeAssistant) -> None:
|
@pytest.mark.parametrize(
|
||||||
"""Test startStop trait support for cover domain."""
|
(
|
||||||
assert helpers.get_google_type(cover.DOMAIN, None) is not None
|
"domain",
|
||||||
assert trait.StartStopTrait.supported(
|
"state_open",
|
||||||
cover.DOMAIN, CoverEntityFeature.STOP, None, None
|
"state_closed",
|
||||||
)
|
"state_opening",
|
||||||
|
"state_closing",
|
||||||
|
"supported_features",
|
||||||
|
"service_close",
|
||||||
|
"service_open",
|
||||||
|
"service_stop",
|
||||||
|
"service_toggle",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
cover.DOMAIN,
|
||||||
|
cover.STATE_OPEN,
|
||||||
|
cover.STATE_CLOSED,
|
||||||
|
cover.STATE_OPENING,
|
||||||
|
cover.STATE_CLOSING,
|
||||||
|
CoverEntityFeature.STOP
|
||||||
|
| CoverEntityFeature.OPEN
|
||||||
|
| CoverEntityFeature.CLOSE,
|
||||||
|
cover.SERVICE_OPEN_COVER,
|
||||||
|
cover.SERVICE_CLOSE_COVER,
|
||||||
|
cover.SERVICE_STOP_COVER,
|
||||||
|
cover.SERVICE_TOGGLE,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
valve.DOMAIN,
|
||||||
|
valve.STATE_OPEN,
|
||||||
|
valve.STATE_CLOSED,
|
||||||
|
valve.STATE_OPENING,
|
||||||
|
valve.STATE_CLOSING,
|
||||||
|
ValveEntityFeature.STOP
|
||||||
|
| ValveEntityFeature.OPEN
|
||||||
|
| ValveEntityFeature.CLOSE,
|
||||||
|
valve.SERVICE_OPEN_VALVE,
|
||||||
|
valve.SERVICE_CLOSE_VALVE,
|
||||||
|
valve.SERVICE_STOP_VALVE,
|
||||||
|
cover.SERVICE_TOGGLE,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_startstop_cover_valve(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
domain: str,
|
||||||
|
state_open: str,
|
||||||
|
state_closed: str,
|
||||||
|
state_opening: str,
|
||||||
|
state_closing: str,
|
||||||
|
supported_features: str,
|
||||||
|
service_open: str,
|
||||||
|
service_close: str,
|
||||||
|
service_stop: str,
|
||||||
|
service_toggle: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test startStop trait support."""
|
||||||
|
assert helpers.get_google_type(domain, None) is not None
|
||||||
|
assert trait.StartStopTrait.supported(domain, supported_features, None, None)
|
||||||
|
|
||||||
state = State(
|
state = State(
|
||||||
"cover.bla",
|
f"{domain}.bla",
|
||||||
cover.STATE_CLOSED,
|
state_closed,
|
||||||
{ATTR_SUPPORTED_FEATURES: CoverEntityFeature.STOP},
|
{ATTR_SUPPORTED_FEATURES: supported_features},
|
||||||
)
|
)
|
||||||
|
|
||||||
trt = trait.StartStopTrait(
|
trt = trait.StartStopTrait(
|
||||||
@ -605,25 +659,48 @@ async def test_startstop_cover(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert trt.sync_attributes() == {}
|
assert trt.sync_attributes() == {}
|
||||||
|
|
||||||
for state_value in (cover.STATE_CLOSING, cover.STATE_OPENING):
|
for state_value in (state_closing, state_opening):
|
||||||
state.state = state_value
|
state.state = state_value
|
||||||
assert trt.query_attributes() == {"isRunning": True}
|
assert trt.query_attributes() == {"isRunning": True}
|
||||||
|
|
||||||
stop_calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_STOP_COVER)
|
stop_calls = async_mock_service(hass, domain, service_stop)
|
||||||
|
open_calls = async_mock_service(hass, domain, service_open)
|
||||||
|
close_calls = async_mock_service(hass, domain, service_close)
|
||||||
|
toggle_calls = async_mock_service(hass, domain, service_toggle)
|
||||||
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": False}, {})
|
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": False}, {})
|
||||||
assert len(stop_calls) == 1
|
assert len(stop_calls) == 1
|
||||||
assert stop_calls[0].data == {ATTR_ENTITY_ID: "cover.bla"}
|
assert stop_calls[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||||
|
|
||||||
for state_value in (cover.STATE_CLOSED, cover.STATE_OPEN):
|
for state_value in (state_closed, state_open):
|
||||||
state.state = state_value
|
state.state = state_value
|
||||||
assert trt.query_attributes() == {"isRunning": False}
|
assert trt.query_attributes() == {"isRunning": False}
|
||||||
|
|
||||||
with pytest.raises(SmartHomeError, match="Cover is already stopped"):
|
for state_value in (state_closing, state_opening):
|
||||||
|
state.state = state_value
|
||||||
|
assert trt.query_attributes() == {"isRunning": True}
|
||||||
|
|
||||||
|
state.state = state_open
|
||||||
|
with pytest.raises(
|
||||||
|
SmartHomeError, match=f"{domain.capitalize()} is already stopped"
|
||||||
|
):
|
||||||
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": False}, {})
|
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": False}, {})
|
||||||
|
|
||||||
with pytest.raises(SmartHomeError, match="Starting a cover is not supported"):
|
# Start triggers toggle open
|
||||||
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": True}, {})
|
state.state = state_closed
|
||||||
|
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": True}, {})
|
||||||
|
assert len(open_calls) == 0
|
||||||
|
assert len(close_calls) == 0
|
||||||
|
assert len(toggle_calls) == 1
|
||||||
|
assert toggle_calls[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||||
|
# Second start triggers toggle close
|
||||||
|
state.state = state_open
|
||||||
|
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": True}, {})
|
||||||
|
assert len(open_calls) == 0
|
||||||
|
assert len(close_calls) == 0
|
||||||
|
assert len(toggle_calls) == 2
|
||||||
|
assert toggle_calls[1].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||||
|
|
||||||
|
state.state = state_closed
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
SmartHomeError,
|
SmartHomeError,
|
||||||
match="Command action.devices.commands.PauseUnpause is not supported",
|
match="Command action.devices.commands.PauseUnpause is not supported",
|
||||||
@ -631,67 +708,89 @@ async def test_startstop_cover(hass: HomeAssistant) -> None:
|
|||||||
await trt.execute(trait.COMMAND_PAUSEUNPAUSE, BASIC_DATA, {"start": True}, {})
|
await trt.execute(trait.COMMAND_PAUSEUNPAUSE, BASIC_DATA, {"start": True}, {})
|
||||||
|
|
||||||
|
|
||||||
async def test_startstop_cover_assumed(hass: HomeAssistant) -> None:
|
@pytest.mark.parametrize(
|
||||||
|
(
|
||||||
|
"domain",
|
||||||
|
"state_open",
|
||||||
|
"state_closed",
|
||||||
|
"state_opening",
|
||||||
|
"state_closing",
|
||||||
|
"supported_features",
|
||||||
|
"service_close",
|
||||||
|
"service_open",
|
||||||
|
"service_stop",
|
||||||
|
"service_toggle",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
cover.DOMAIN,
|
||||||
|
cover.STATE_OPEN,
|
||||||
|
cover.STATE_CLOSED,
|
||||||
|
cover.STATE_OPENING,
|
||||||
|
cover.STATE_CLOSING,
|
||||||
|
CoverEntityFeature.STOP
|
||||||
|
| CoverEntityFeature.OPEN
|
||||||
|
| CoverEntityFeature.CLOSE,
|
||||||
|
cover.SERVICE_OPEN_COVER,
|
||||||
|
cover.SERVICE_CLOSE_COVER,
|
||||||
|
cover.SERVICE_STOP_COVER,
|
||||||
|
cover.SERVICE_TOGGLE,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
valve.DOMAIN,
|
||||||
|
valve.STATE_OPEN,
|
||||||
|
valve.STATE_CLOSED,
|
||||||
|
valve.STATE_OPENING,
|
||||||
|
valve.STATE_CLOSING,
|
||||||
|
ValveEntityFeature.STOP
|
||||||
|
| ValveEntityFeature.OPEN
|
||||||
|
| ValveEntityFeature.CLOSE,
|
||||||
|
valve.SERVICE_OPEN_VALVE,
|
||||||
|
valve.SERVICE_CLOSE_VALVE,
|
||||||
|
valve.SERVICE_STOP_VALVE,
|
||||||
|
cover.SERVICE_TOGGLE,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_startstop_cover_valve_assumed(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
domain: str,
|
||||||
|
state_open: str,
|
||||||
|
state_closed: str,
|
||||||
|
state_opening: str,
|
||||||
|
state_closing: str,
|
||||||
|
supported_features: str,
|
||||||
|
service_open: str,
|
||||||
|
service_close: str,
|
||||||
|
service_stop: str,
|
||||||
|
service_toggle: str,
|
||||||
|
) -> None:
|
||||||
"""Test startStop trait support for cover domain of assumed state."""
|
"""Test startStop trait support for cover domain of assumed state."""
|
||||||
trt = trait.StartStopTrait(
|
trt = trait.StartStopTrait(
|
||||||
hass,
|
hass,
|
||||||
State(
|
State(
|
||||||
"cover.bla",
|
f"{domain}.bla",
|
||||||
cover.STATE_CLOSED,
|
state_closed,
|
||||||
{
|
{
|
||||||
ATTR_SUPPORTED_FEATURES: CoverEntityFeature.STOP,
|
ATTR_SUPPORTED_FEATURES: supported_features,
|
||||||
ATTR_ASSUMED_STATE: True,
|
ATTR_ASSUMED_STATE: True,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
BASIC_CONFIG,
|
BASIC_CONFIG,
|
||||||
)
|
)
|
||||||
|
|
||||||
stop_calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_STOP_COVER)
|
stop_calls = async_mock_service(hass, domain, service_stop)
|
||||||
|
toggle_calls = async_mock_service(hass, domain, service_toggle)
|
||||||
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": False}, {})
|
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": False}, {})
|
||||||
assert len(stop_calls) == 1
|
assert len(stop_calls) == 1
|
||||||
assert stop_calls[0].data == {ATTR_ENTITY_ID: "cover.bla"}
|
assert len(toggle_calls) == 0
|
||||||
|
assert stop_calls[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||||
|
|
||||||
|
stop_calls.clear()
|
||||||
async def test_startstop_valve(hass: HomeAssistant) -> None:
|
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {"start": True}, {})
|
||||||
"""Test startStop trait support for valve domain."""
|
assert len(stop_calls) == 0
|
||||||
assert helpers.get_google_type(valve.DOMAIN, None) is not None
|
assert len(toggle_calls) == 1
|
||||||
assert trait.StartStopTrait.supported(
|
assert toggle_calls[0].data == {ATTR_ENTITY_ID: f"{domain}.bla"}
|
||||||
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"]])
|
@pytest.mark.parametrize("supported_color_modes", [["hs"], ["rgb"], ["xy"]])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user