From 8588d1669bbbe38a137f6ed3510a6eb867ef4624 Mon Sep 17 00:00:00 2001 From: Swamp-Ig Date: Sat, 9 Mar 2019 08:38:05 +0800 Subject: [PATCH] Handle on/off through TemperatrureSetting trait.] --- .../components/google_assistant/trait.py | 56 +++++++++--- tests/components/google_assistant/__init__.py | 5 +- .../google_assistant/test_google_assistant.py | 2 - .../components/google_assistant/test_trait.py | 88 +++++++++++-------- 4 files changed, 93 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index aff24f3051257f..f5b959285db4fe 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -21,6 +21,7 @@ SERVICE_TURN_ON, STATE_LOCKED, STATE_OFF, + STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, @@ -199,8 +200,6 @@ class OnOffTrait(_Trait): @staticmethod def supported(domain, features): """Test if state is supported.""" - if domain == climate.DOMAIN: - return features & climate.SUPPORT_ON_OFF != 0 return domain in ( group.DOMAIN, input_boolean.DOMAIN, @@ -537,10 +536,18 @@ def supported(domain, features): def sync_attributes(self): """Return temperature point and modes attributes for a sync request.""" modes = [] - for mode in self.state.attributes.get(climate.ATTR_OPERATION_LIST, []): - google_mode = self.hass_to_google.get(mode) - if google_mode is not None: - modes.append(google_mode) + supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) + + if supported & climate.SUPPORT_ON_OFF != 0: + modes.append(STATE_OFF) + modes.append(STATE_ON) + + if supported & climate.SUPPORT_OPERATION_MODE != 0: + for mode in self.state.attributes.get(climate.ATTR_OPERATION_LIST, + []): + google_mode = self.hass_to_google.get(mode) + if google_mode and google_mode not in modes: + modes.append(google_mode) return { 'availableThermostatModes': ','.join(modes), @@ -554,8 +561,16 @@ def query_attributes(self): response = {} operation = attrs.get(climate.ATTR_OPERATION_MODE) - if operation is not None and operation in self.hass_to_google: + supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) + + if (supported & climate.SUPPORT_ON_OFF + and self.state.state == STATE_OFF): + response['thermostatMode'] = 'off' + elif (supported & climate.SUPPORT_OPERATION_MODE and + operation in self.hass_to_google): response['thermostatMode'] = self.hass_to_google[operation] + elif supported & climate.SUPPORT_ON_OFF: + response['thermostatMode'] = 'on' unit = self.hass.config.units.temperature_unit @@ -644,12 +659,27 @@ async def execute(self, command, data, params): }, blocking=True, context=data.context) elif command == COMMAND_THERMOSTAT_SET_MODE: - await self.hass.services.async_call( - climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE, { - ATTR_ENTITY_ID: self.state.entity_id, - climate.ATTR_OPERATION_MODE: - self.google_to_hass[params['thermostatMode']], - }, blocking=True, context=data.context) + target_mode = params['thermostatMode'] + supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) + + if (target_mode in [STATE_ON, STATE_OFF] and + supported & climate.SUPPORT_ON_OFF): + await self.hass.services.async_call( + climate.DOMAIN, + (SERVICE_TURN_ON + if target_mode == STATE_ON + else SERVICE_TURN_OFF), + { + ATTR_ENTITY_ID: self.state.entity_id, + climate.ATTR_OPERATION_MODE: target_mode, + }, blocking=True, context=data.context) + elif supported & climate.SUPPORT_OPERATION_MODE: + await self.hass.services.async_call( + climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE, { + ATTR_ENTITY_ID: self.state.entity_id, + climate.ATTR_OPERATION_MODE: + self.google_to_hass[target_mode], + }, blocking=True, context=data.context) @register_trait diff --git a/tests/components/google_assistant/__init__.py b/tests/components/google_assistant/__init__.py index 03cc327a5c51f7..a8ea4a3f8881d0 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -231,10 +231,7 @@ 'name': { 'name': 'HeatPump' }, - 'traits': [ - 'action.devices.traits.OnOff', - 'action.devices.traits.TemperatureSetting' - ], + 'traits': ['action.devices.traits.TemperatureSetting'], 'type': 'action.devices.types.THERMOSTAT', 'willReportState': False }, { diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index 18f99c8268524a..fbaf1b47898528 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -205,7 +205,6 @@ def test_query_climate_request(hass_fixture, assistant_client, auth_header): devices = body['payload']['devices'] assert len(devices) == 3 assert devices['climate.heatpump'] == { - 'on': True, 'online': True, 'thermostatTemperatureSetpoint': 20.0, 'thermostatTemperatureAmbient': 25.0, @@ -262,7 +261,6 @@ def test_query_climate_request_f(hass_fixture, assistant_client, auth_header): devices = body['payload']['devices'] assert len(devices) == 3 assert devices['climate.heatpump'] == { - 'on': True, 'online': True, 'thermostatTemperatureSetpoint': -6.7, 'thermostatTemperatureAmbient': -3.9, diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index 301de9c8c256b8..3af60e2f014c0e 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -408,44 +408,9 @@ async def test_onoff_media_player(hass): async def test_onoff_climate(hass): - """Test OnOff trait support for climate domain.""" - assert trait.OnOffTrait.supported(climate.DOMAIN, climate.SUPPORT_ON_OFF) - - trt_on = trait.OnOffTrait(hass, State('climate.bla', STATE_ON), - BASIC_CONFIG) - - assert trt_on.sync_attributes() == {} - - assert trt_on.query_attributes() == { - 'on': True - } - - trt_off = trait.OnOffTrait(hass, State('climate.bla', STATE_OFF), - BASIC_CONFIG) - - assert trt_off.query_attributes() == { - 'on': False - } - - on_calls = async_mock_service(hass, climate.DOMAIN, SERVICE_TURN_ON) - await trt_on.execute( - trait.COMMAND_ONOFF, BASIC_DATA, - {'on': True}) - assert len(on_calls) == 1 - assert on_calls[0].data == { - ATTR_ENTITY_ID: 'climate.bla', - } - - off_calls = async_mock_service(hass, climate.DOMAIN, - SERVICE_TURN_OFF) - - await trt_on.execute( - trait.COMMAND_ONOFF, BASIC_DATA, - {'on': False}) - assert len(off_calls) == 1 - assert off_calls[0].data == { - ATTR_ENTITY_ID: 'climate.bla', - } + """Test OnOff trait not supported for climate domain.""" + assert not trait.OnOffTrait.supported( + climate.DOMAIN, climate.SUPPORT_ON_OFF) async def test_dock_vacuum(hass): @@ -673,6 +638,48 @@ async def test_scene_script(hass): } +async def test_temperature_setting_climate_onoff(hass): + """Test TemperatureSetting trait support for climate domain - range.""" + assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0) + assert trait.TemperatureSettingTrait.supported( + climate.DOMAIN, climate.SUPPORT_OPERATION_MODE) + + hass.config.units.temperature_unit = TEMP_FAHRENHEIT + + trt = trait.TemperatureSettingTrait(hass, State( + 'climate.bla', climate.STATE_AUTO, { + ATTR_SUPPORTED_FEATURES: ( + climate.SUPPORT_OPERATION_MODE | climate.SUPPORT_ON_OFF), + climate.ATTR_OPERATION_MODE: climate.STATE_COOL, + climate.ATTR_OPERATION_LIST: [ + climate.STATE_COOL, + climate.STATE_HEAT, + climate.STATE_AUTO, + ], + climate.ATTR_MIN_TEMP: None, + climate.ATTR_MAX_TEMP: None, + }), BASIC_CONFIG) + assert trt.sync_attributes() == { + 'availableThermostatModes': 'off,on,cool,heat,heatcool', + 'thermostatTemperatureUnit': 'F', + } + assert trt.can_execute(trait.COMMAND_THERMOSTAT_SET_MODE, {}) + + calls = async_mock_service( + hass, climate.DOMAIN, SERVICE_TURN_ON) + await trt.execute(trait.COMMAND_THERMOSTAT_SET_MODE, BASIC_DATA, { + 'thermostatMode': 'on', + }) + assert len(calls) == 1 + + calls = async_mock_service( + hass, climate.DOMAIN, SERVICE_TURN_OFF) + await trt.execute(trait.COMMAND_THERMOSTAT_SET_MODE, BASIC_DATA, { + 'thermostatMode': 'off', + }) + assert len(calls) == 1 + + async def test_temperature_setting_climate_range(hass): """Test TemperatureSetting trait support for climate domain - range.""" assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0) @@ -685,6 +692,7 @@ async def test_temperature_setting_climate_range(hass): 'climate.bla', climate.STATE_AUTO, { climate.ATTR_CURRENT_TEMPERATURE: 70, climate.ATTR_CURRENT_HUMIDITY: 25, + ATTR_SUPPORTED_FEATURES: climate.SUPPORT_OPERATION_MODE, climate.ATTR_OPERATION_MODE: climate.STATE_AUTO, climate.ATTR_OPERATION_LIST: [ STATE_OFF, @@ -755,6 +763,8 @@ async def test_temperature_setting_climate_setpoint(hass): trt = trait.TemperatureSettingTrait(hass, State( 'climate.bla', climate.STATE_AUTO, { + ATTR_SUPPORTED_FEATURES: ( + climate.SUPPORT_OPERATION_MODE | climate.SUPPORT_ON_OFF), climate.ATTR_OPERATION_MODE: climate.STATE_COOL, climate.ATTR_OPERATION_LIST: [ STATE_OFF, @@ -766,7 +776,7 @@ async def test_temperature_setting_climate_setpoint(hass): climate.ATTR_CURRENT_TEMPERATURE: 20 }), BASIC_CONFIG) assert trt.sync_attributes() == { - 'availableThermostatModes': 'off,cool', + 'availableThermostatModes': 'off,on,cool', 'thermostatTemperatureUnit': 'C', } assert trt.query_attributes() == {