Add support for tilt only covers to HomeKit (#53130)

pull/53142/head
J. Nick Koston 2021-07-18 10:17:58 -10:00 committed by GitHub
parent cb1eab6c24
commit 236738c455
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 12 deletions

View File

@ -127,7 +127,7 @@ def get_accessory(hass, driver, state, aid, config): # noqa: C901
and features & cover.SUPPORT_SET_POSITION
):
a_type = "Window"
elif features & cover.SUPPORT_SET_POSITION:
elif features & (cover.SUPPORT_SET_POSITION | cover.SUPPORT_SET_TILT_POSITION):
a_type = "WindowCovering"
elif features & (cover.SUPPORT_OPEN | cover.SUPPORT_CLOSE):
a_type = "WindowCoveringBasic"

View File

@ -13,6 +13,7 @@ from homeassistant.components.cover import (
ATTR_POSITION,
ATTR_TILT_POSITION,
DOMAIN,
SUPPORT_SET_POSITION,
SUPPORT_SET_TILT_POSITION,
SUPPORT_STOP,
)
@ -53,6 +54,8 @@ from .const import (
HK_POSITION_GOING_TO_MAX,
HK_POSITION_GOING_TO_MIN,
HK_POSITION_STOPPED,
PROP_MAX_VALUE,
PROP_MIN_VALUE,
SERV_GARAGE_DOOR_OPENER,
SERV_WINDOW,
SERV_WINDOW_COVERING,
@ -273,12 +276,24 @@ class OpeningDevice(OpeningDeviceBase, HomeAccessory):
"""Initialize a WindowCovering accessory object."""
super().__init__(*args, category=category, service=service)
state = self.hass.states.get(self.entity_id)
self.char_current_position = self.serv_cover.configure_char(
CHAR_CURRENT_POSITION, value=0
)
target_args = {"value": 0}
if self.features & SUPPORT_SET_POSITION:
target_args["setter_callback"] = self.move_cover
else:
# If its tilt only we lock the position state to 0 (closed)
# since CHAR_CURRENT_POSITION/CHAR_TARGET_POSITION are required
# by homekit, but really don't exist.
_LOGGER.debug(
"%s does not support setting position, current position will be locked to closed",
self.entity_id,
)
target_args["properties"] = {PROP_MIN_VALUE: 0, PROP_MAX_VALUE: 0}
self.char_target_position = self.serv_cover.configure_char(
CHAR_TARGET_POSITION, value=0, setter_callback=self.move_cover
CHAR_TARGET_POSITION, **target_args
)
self.char_position_state = self.serv_cover.configure_char(
CHAR_POSITION_STATE, value=HK_POSITION_STOPPED

View File

@ -126,14 +126,28 @@ def test_types(type_name, entity_id, state, attrs, config):
"Window",
"cover.set_position",
"open",
{ATTR_DEVICE_CLASS: "window", ATTR_SUPPORTED_FEATURES: 4},
{
ATTR_DEVICE_CLASS: "window",
ATTR_SUPPORTED_FEATURES: cover.SUPPORT_SET_POSITION,
},
),
(
"WindowCovering",
"cover.set_position",
"open",
{ATTR_SUPPORTED_FEATURES: cover.SUPPORT_SET_POSITION},
),
(
"WindowCovering",
"cover.tilt",
"open",
{ATTR_SUPPORTED_FEATURES: cover.SUPPORT_SET_TILT_POSITION},
),
("WindowCovering", "cover.set_position", "open", {ATTR_SUPPORTED_FEATURES: 4}),
(
"WindowCoveringBasic",
"cover.open_window",
"open",
{ATTR_SUPPORTED_FEATURES: 3},
{ATTR_SUPPORTED_FEATURES: (cover.SUPPORT_OPEN | cover.SUPPORT_CLOSE)},
),
],
)

View File

