Add support to climate devices in Google Assistant Fan Trait (#38337)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
pull/38447/head
Marcio Granzotto Rodrigues 2020-07-31 23:05:00 -03:00 committed by GitHub
parent 2e340d2c2f
commit 416ee7f143
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 137 additions and 34 deletions

View File

@ -1153,55 +1153,89 @@ class FanSpeedTrait(_Trait):
@staticmethod
def supported(domain, features, device_class):
"""Test if state is supported."""
if domain != fan.DOMAIN:
return False
return features & fan.SUPPORT_SET_SPEED
if domain == fan.DOMAIN:
return features & fan.SUPPORT_SET_SPEED
if domain == climate.DOMAIN:
return features & climate.SUPPORT_FAN_MODE
return False
def sync_attributes(self):
"""Return speed point and modes attributes for a sync request."""
modes = self.state.attributes.get(fan.ATTR_SPEED_LIST, [])
domain = self.state.domain
speeds = []
for mode in modes:
if mode not in self.speed_synonyms:
continue
speed = {
"speed_name": mode,
"speed_values": [
{"speed_synonym": self.speed_synonyms.get(mode), "lang": "en"}
],
}
speeds.append(speed)
reversible = False
if domain == fan.DOMAIN:
modes = self.state.attributes.get(fan.ATTR_SPEED_LIST, [])
for mode in modes:
if mode not in self.speed_synonyms:
continue
speed = {
"speed_name": mode,
"speed_values": [
{"speed_synonym": self.speed_synonyms.get(mode), "lang": "en"}
],
}
speeds.append(speed)
reversible = bool(
self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
& fan.SUPPORT_DIRECTION
)
elif domain == climate.DOMAIN:
modes = self.state.attributes.get(climate.ATTR_FAN_MODES, [])
for mode in modes:
speed = {
"speed_name": mode,
"speed_values": [{"speed_synonym": [mode], "lang": "en"}],
}
speeds.append(speed)
return {
"availableFanSpeeds": {"speeds": speeds, "ordered": True},
"reversible": bool(
self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
& fan.SUPPORT_DIRECTION
),
"reversible": reversible,
}
def query_attributes(self):
"""Return speed point and modes query attributes."""
attrs = self.state.attributes
domain = self.state.domain
response = {}
speed = attrs.get(fan.ATTR_SPEED)
if speed is not None:
response["on"] = speed != fan.SPEED_OFF
response["currentFanSpeedSetting"] = speed
if domain == climate.DOMAIN:
speed = attrs.get(climate.ATTR_FAN_MODE)
if speed is not None:
response["currentFanSpeedSetting"] = speed
if domain == fan.DOMAIN:
speed = attrs.get(fan.ATTR_SPEED)
if speed is not None:
response["on"] = speed != fan.SPEED_OFF
response["currentFanSpeedSetting"] = speed
return response
async def execute(self, command, data, params, challenge):
"""Execute an SetFanSpeed command."""
await self.hass.services.async_call(
fan.DOMAIN,
fan.SERVICE_SET_SPEED,
{ATTR_ENTITY_ID: self.state.entity_id, fan.ATTR_SPEED: params["fanSpeed"]},
blocking=True,
context=data.context,
)
domain = self.state.domain
if domain == climate.DOMAIN:
await self.hass.services.async_call(
climate.DOMAIN,
climate.SERVICE_SET_FAN_MODE,
{
ATTR_ENTITY_ID: self.state.entity_id,
climate.ATTR_FAN_MODE: params["fanSpeed"],
},
blocking=True,
context=data.context,
)
if domain == fan.DOMAIN:
await self.hass.services.async_call(
fan.DOMAIN,
fan.SERVICE_SET_SPEED,
{
ATTR_ENTITY_ID: self.state.entity_id,
fan.ATTR_SPEED: params["fanSpeed"],
},
blocking=True,
context=data.context,
)
@register_trait

View File

@ -229,7 +229,10 @@ DEMO_DEVICES = [
{
"id": "climate.hvac",
"name": {"name": "Hvac"},
"traits": ["action.devices.traits.TemperatureSetting"],
"traits": [
"action.devices.traits.TemperatureSetting",
"action.devices.traits.FanSpeed",
],
"type": "action.devices.types.THERMOSTAT",
"willReportState": False,
"attributes": {
@ -247,7 +250,10 @@ DEMO_DEVICES = [
{
"id": "climate.ecobee",
"name": {"name": "Ecobee"},
"traits": ["action.devices.traits.TemperatureSetting"],
"traits": [
"action.devices.traits.TemperatureSetting",
"action.devices.traits.FanSpeed",
],
"type": "action.devices.types.THERMOSTAT",
"willReportState": False,
},

View File

@ -231,6 +231,7 @@ async def test_query_climate_request(hass_fixture, assistant_client, auth_header
"thermostatTemperatureAmbient": 23,
"thermostatMode": "heatcool",
"thermostatTemperatureSetpointLow": 21,
"currentFanSpeedSetting": "Auto Low",
}
assert devices["climate.hvac"] == {
"online": True,
@ -238,6 +239,7 @@ async def test_query_climate_request(hass_fixture, assistant_client, auth_header
"thermostatTemperatureAmbient": 22,
"thermostatMode": "cool",
"thermostatHumidityAmbient": 54,
"currentFanSpeedSetting": "On High",
}
@ -288,6 +290,7 @@ async def test_query_climate_request_f(hass_fixture, assistant_client, auth_head
"thermostatTemperatureAmbient": -5,
"thermostatMode": "heatcool",
"thermostatTemperatureSetpointLow": -6.1,
"currentFanSpeedSetting": "Auto Low",
}
assert devices["climate.hvac"] == {
"online": True,
@ -295,6 +298,7 @@ async def test_query_climate_request_f(hass_fixture, assistant_client, auth_head
"thermostatTemperatureAmbient": -5.6,
"thermostatMode": "cool",
"thermostatHumidityAmbient": 54,
"currentFanSpeedSetting": "On High",
}
hass_fixture.config.units.temperature_unit = const.TEMP_CELSIUS

View File

@ -1313,6 +1313,65 @@ async def test_fan_speed(hass):
assert calls[0].data == {"entity_id": "fan.living_room_fan", "speed": "medium"}
async def test_climate_fan_speed(hass):
"""Test FanSpeed trait speed control support for climate domain."""
assert helpers.get_google_type(climate.DOMAIN, None) is not None
assert trait.FanSpeedTrait.supported(climate.DOMAIN, climate.SUPPORT_FAN_MODE, None)
trt = trait.FanSpeedTrait(
hass,
State(
"climate.living_room_ac",
"on",
attributes={
"fan_modes": ["auto", "low", "medium", "high"],
"fan_mode": "low",
},
),
BASIC_CONFIG,
)
assert trt.sync_attributes() == {
"availableFanSpeeds": {
"ordered": True,
"speeds": [
{
"speed_name": "auto",
"speed_values": [{"speed_synonym": ["auto"], "lang": "en"}],
},
{
"speed_name": "low",
"speed_values": [{"speed_synonym": ["low"], "lang": "en"}],
},
{
"speed_name": "medium",
"speed_values": [{"speed_synonym": ["medium"], "lang": "en"}],
},
{
"speed_name": "high",
"speed_values": [{"speed_synonym": ["high"], "lang": "en"}],
},
],
},
"reversible": False,
}
assert trt.query_attributes() == {
"currentFanSpeedSetting": "low",
}
assert trt.can_execute(trait.COMMAND_FANSPEED, params={"fanSpeed": "medium"})
calls = async_mock_service(hass, climate.DOMAIN, climate.SERVICE_SET_FAN_MODE)
await trt.execute(trait.COMMAND_FANSPEED, BASIC_DATA, {"fanSpeed": "medium"}, {})
assert len(calls) == 1
assert calls[0].data == {
"entity_id": "climate.living_room_ac",
"fan_mode": "medium",
}
async def test_inputselector(hass):
"""Test input selector trait."""
assert helpers.get_google_type(media_player.DOMAIN, None) is not None