Skip to content

Commit

Permalink
Added dock support for vacuum through google assistant
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuszluciow authored and mariuszluciow-ocado committed Oct 21, 2018
1 parent 7e3d0f0 commit 4dc97a1
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 7 deletions.
6 changes: 5 additions & 1 deletion homeassistant/components/google_assistant/const.py
Expand Up @@ -11,17 +11,21 @@
CONF_ALIASES = 'aliases'
CONF_API_KEY = 'api_key'
CONF_ROOM_HINT = 'room'
CONF_TYPE = 'type'

DEFAULT_EXPOSE_BY_DEFAULT = True
DEFAULT_EXPOSED_DOMAINS = [
'switch', 'light', 'group', 'media_player', 'fan', 'cover', 'climate'
'switch', 'light', 'group', 'media_player', 'fan', 'cover', 'climate',
'scene', 'vacuum',
]
CLIMATE_MODE_HEATCOOL = 'heatcool'
CLIMATE_SUPPORTED_MODES = {'heat', 'cool', 'off', 'on', CLIMATE_MODE_HEATCOOL}

PREFIX_TYPES = 'action.devices.types.'
TYPE_LIGHT = PREFIX_TYPES + 'LIGHT'
TYPE_SWITCH = PREFIX_TYPES + 'SWITCH'
TYPE_VACUUM = PREFIX_TYPES + 'VACUUM'
TYPE_FAN = PREFIX_TYPES + 'FAN'
TYPE_SCENE = PREFIX_TYPES + 'SCENE'
TYPE_THERMOSTAT = PREFIX_TYPES + 'THERMOSTAT'

Expand Down
13 changes: 9 additions & 4 deletions homeassistant/components/google_assistant/smart_home.py
Expand Up @@ -19,12 +19,14 @@
scene,
script,
switch,
vacuum,
)

from . import trait
from .const import (
TYPE_LIGHT, TYPE_SCENE, TYPE_SWITCH, TYPE_THERMOSTAT,
CONF_ALIASES, CONF_ROOM_HINT,
TYPE_LIGHT, TYPE_SCENE, TYPE_SWITCH, TYPE_FAN, TYPE_VACUUM,
TYPE_THERMOSTAT,
CONF_ALIASES, CONF_ROOM_HINT, CONF_TYPE,
ERR_NOT_SUPPORTED, ERR_PROTOCOL_ERROR, ERR_DEVICE_OFFLINE,
ERR_UNKNOWN_ERROR
)
Expand All @@ -36,14 +38,15 @@
DOMAIN_TO_GOOGLE_TYPES = {
climate.DOMAIN: TYPE_THERMOSTAT,
cover.DOMAIN: TYPE_SWITCH,
fan.DOMAIN: TYPE_SWITCH,
fan.DOMAIN: TYPE_FAN,
group.DOMAIN: TYPE_SWITCH,
input_boolean.DOMAIN: TYPE_SWITCH,
light.DOMAIN: TYPE_LIGHT,
media_player.DOMAIN: TYPE_SWITCH,
scene.DOMAIN: TYPE_SCENE,
script.DOMAIN: TYPE_SCENE,
switch.DOMAIN: TYPE_SWITCH,
vacuum.DOMAIN: TYPE_VACUUM,
}


Expand Down Expand Up @@ -96,6 +99,8 @@ def sync_serialize(self):

entity_config = self.config.entity_config.get(state.entity_id, {})
name = (entity_config.get(CONF_NAME) or state.name).strip()
default_type = DOMAIN_TO_GOOGLE_TYPES[state.domain]
device_type = (entity_config.get(CONF_TYPE) or default_type).strip()

# If an empty string
if not name:
Expand All @@ -115,7 +120,7 @@ def sync_serialize(self):
'attributes': {},
'traits': [trait.name for trait in traits],
'willReportState': False,
'type': DOMAIN_TO_GOOGLE_TYPES[state.domain],
'type': device_type,
}

# use aliases
Expand Down
39 changes: 39 additions & 0 deletions homeassistant/components/google_assistant/trait.py
Expand Up @@ -13,6 +13,7 @@
scene,
script,
switch,
vacuum,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
Expand All @@ -31,6 +32,7 @@

PREFIX_TRAITS = 'action.devices.traits.'
TRAIT_ONOFF = PREFIX_TRAITS + 'OnOff'
TRAIT_DOCK = PREFIX_TRAITS + 'Dock'
TRAIT_BRIGHTNESS = PREFIX_TRAITS + 'Brightness'
TRAIT_COLOR_SPECTRUM = PREFIX_TRAITS + 'ColorSpectrum'
TRAIT_COLOR_TEMP = PREFIX_TRAITS + 'ColorTemperature'
Expand All @@ -39,6 +41,7 @@

