Handle on/off through TemperatrureSetting trait. (#21842)

This commit is contained in:
Penny Wood 2019-03-22 01:57:42 +08:00 committed by Paulus Schoutsen
parent fe468ace34
commit 81bb928394
4 changed files with 93 additions and 58 deletions

View File

@ -21,6 +21,7 @@ from homeassistant.const import (
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 @@ class TemperatureSettingTrait(_Trait):
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 @@ class TemperatureSettingTrait(_Trait):
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 @@ class TemperatureSettingTrait(_Trait):
}, 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

View File

@ -231,10 +231,7 @@ DEMO_DEVICES = [{
'name': {
'name': 'HeatPump'
},
'traits': [
'action.devices.traits.OnOff',
'action.devices.traits.TemperatureSetting'
],
'traits': ['action.devices.traits.TemperatureSetting'],
'type': 'action.devices.types.THERMOSTAT',
'willReportState': False
}, {

View File

@ -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,

View File

@ -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() == {