Add vacuum support to homekit (#34386)

pull/34475/head
J. Nick Koston 2020-04-20 10:00:52 -05:00 committed by GitHub
parent d144228272
commit f0d553514d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 2 deletions

View File

@ -5,7 +5,7 @@ import logging
import voluptuous as vol
from zeroconf import InterfaceChoice
from homeassistant.components import cover
from homeassistant.components import cover, vacuum
from homeassistant.components.cover import DEVICE_CLASS_GARAGE, DEVICE_CLASS_GATE
from homeassistant.components.media_player import DEVICE_CLASS_TV
from homeassistant.const import (
@ -268,6 +268,13 @@ def get_accessory(hass, driver, state, aid, config):
switch_type = config.get(CONF_TYPE, TYPE_SWITCH)
a_type = SWITCH_TYPES[switch_type]
elif state.domain == "vacuum":
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if features & (vacuum.SUPPORT_START | vacuum.SUPPORT_RETURN_HOME):
a_type = "DockVacuum"
else:
a_type = "Switch"
elif state.domain in ("automation", "input_boolean", "remote", "scene", "script"):
a_type = "Switch"

View File

@ -11,6 +11,12 @@ from pyhap.const import (
from homeassistant.components.script import ATTR_CAN_CANCEL
from homeassistant.components.switch import DOMAIN
from homeassistant.components.vacuum import (
DOMAIN as VACUUM_DOMAIN,
SERVICE_RETURN_TO_BASE,
SERVICE_START,
STATE_CLEANING,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_TYPE,
@ -146,6 +152,25 @@ class Switch(HomeAccessory):
self.char_on.set_value(current_state)
@TYPES.register("DockVacuum")
class DockVacuum(Switch):
"""Generate a Switch accessory."""
def set_state(self, value):
"""Move switch state to value if call came from HomeKit."""
_LOGGER.debug("%s: Set switch state to %s", self.entity_id, value)
params = {ATTR_ENTITY_ID: self.entity_id}
service = SERVICE_START if value else SERVICE_RETURN_TO_BASE
self.call_service(VACUUM_DOMAIN, service, params)
def update_state(self, new_state):
"""Update switch state after state changed."""
current_state = new_state.state in (STATE_CLEANING, STATE_ON)
if self.char_on.value is not current_state:
_LOGGER.debug("%s: Set current state to %s", self.entity_id, current_state)
self.char_on.set_value(current_state)
@TYPES.register("Valve")
class Valve(HomeAccessory):
"""Generate a Valve accessory."""

View File

@ -10,8 +10,20 @@ from homeassistant.components.homekit.const import (
TYPE_SPRINKLER,
TYPE_VALVE,
)
from homeassistant.components.homekit.type_switches import Outlet, Switch, Valve
from homeassistant.components.homekit.type_switches import (
DockVacuum,
Outlet,
Switch,
Valve,
)
from homeassistant.components.script import ATTR_CAN_CANCEL
from homeassistant.components.vacuum import (
DOMAIN as VACUUM_DOMAIN,
SERVICE_RETURN_TO_BASE,
SERVICE_START,
STATE_CLEANING,
STATE_DOCKED,
)
from homeassistant.const import ATTR_ENTITY_ID, CONF_TYPE, STATE_OFF, STATE_ON
from homeassistant.core import split_entity_id
import homeassistant.util.dt as dt_util
@ -182,6 +194,52 @@ async def test_valve_set_state(hass, hk_driver, events):
assert events[-1].data[ATTR_VALUE] is None
async def test_vacuum_set_state(hass, hk_driver, events):
"""Test if Vacuum accessory and HA are updated accordingly."""
entity_id = "vacuum.roomba"
hass.states.async_set(entity_id, None)
await hass.async_block_till_done()
acc = DockVacuum(hass, hk_driver, "DockVacuum", entity_id, 2, None)
await acc.run_handler()
await hass.async_block_till_done()
assert acc.aid == 2
assert acc.category == 8 # Switch
assert acc.char_on.value == 0
hass.states.async_set(entity_id, STATE_CLEANING)
await hass.async_block_till_done()
assert acc.char_on.value == 1
hass.states.async_set(entity_id, STATE_DOCKED)
await hass.async_block_till_done()
assert acc.char_on.value == 0
# Set from HomeKit
call_start = async_mock_service(hass, VACUUM_DOMAIN, SERVICE_START)
call_return_to_base = async_mock_service(
hass, VACUUM_DOMAIN, SERVICE_RETURN_TO_BASE
)
await hass.async_add_executor_job(acc.char_on.client_update_value, 1)
await hass.async_block_till_done()
assert acc.char_on.value == 1
assert call_start
assert call_start[0].data[ATTR_ENTITY_ID] == entity_id
assert len(events) == 1
assert events[-1].data[ATTR_VALUE] is None
await hass.async_add_executor_job(acc.char_on.client_update_value, 0)
await hass.async_block_till_done()
assert acc.char_on.value == 0
assert call_return_to_base
assert call_return_to_base[0].data[ATTR_ENTITY_ID] == entity_id
assert len(events) == 2
assert events[-1].data[ATTR_VALUE] is None
@pytest.mark.parametrize(
"entity_id, attrs",
[