@@ -693,7 +693,7 @@ async def test_startstop_lawn_mower(hass: HomeAssistant) -> None:
693693 ),
694694 ],
695695)
696- async def test_startstop_cover_valve (
696+ async def test_startstop_cover_valve_no_assumed_state (
697697 hass : HomeAssistant ,
698698 domain : str ,
699699 state_open : str ,
@@ -706,14 +706,14 @@ async def test_startstop_cover_valve(
706706 service_stop : str ,
707707 service_toggle : str ,
708708) -> None :
709- """Test startStop trait support."""
709+ """Test startStop trait support and no assumed state ."""
710710 assert helpers .get_google_type (domain , None ) is not None
711711 assert trait .StartStopTrait .supported (domain , supported_features , None , None )
712712
713713 state = State (
714714 f"{ domain } .bla" ,
715715 state_closed ,
716- {ATTR_SUPPORTED_FEATURES : supported_features },
716+ {ATTR_SUPPORTED_FEATURES : supported_features , ATTR_ASSUMED_STATE : False },
717717 )
718718
719719 trt = trait .StartStopTrait (
@@ -773,6 +773,168 @@ async def test_startstop_cover_valve(
773773 await trt .execute (trait .COMMAND_PAUSE_UNPAUSE , BASIC_DATA , {"start" : True }, {})
774774
775775
776+ @pytest .mark .parametrize (
777+ (
778+ "domain" ,
779+ "state_open" ,
780+ "state_closed" ,
781+ "state_opening" ,
782+ "state_closing" ,
783+ "supported_features" ,
784+ "service_close" ,
785+ "service_open" ,
786+ "service_stop" ,
787+ "service_toggle" ,
788+ "assumed_state" ,
789+ ),
790+ [
791+ (
792+ cover .DOMAIN ,
793+ cover .CoverState .OPEN ,
794+ cover .CoverState .CLOSED ,
795+ cover .CoverState .OPENING ,
796+ cover .CoverState .CLOSING ,
797+ CoverEntityFeature .STOP
798+ | CoverEntityFeature .OPEN
799+ | CoverEntityFeature .CLOSE ,
800+ cover .SERVICE_OPEN_COVER ,
801+ cover .SERVICE_CLOSE_COVER ,
802+ cover .SERVICE_STOP_COVER ,
803+ cover .SERVICE_TOGGLE ,
804+ True ,
805+ ),
806+ (
807+ valve .DOMAIN ,
808+ valve .ValveState .OPEN ,
809+ valve .ValveState .CLOSED ,
810+ valve .ValveState .OPENING ,
811+ valve .ValveState .CLOSING ,
812+ ValveEntityFeature .STOP
813+ | ValveEntityFeature .OPEN
814+ | ValveEntityFeature .CLOSE ,
815+ valve .SERVICE_OPEN_VALVE ,
816+ valve .SERVICE_CLOSE_VALVE ,
817+ valve .SERVICE_STOP_VALVE ,
818+ cover .SERVICE_TOGGLE ,
819+ True ,
820+ ),
821+ (
822+ cover .DOMAIN ,
823+ cover .CoverState .OPEN ,
824+ cover .CoverState .CLOSED ,
825+ cover .CoverState .OPENING ,
826+ cover .CoverState .CLOSING ,
827+ CoverEntityFeature .STOP
828+ | CoverEntityFeature .OPEN
829+ | CoverEntityFeature .CLOSE
830+ | CoverEntityFeature .SET_POSITION ,
831+ cover .SERVICE_OPEN_COVER ,
832+ cover .SERVICE_CLOSE_COVER ,
833+ cover .SERVICE_STOP_COVER ,
834+ cover .SERVICE_TOGGLE ,
835+ False ,
836+ ),
837+ (
838+ valve .DOMAIN ,
839+ valve .ValveState .OPEN ,
840+ valve .ValveState .CLOSED ,
841+ valve .ValveState .OPENING ,
842+ valve .ValveState .CLOSING ,
843+ ValveEntityFeature .STOP
844+ | ValveEntityFeature .OPEN
845+ | ValveEntityFeature .CLOSE
846+ | ValveEntityFeature .SET_POSITION ,
847+ valve .SERVICE_OPEN_VALVE ,
848+ valve .SERVICE_CLOSE_VALVE ,
849+ valve .SERVICE_STOP_VALVE ,
850+ cover .SERVICE_TOGGLE ,
851+ False ,
852+ ),
853+ ],
854+ )
855+ async def test_startstop_cover_valve_with_assumed_state_or_reports_position (
856+ hass : HomeAssistant ,
857+ domain : str ,
858+ state_open : str ,
859+ state_closed : str ,
860+ state_opening : str ,
861+ state_closing : str ,
862+ supported_features : str ,
863+ service_open : str ,
864+ service_close : str ,
865+ service_stop : str ,
866+ service_toggle : str ,
867+ assumed_state : bool ,
868+ ) -> None :
869+ """Test startStop trait support without an assumed state or reporting position."""
870+ assert helpers .get_google_type (domain , None ) is not None
871+ assert trait .StartStopTrait .supported (domain , supported_features , None , None )
872+
873+ state = State (
874+ f"{ domain } .bla" ,
875+ state_closed ,
876+ {
877+ ATTR_SUPPORTED_FEATURES : supported_features ,
878+ ATTR_ASSUMED_STATE : assumed_state ,
879+ },
880+ )
881+
882+ trt = trait .StartStopTrait (
883+ hass ,
884+ state ,
885+ BASIC_CONFIG ,
886+ )
887+
888+ assert trt .sync_attributes () == {}
889+
890+ for state_value in (state_closing , state_opening ):
891+ state .state = state_value
892+ assert trt .query_attributes ()["isRunning" ] is True
893+
894+ stop_calls = async_mock_service (hass , domain , service_stop )
895+ open_calls = async_mock_service (hass , domain , service_open )
896+ close_calls = async_mock_service (hass , domain , service_close )
897+ toggle_calls = async_mock_service (hass , domain , service_toggle )
898+ await trt .execute (trait .COMMAND_START_STOP , BASIC_DATA , {"start" : False }, {})
899+ assert len (stop_calls ) == 1
900+ assert stop_calls [0 ].data == {ATTR_ENTITY_ID : f"{ domain } .bla" }
901+
902+ # Trait attr isRunning always returns True,
903+ # so the cover or valve can always be stopped
904+ for state_value in (state_closing , state_opening , state_closed , state_open ):
905+ state .state = state_value
906+ assert trt .query_attributes ()["isRunning" ] is True
907+
908+ state .state = state_open
909+
910+ # Stop does not raise because we assume the state
911+ # or the position is reported
912+ await trt .execute (trait .COMMAND_START_STOP , BASIC_DATA , {"start" : False }, {})
913+ assert len (stop_calls ) == 2
914+
915+ # Start triggers toggle open
916+ state .state = state_closed
917+ await trt .execute (trait .COMMAND_START_STOP , BASIC_DATA , {"start" : True }, {})
918+ assert len (open_calls ) == 0
919+ assert len (close_calls ) == 0
920+ assert len (toggle_calls ) == 1
921+ assert toggle_calls [0 ].data == {ATTR_ENTITY_ID : f"{ domain } .bla" }
922+ # Second start triggers toggle close
923+ state .state = state_open
924+ await trt .execute (trait .COMMAND_START_STOP , BASIC_DATA , {"start" : True }, {})
925+ assert len (open_calls ) == 0
926+ assert len (close_calls ) == 0
927+ assert len (toggle_calls ) == 2
928+ assert toggle_calls [1 ].data == {ATTR_ENTITY_ID : f"{ domain } .bla" }
929+
930+ state .state = state_closed
931+ with pytest .raises (
932+ SmartHomeError ,
933+ match = "Command action.devices.commands.PauseUnpause is not supported" ,
934+ ):
935+ await trt .execute (trait .COMMAND_PAUSE_UNPAUSE , BASIC_DATA , {"start" : True }, {})
936+
937+
776938@pytest .mark .parametrize (
777939 (
778940 "domain" ,
0 commit comments