mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 09:47:13 +00:00
Google assistant enable fan speed controls (#18373)
* Initial commit of Traits changes. * Initial commit of tests chagnes for added FanSpeed trait. * pylint fixes. * Default reversible to false * Ensure reversible returns True/False. * Fix FanSpeed trait name and fix order. * Add remaining checks to FanSpeed trait Test. * Remove un-needed blank lines at EOF. * Update homeassistant/components/google_assistant/trait.py Co-Authored-By: marchingphoenix <eanagley@gmail.com> * use fan.SPEED_* constants as keys to speed_synonyms dict. convert True if() to bool() for reversible assignment. * use fan.SPEED_OFF constant of 'on' check.
This commit is contained in:
parent
5129a48750
commit
ddeeba20b9
@ -1,7 +1,6 @@
|
|||||||
"""Implement the Smart Home traits."""
|
"""Implement the Smart Home traits."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.core import DOMAIN as HA_DOMAIN
|
|
||||||
from homeassistant.components import (
|
from homeassistant.components import (
|
||||||
climate,
|
climate,
|
||||||
cover,
|
cover,
|
||||||
@ -26,8 +25,8 @@ from homeassistant.const import (
|
|||||||
TEMP_FAHRENHEIT,
|
TEMP_FAHRENHEIT,
|
||||||
ATTR_SUPPORTED_FEATURES,
|
ATTR_SUPPORTED_FEATURES,
|
||||||
)
|
)
|
||||||
|
from homeassistant.core import DOMAIN as HA_DOMAIN
|
||||||
from homeassistant.util import color as color_util, temperature as temp_util
|
from homeassistant.util import color as color_util, temperature as temp_util
|
||||||
|
|
||||||
from .const import ERR_VALUE_OUT_OF_RANGE
|
from .const import ERR_VALUE_OUT_OF_RANGE
|
||||||
from .helpers import SmartHomeError
|
from .helpers import SmartHomeError
|
||||||
|
|
||||||
@ -43,6 +42,7 @@ TRAIT_COLOR_TEMP = PREFIX_TRAITS + 'ColorTemperature'
|
|||||||
TRAIT_SCENE = PREFIX_TRAITS + 'Scene'
|
TRAIT_SCENE = PREFIX_TRAITS + 'Scene'
|
||||||
TRAIT_TEMPERATURE_SETTING = PREFIX_TRAITS + 'TemperatureSetting'
|
TRAIT_TEMPERATURE_SETTING = PREFIX_TRAITS + 'TemperatureSetting'
|
||||||
TRAIT_LOCKUNLOCK = PREFIX_TRAITS + 'LockUnlock'
|
TRAIT_LOCKUNLOCK = PREFIX_TRAITS + 'LockUnlock'
|
||||||
|
TRAIT_FANSPEED = PREFIX_TRAITS + 'FanSpeed'
|
||||||
|
|
||||||
PREFIX_COMMANDS = 'action.devices.commands.'
|
PREFIX_COMMANDS = 'action.devices.commands.'
|
||||||
COMMAND_ONOFF = PREFIX_COMMANDS + 'OnOff'
|
COMMAND_ONOFF = PREFIX_COMMANDS + 'OnOff'
|
||||||
@ -58,6 +58,7 @@ COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE = (
|
|||||||
PREFIX_COMMANDS + 'ThermostatTemperatureSetRange')
|
PREFIX_COMMANDS + 'ThermostatTemperatureSetRange')
|
||||||
COMMAND_THERMOSTAT_SET_MODE = PREFIX_COMMANDS + 'ThermostatSetMode'
|
COMMAND_THERMOSTAT_SET_MODE = PREFIX_COMMANDS + 'ThermostatSetMode'
|
||||||
COMMAND_LOCKUNLOCK = PREFIX_COMMANDS + 'LockUnlock'
|
COMMAND_LOCKUNLOCK = PREFIX_COMMANDS + 'LockUnlock'
|
||||||
|
COMMAND_FANSPEED = PREFIX_COMMANDS + 'SetFanSpeed'
|
||||||
|
|
||||||
|
|
||||||
TRAITS = []
|
TRAITS = []
|
||||||
@ -675,3 +676,77 @@ class LockUnlockTrait(_Trait):
|
|||||||
await self.hass.services.async_call(lock.DOMAIN, service, {
|
await self.hass.services.async_call(lock.DOMAIN, service, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=True)
|
}, blocking=True)
|
||||||
|
|
||||||
|
|
||||||
|
@register_trait
|
||||||
|
class FanSpeedTrait(_Trait):
|
||||||
|
"""Trait to control speed of Fan.
|
||||||
|
|
||||||
|
https://developers.google.com/actions/smarthome/traits/fanspeed
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = TRAIT_FANSPEED
|
||||||
|
commands = [
|
||||||
|
COMMAND_FANSPEED
|
||||||
|
]
|
||||||
|
|
||||||
|
speed_synonyms = {
|
||||||
|
fan.SPEED_OFF: ['stop', 'off'],
|
||||||
|
fan.SPEED_LOW: ['slow', 'low', 'slowest', 'lowest'],
|
||||||
|
fan.SPEED_MEDIUM: ['medium', 'mid', 'middle'],
|
||||||
|
fan.SPEED_HIGH: [
|
||||||
|
'high', 'max', 'fast', 'highest', 'fastest', 'maximum'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def supported(domain, features):
|
||||||
|
"""Test if state is supported."""
|
||||||
|
if domain != fan.DOMAIN:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return features & fan.SUPPORT_SET_SPEED
|
||||||
|
|
||||||
|
def sync_attributes(self):
|
||||||
|
"""Return speed point and modes attributes for a sync request."""
|
||||||
|
modes = self.state.attributes.get(fan.ATTR_SPEED_LIST, [])
|
||||||
|
speeds = []
|
||||||
|
for mode in modes:
|
||||||
|
speed = {
|
||||||
|
"speed_name": mode,
|
||||||
|
"speed_values": [{
|
||||||
|
"speed_synonym": self.speed_synonyms.get(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
def query_attributes(self):
|
||||||
|
"""Return speed point and modes query attributes."""
|
||||||
|
attrs = self.state.attributes
|
||||||
|
response = {}
|
||||||
|
|
||||||
|
speed = attrs.get(fan.ATTR_SPEED)
|
||||||
|
if speed is not None:
|
||||||
|
response['on'] = speed != fan.SPEED_OFF
|
||||||
|
response['online'] = True
|
||||||
|
response['currentFanSpeedSetting'] = speed
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
async def execute(self, command, params):
|
||||||
|
"""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)
|
||||||
|
@ -183,7 +183,10 @@ DEMO_DEVICES = [{
|
|||||||
'name': {
|
'name': {
|
||||||
'name': 'Living Room Fan'
|
'name': 'Living Room Fan'
|
||||||
},
|
},
|
||||||
'traits': ['action.devices.traits.OnOff'],
|
'traits': [
|
||||||
|
'action.devices.traits.FanSpeed',
|
||||||
|
'action.devices.traits.OnOff'
|
||||||
|
],
|
||||||
'type': 'action.devices.types.FAN',
|
'type': 'action.devices.types.FAN',
|
||||||
'willReportState': False
|
'willReportState': False
|
||||||
}, {
|
}, {
|
||||||
@ -191,7 +194,10 @@ DEMO_DEVICES = [{
|
|||||||
'name': {
|
'name': {
|
||||||
'name': 'Ceiling Fan'
|
'name': 'Ceiling Fan'
|
||||||
},
|
},
|
||||||
'traits': ['action.devices.traits.OnOff'],
|
'traits': [
|
||||||
|
'action.devices.traits.FanSpeed',
|
||||||
|
'action.devices.traits.OnOff'
|
||||||
|
],
|
||||||
'type': 'action.devices.types.FAN',
|
'type': 'action.devices.types.FAN',
|
||||||
'willReportState': False
|
'willReportState': False
|
||||||
}, {
|
}, {
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
"""Tests for the Google Assistant traits."""
|
"""Tests for the Google Assistant traits."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.const import (
|
|
||||||
STATE_ON, STATE_OFF, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
|
||||||
TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES)
|
|
||||||
from homeassistant.core import State, DOMAIN as HA_DOMAIN
|
|
||||||
from homeassistant.components import (
|
from homeassistant.components import (
|
||||||
climate,
|
climate,
|
||||||
cover,
|
cover,
|
||||||
@ -20,8 +16,11 @@ from homeassistant.components import (
|
|||||||
group,
|
group,
|
||||||
)
|
)
|
||||||
from homeassistant.components.google_assistant import trait, helpers, const
|
from homeassistant.components.google_assistant import trait, helpers, const
|
||||||
|
from homeassistant.const import (
|
||||||
|
STATE_ON, STATE_OFF, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||||
|
TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES)
|
||||||
|
from homeassistant.core import State, DOMAIN as HA_DOMAIN
|
||||||
from homeassistant.util import color
|
from homeassistant.util import color
|
||||||
|
|
||||||
from tests.common import async_mock_service
|
from tests.common import async_mock_service
|
||||||
|
|
||||||
BASIC_CONFIG = helpers.Config(
|
BASIC_CONFIG = helpers.Config(
|
||||||
@ -795,3 +794,84 @@ async def test_lock_unlock_unlock(hass):
|
|||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'lock.front_door'
|
ATTR_ENTITY_ID: 'lock.front_door'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_fan_speed(hass):
|
||||||
|
"""Test FanSpeed trait speed control support for fan domain."""
|
||||||
|
assert trait.FanSpeedTrait.supported(fan.DOMAIN, fan.SUPPORT_SET_SPEED)
|
||||||
|
|
||||||
|
trt = trait.FanSpeedTrait(
|
||||||
|
hass, State(
|
||||||
|
'fan.living_room_fan', fan.SPEED_HIGH, attributes={
|
||||||
|
'speed_list': [
|
||||||
|
fan.SPEED_OFF, fan.SPEED_LOW, fan.SPEED_MEDIUM,
|
||||||
|
fan.SPEED_HIGH
|
||||||
|
],
|
||||||
|
'speed': 'low'
|
||||||
|
}), BASIC_CONFIG)
|
||||||
|
|
||||||
|
assert trt.sync_attributes() == {
|
||||||
|
'availableFanSpeeds': {
|
||||||
|
'ordered': True,
|
||||||
|
'speeds': [
|
||||||
|
{
|
||||||
|
'speed_name': 'off',
|
||||||
|
'speed_values': [
|
||||||
|
{
|
||||||
|
'speed_synonym': ['stop', 'off'],
|
||||||
|
'lang': 'en'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'speed_name': 'low',
|
||||||
|
'speed_values': [
|
||||||
|
{
|
||||||
|
'speed_synonym': [
|
||||||
|
'slow', 'low', 'slowest', 'lowest'],
|
||||||
|
'lang': 'en'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'speed_name': 'medium',
|
||||||
|
'speed_values': [
|
||||||
|
{
|
||||||
|
'speed_synonym': ['medium', 'mid', 'middle'],
|
||||||
|
'lang': 'en'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'speed_name': 'high',
|
||||||
|
'speed_values': [
|
||||||
|
{
|
||||||
|
'speed_synonym': [
|
||||||
|
'high', 'max', 'fast', 'highest', 'fastest',
|
||||||
|
'maximum'],
|
||||||
|
'lang': 'en'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'reversible': False
|
||||||
|
}
|
||||||
|
|
||||||
|
assert trt.query_attributes() == {
|
||||||
|
'currentFanSpeedSetting': 'low',
|
||||||
|
'on': True,
|
||||||
|
'online': True
|
||||||
|
}
|
||||||
|
|
||||||
|
assert trt.can_execute(
|
||||||
|
trait.COMMAND_FANSPEED, params={'fanSpeed': 'medium'})
|
||||||
|
|
||||||
|
calls = async_mock_service(hass, fan.DOMAIN, fan.SERVICE_SET_SPEED)
|
||||||
|
await trt.execute(trait.COMMAND_FANSPEED, params={'fanSpeed': 'medium'})
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data == {
|
||||||
|
'entity_id': 'fan.living_room_fan',
|
||||||
|
'speed': 'medium'
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user