Add vacuum support to homekit (#34386)

This commit is contained in:
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 import voluptuous as vol
from zeroconf import InterfaceChoice 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.cover import DEVICE_CLASS_GARAGE, DEVICE_CLASS_GATE
from homeassistant.components.media_player import DEVICE_CLASS_TV from homeassistant.components.media_player import DEVICE_CLASS_TV
from homeassistant.const import ( from homeassistant.const import (
@ -268,6 +268,13 @@ def get_accessory(hass, driver, state, aid, config):
switch_type = config.get(CONF_TYPE, TYPE_SWITCH) switch_type = config.get(CONF_TYPE, TYPE_SWITCH)
a_type = SWITCH_TYPES[switch_type] 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"): elif state.domain in ("automation", "input_boolean", "remote", "scene", "script"):
a_type = "Switch" a_type = "Switch"

View File

@ -11,6 +11,12 @@ from pyhap.const import (
from homeassistant.components.script import ATTR_CAN_CANCEL from homeassistant.components.script import ATTR_CAN_CANCEL
from homeassistant.components.switch import DOMAIN 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 ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
CONF_TYPE, CONF_TYPE,
@ -146,6 +152,25 @@ class Switch(HomeAccessory):
self.char_on.set_value(current_state) 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") @TYPES.register("Valve")
class Valve(HomeAccessory): class Valve(HomeAccessory):
"""Generate a Valve accessory.""" """Generate a Valve accessory."""

View File

@ -10,8 +10,20 @@ from homeassistant.components.homekit.const import (
TYPE_SPRINKLER, TYPE_SPRINKLER,
TYPE_VALVE, 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.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.const import ATTR_ENTITY_ID, CONF_TYPE, STATE_OFF, STATE_ON
from homeassistant.core import split_entity_id from homeassistant.core import split_entity_id
import homeassistant.util.dt as dt_util 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 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( @pytest.mark.parametrize(
"entity_id, attrs", "entity_id, attrs",
[ [