@ -18,6 +18,8 @@ from homeassistant.components.homekit.const import (
HK_DOOR_CLOSING,
HK_DOOR_OPEN,
HK_DOOR_OPENING,
PROP_MAX_VALUE,
PROP_MIN_VALUE,
)
from homeassistant.components.homekit.type_covers import (
GarageDoorOpener,
@ -133,7 +135,9 @@ async def test_windowcovering_set_cover_position(hass, hk_driver, events):
"""Test if accessory and HA are updated accordingly."""
entity_id = "cover.window"
hass.states.async_set(entity_id, None)
hass.states.async_set(
entity_id, STATE_UNKNOWN, {ATTR_SUPPORTED_FEATURES: SUPPORT_SET_POSITION}
)
await hass.async_block_till_done()
acc = WindowCovering(hass, hk_driver, "Cover", entity_id, 2, None)
await acc.run()
@ -145,31 +149,51 @@ async def test_windowcovering_set_cover_position(hass, hk_driver, events):
assert acc.char_current_position.value == 0
assert acc.char_target_position.value == 0
hass.states.async_set(entity_id, STATE_UNKNOWN, {ATTR_CURRENT_POSITION: None})
hass.states.async_set(
entity_id,
STATE_UNKNOWN,
{ATTR_SUPPORTED_FEATURES: SUPPORT_SET_POSITION, ATTR_CURRENT_POSITION: None},
)
await hass.async_block_till_done()
assert acc.char_current_position.value == 0
assert acc.char_target_position.value == 0
assert acc.char_position_state.value == 2
hass.states.async_set(entity_id, STATE_OPENING, {ATTR_CURRENT_POSITION: 60})
hass.states.async_set(
entity_id,
STATE_OPENING,
{ATTR_SUPPORTED_FEATURES: SUPPORT_SET_POSITION, ATTR_CURRENT_POSITION: 60},
)
await hass.async_block_till_done()
assert acc.char_current_position.value == 60
assert acc.char_target_position.value == 60
assert acc.char_position_state.value == 1
hass.states.async_set(entity_id, STATE_OPENING, {ATTR_CURRENT_POSITION: 70.0})
hass.states.async_set(
entity_id,
STATE_OPENING,
{ATTR_SUPPORTED_FEATURES: SUPPORT_SET_POSITION, ATTR_CURRENT_POSITION: 70.0},
)
await hass.async_block_till_done()
assert acc.char_current_position.value == 70
assert acc.char_target_position.value == 70
assert acc.char_position_state.value == 1
hass.states.async_set(entity_id, STATE_CLOSING, {ATTR_CURRENT_POSITION: 50})
hass.states.async_set(
entity_id,
STATE_CLOSING,
{ATTR_SUPPORTED_FEATURES: SUPPORT_SET_POSITION, ATTR_CURRENT_POSITION: 50},
)
await hass.async_block_till_done()
assert acc.char_current_position.value == 50
assert acc.char_target_position.value == 50
assert acc.char_position_state.value == 0
hass.states.async_set(entity_id, STATE_OPEN, {ATTR_CURRENT_POSITION: 50})
hass.states.async_set(
entity_id,
STATE_OPEN,
{ATTR_SUPPORTED_FEATURES: SUPPORT_SET_POSITION, ATTR_CURRENT_POSITION: 50},
)
await hass.async_block_till_done()
assert acc.char_current_position.value == 50
assert acc.char_target_position.value == 50
@ -283,6 +307,27 @@ async def test_windowcovering_cover_set_tilt(hass, hk_driver, events):
assert events[-1].data[ATTR_VALUE] == 75
async def test_windowcovering_tilt_only(hass, hk_driver, events):
"""Test we lock the window covering closed when its tilt only."""
entity_id = "cover.window"
hass.states.async_set(
entity_id, STATE_UNKNOWN, {ATTR_SUPPORTED_FEATURES: SUPPORT_SET_TILT_POSITION}
)
await hass.async_block_till_done()
acc = WindowCovering(hass, hk_driver, "Cover", entity_id, 2, None)
await acc.run()
await hass.async_block_till_done()
assert acc.aid == 2
assert acc.category == 14 # WindowCovering
assert acc.char_current_position.value == 0
assert acc.char_target_position.value == 0
assert acc.char_target_position.properties[PROP_MIN_VALUE] == 0
assert acc.char_target_position.properties[PROP_MAX_VALUE] == 0
async def test_windowcovering_open_close(hass, hk_driver, events):
"""Test if accessory and HA are updated accordingly."""
entity_id = "cover.window"