diff --git a/homeassistant/components/google_assistant/const.py b/homeassistant/components/google_assistant/const.py index 70bdc37df66..431433e2bba 100644 --- a/homeassistant/components/google_assistant/const.py +++ b/homeassistant/components/google_assistant/const.py @@ -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, } diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 2e861f16a02..638dfb6eff5 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -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, diff --git a/tests/components/google_assistant/snapshots/test_diagnostics.ambr b/tests/components/google_assistant/snapshots/test_diagnostics.ambr index e29b4d5f487..9a4ad8b3da3 100644 --- a/tests/components/google_assistant/snapshots/test_diagnostics.ambr +++ b/tests/components/google_assistant/snapshots/test_diagnostics.ambr @@ -103,6 +103,7 @@ 'sensor', 'switch', 'vacuum', + 'valve', 'water_heater', ]), 'project_id': '1234', diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index 30a83e7e0c3..3be0030f63e 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -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"