From 755c44d1525ab8fe185e5f068513f3f8205d9ef8 Mon Sep 17 00:00:00 2001 From: Doney den Ouden Date: Tue, 7 Mar 2023 05:07:43 +0100 Subject: [PATCH] Add HomeKit Door accessory type (#80741) Co-authored-by: Jason Redd Co-authored-by: J. Nick Koston --- .../components/homekit/accessories.py | 5 ++ homeassistant/components/homekit/const.py | 1 + .../components/homekit/type_covers.py | 14 +++++ .../homekit/test_get_accessories.py | 9 ++++ tests/components/homekit/test_type_covers.py | 53 +++++++++++++++++++ 5 files changed, 82 insertions(+) diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index adab539fb30..dc8a2a7c639 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -148,6 +148,11 @@ def get_accessory( # noqa: C901 and features & CoverEntityFeature.SET_POSITION ): a_type = "Window" + elif ( + device_class == CoverDeviceClass.DOOR + and features & CoverEntityFeature.SET_POSITION + ): + a_type = "Door" elif features & CoverEntityFeature.SET_POSITION: a_type = "WindowCovering" elif features & (CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE): diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 58e1e13a3f3..4517f9c5a5e 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -119,6 +119,7 @@ SERV_CAMERA_RTP_STREAM_MANAGEMENT = "CameraRTPStreamManagement" SERV_CARBON_DIOXIDE_SENSOR = "CarbonDioxideSensor" SERV_CARBON_MONOXIDE_SENSOR = "CarbonMonoxideSensor" SERV_CONTACT_SENSOR = "ContactSensor" +SERV_DOOR = "Door" SERV_DOORBELL = "Doorbell" SERV_FANV2 = "Fanv2" SERV_GARAGE_DOOR_OPENER = "GarageDoorOpener" diff --git a/homeassistant/components/homekit/type_covers.py b/homeassistant/components/homekit/type_covers.py index 4b21bfb77df..05feb580572 100644 --- a/homeassistant/components/homekit/type_covers.py +++ b/homeassistant/components/homekit/type_covers.py @@ -2,6 +2,7 @@ import logging from pyhap.const import ( + CATEGORY_DOOR, CATEGORY_GARAGE_DOOR_OPENER, CATEGORY_WINDOW, CATEGORY_WINDOW_COVERING, @@ -54,6 +55,7 @@ from .const import ( HK_POSITION_STOPPED, PROP_MAX_VALUE, PROP_MIN_VALUE, + SERV_DOOR, SERV_GARAGE_DOOR_OPENER, SERV_WINDOW, SERV_WINDOW_COVERING, @@ -323,6 +325,18 @@ class OpeningDevice(OpeningDeviceBase, HomeAccessory): super().async_update_state(new_state) +@TYPES.register("Door") +class Door(OpeningDevice): + """Generate a Door accessory for a cover entity. + + The entity must support: set_cover_position. + """ + + def __init__(self, *args): + """Initialize a Door accessory object.""" + super().__init__(*args, category=CATEGORY_DOOR, service=SERV_DOOR) + + @TYPES.register("Window") class Window(OpeningDevice): """Generate a Window accessory for a cover entity with WINDOW device class. diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index b5d65993a87..08a7f8a2206 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -160,6 +160,15 @@ def test_types(type_name, entity_id, state, attrs, config) -> None: ) }, ), + ( + "Door", + "cover.door", + "open", + { + ATTR_DEVICE_CLASS: "door", + ATTR_SUPPORTED_FEATURES: cover.SUPPORT_SET_POSITION, + }, + ), ], ) def test_type_covers(type_name, entity_id, state, attrs) -> None: diff --git a/tests/components/homekit/test_type_covers.py b/tests/components/homekit/test_type_covers.py index e1c547315c1..9da576b6a0e 100644 --- a/tests/components/homekit/test_type_covers.py +++ b/tests/components/homekit/test_type_covers.py @@ -19,6 +19,7 @@ from homeassistant.components.homekit.const import ( PROP_MIN_VALUE, ) from homeassistant.components.homekit.type_covers import ( + Door, GarageDoorOpener, Window, WindowCovering, @@ -128,6 +129,58 @@ async def test_garage_door_open_close(hass: HomeAssistant, hk_driver, events) -> assert events[-1].data[ATTR_VALUE] is None +async def test_door_instantiate_set_position( + hass: HomeAssistant, hk_driver, events +) -> None: + """Test if Door accessory is instantiated correctly and can set position.""" + entity_id = "cover.door" + + hass.states.async_set( + entity_id, + STATE_OPEN, + { + ATTR_SUPPORTED_FEATURES: CoverEntityFeature.SET_POSITION, + ATTR_CURRENT_POSITION: 0, + }, + ) + await hass.async_block_till_done() + acc = Door(hass, hk_driver, "Door", entity_id, 2, None) + await acc.run() + await hass.async_block_till_done() + + assert acc.aid == 2 + assert acc.category == 12 # Door + + assert acc.char_current_position.value == 0 + assert acc.char_target_position.value == 0 + + hass.states.async_set( + entity_id, + STATE_OPEN, + { + ATTR_SUPPORTED_FEATURES: CoverEntityFeature.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 == 2 + + hass.states.async_set( + entity_id, + STATE_OPEN, + { + ATTR_SUPPORTED_FEATURES: CoverEntityFeature.SET_POSITION, + ATTR_CURRENT_POSITION: "GARBAGE", + }, + ) + 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 == 2 + + async def test_windowcovering_set_cover_position( hass: HomeAssistant, hk_driver, events ) -> None: