Add button support to HomeKit (#60165)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Franck Nijhof 2021-11-23 00:46:51 +01:00 committed by GitHub
parent f510534c58
commit 766c889e70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 2 deletions

View file

@ -195,7 +195,14 @@ def get_accessory(hass, driver, state, aid, config): # noqa: C901
elif state.domain == "remote" and features & SUPPORT_ACTIVITY: elif state.domain == "remote" and features & SUPPORT_ACTIVITY:
a_type = "ActivityRemote" a_type = "ActivityRemote"
elif state.domain in ("automation", "input_boolean", "remote", "scene", "script"): elif state.domain in (
"automation",
"button",
"input_boolean",
"remote",
"scene",
"script",
):
a_type = "Switch" a_type = "Switch"
elif state.domain in ("input_select", "select"): elif state.domain in ("input_select", "select"):

View file

@ -76,6 +76,7 @@ SUPPORTED_DOMAINS = [
"alarm_control_panel", "alarm_control_panel",
"automation", "automation",
"binary_sensor", "binary_sensor",
"button",
CAMERA_DOMAIN, CAMERA_DOMAIN,
"climate", "climate",
"cover", "cover",

View file

@ -12,6 +12,7 @@ from pyhap.const import (
CATEGORY_SWITCH, CATEGORY_SWITCH,
) )
from homeassistant.components import button
from homeassistant.components.input_select import ATTR_OPTIONS, SERVICE_SELECT_OPTION from homeassistant.components.input_select import ATTR_OPTIONS, SERVICE_SELECT_OPTION
from homeassistant.components.switch import DOMAIN from homeassistant.components.switch import DOMAIN
from homeassistant.components.vacuum import ( from homeassistant.components.vacuum import (
@ -69,7 +70,7 @@ VALVE_TYPE: dict[str, ValveInfo] = {
} }
ACTIVATE_ONLY_SWITCH_DOMAINS = {"scene", "script"} ACTIVATE_ONLY_SWITCH_DOMAINS = {"button", "scene", "script"}
ACTIVATE_ONLY_RESET_SECONDS = 10 ACTIVATE_ONLY_RESET_SECONDS = 10
@ -149,6 +150,8 @@ class Switch(HomeAccessory):
if self._domain == "script": if self._domain == "script":
service = self._object_id service = self._object_id
params = {} params = {}
elif self._domain == button.DOMAIN:
service = button.SERVICE_PRESS
else: else:
service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF

View file

@ -95,6 +95,7 @@ ALLOWED_USED_COMPONENTS = {
"alert", "alert",
"automation", "automation",
"conversation", "conversation",
"button",
"device_automation", "device_automation",
"frontend", "frontend",
"group", "group",

View file

@ -30,6 +30,7 @@ from homeassistant.const import (
DEVICE_CLASS_CO2, DEVICE_CLASS_CO2,
LIGHT_LUX, LIGHT_LUX,
PERCENTAGE, PERCENTAGE,
STATE_UNKNOWN,
TEMP_CELSIUS, TEMP_CELSIUS,
TEMP_FAHRENHEIT, TEMP_FAHRENHEIT,
) )
@ -270,6 +271,7 @@ def test_type_sensors(type_name, entity_id, state, attrs):
[ [
("Outlet", "switch.test", "on", {}, {CONF_TYPE: TYPE_OUTLET}), ("Outlet", "switch.test", "on", {}, {CONF_TYPE: TYPE_OUTLET}),
("Switch", "automation.test", "on", {}, {}), ("Switch", "automation.test", "on", {}, {}),
("Switch", "button.test", STATE_UNKNOWN, {}, {}),
("Switch", "input_boolean.test", "on", {}, {}), ("Switch", "input_boolean.test", "on", {}, {}),
("Switch", "remote.test", "on", {}, {}), ("Switch", "remote.test", "on", {}, {}),
("Switch", "scene.test", "on", {}, {}), ("Switch", "scene.test", "on", {}, {}),

View file

@ -449,3 +449,46 @@ async def test_input_select_switch(hass, hk_driver, events, domain):
assert acc.select_chars["option1"].value is False assert acc.select_chars["option1"].value is False
assert acc.select_chars["option2"].value is False assert acc.select_chars["option2"].value is False
assert acc.select_chars["option3"].value is False assert acc.select_chars["option3"].value is False
async def test_button_switch(hass, hk_driver, events):
"""Test switch accessory from a button entity."""
domain = "button"
entity_id = "button.test"
hass.states.async_set(entity_id, None)
await hass.async_block_till_done()
acc = Switch(hass, hk_driver, "Switch", entity_id, 2, None)
await acc.run()
await hass.async_block_till_done()
assert acc.activate_only is True
assert acc.char_on.value is False
call_press = async_mock_service(hass, domain, "press")
acc.char_on.client_update_value(True)
await hass.async_block_till_done()
assert acc.char_on.value is True
assert len(call_press) == 1
assert call_press[0].data[ATTR_ENTITY_ID] == entity_id
assert len(events) == 1
assert events[-1].data[ATTR_VALUE] is None
future = dt_util.utcnow() + timedelta(seconds=1)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
assert acc.char_on.value is True
future = dt_util.utcnow() + timedelta(seconds=10)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
assert acc.char_on.value is False
assert len(events) == 1
assert len(call_press) == 1
acc.char_on.client_update_value(False)
await hass.async_block_till_done()
assert acc.char_on.value is False
assert len(events) == 1