PREFIX_COMMANDS = 'action.devices.commands.'
COMMAND_ONOFF = PREFIX_COMMANDS + 'OnOff'
COMMAND_DOCK = PREFIX_COMMANDS + 'Dock'
COMMAND_BRIGHTNESS_ABSOLUTE = PREFIX_COMMANDS + 'BrightnessAbsolute'
COMMAND_COLOR_ABSOLUTE = PREFIX_COMMANDS + 'ColorAbsolute'
COMMAND_ACTIVATE_SCENE = PREFIX_COMMANDS + 'ActivateScene'
Expand Down Expand Up @@ -192,6 +195,7 @@ def supported(domain, features):
light.DOMAIN,
cover.DOMAIN,
media_player.DOMAIN,
vacuum.DOMAIN,
)

def sync_attributes(self):
Expand Down Expand Up @@ -392,6 +396,41 @@ async def execute(self, command, params):
}, blocking=self.state.domain != script.DOMAIN)


@register_trait
class DockTrait(_Trait):
"""Trait to offer dock functionality.
https://developers.google.com/actions/smarthome/traits/dock
"""

name = TRAIT_DOCK
commands = [
COMMAND_DOCK
]

@staticmethod
def supported(domain, features):
"""Test if state is supported."""
return domain == vacuum.DOMAIN

def sync_attributes(self):
"""Return dock attributes for a sync request."""
# Neither supported domain can support sceneReversible
return {}

def query_attributes(self):
"""Return dock query attributes."""
return {'isDocked': self.state.state != vacuum.STATE_DOCKED}

async def execute(self, command, params):
"""Execute a dock command."""
# Don't block for scripts as they can be slow.
await self.hass.services.async_call(
self.state.domain, vacuum.SERVICE_RETURN_TO_BASE, {
ATTR_ENTITY_ID: self.state.entity_id
}, blocking=True)


@register_trait
class TemperatureSettingTrait(_Trait):
"""Trait to offer handling both temperature point and modes functionality.
Expand Down
4 changes: 2 additions & 2 deletions tests/components/google_assistant/__init__.py
Expand Up @@ -184,15 +184,15 @@
'name': 'Living Room Fan'
},
'traits': ['action.devices.traits.OnOff'],
'type': 'action.devices.types.SWITCH',
'type': 'action.devices.types.FAN',
'willReportState': False
}, {
'id': 'fan.ceiling_fan',
'name': {
'name': 'Ceiling Fan'
},
'traits': ['action.devices.traits.OnOff'],
'type': 'action.devices.types.SWITCH',
'type': 'action.devices.types.FAN',
'willReportState': False
}, {
'id': 'group.all_fans',
Expand Down
58 changes: 58 additions & 0 deletions tests/components/google_assistant/test_trait.py
Expand Up @@ -15,6 +15,7 @@
scene,
script,
switch,
vacuum,
)
from homeassistant.components.google_assistant import trait, helpers, const
from homeassistant.util import color
Expand Down Expand Up @@ -357,6 +358,63 @@ async def test_onoff_media_player(hass):
}


async def test_onoff_vacuum(hass):
"""Test OnOff trait support for vacuum domain."""
assert trait.OnOffTrait.supported(vacuum.DOMAIN, 0)

trt_on = trait.OnOffTrait(hass, State('vacuum.bla', STATE_ON))

assert trt_on.sync_attributes() == {}

assert trt_on.query_attributes() == {
'on': True
}

trt_off = trait.OnOffTrait(hass, State('vacuum.bla', STATE_OFF))
assert trt_off.query_attributes() == {
'on': False
}

on_calls = async_mock_service(hass, vacuum.DOMAIN, SERVICE_TURN_ON)
await trt_on.execute(trait.COMMAND_ONOFF, {
'on': True
})
assert len(on_calls) == 1
assert on_calls[0].data == {
ATTR_ENTITY_ID: 'vacuum.bla',
}

off_calls = async_mock_service(hass, vacuum.DOMAIN, SERVICE_TURN_OFF)
await trt_on.execute(trait.COMMAND_ONOFF, {
'on': False
})
assert len(off_calls) == 1
assert off_calls[0].data == {
ATTR_ENTITY_ID: 'vacuum.bla',
}


async def test_dock_vacuum(hass):
"""Test dock trait support for vacuum domain."""
assert trait.DockTrait.supported(vacuum.DOMAIN, 0)

trt = trait.DockTrait(hass, State('vacuum.bla', vacuum.STATE_ON))

assert trt.sync_attributes() == {}

assert trt.query_attributes() == {
'isDocked': False
}

calls = async_mock_service(hass, vacuum.DOMAIN,
vacuum.SERVICE_RETURN_TO_BASE)
await trt.execute(trait.COMMAND_DOCK, {})
assert len(calls) == 1
assert calls[0].data == {
ATTR_ENTITY_ID: 'vacuum.bla',
}


async def test_color_spectrum_light(hass):
"""Test ColorSpectrum trait support for light domain."""
assert not trait.ColorSpectrumTrait.supported(light.DOMAIN, 0)
Expand Down

0 comments on commit 4dc97a1

Please sign in to comment.