From b87b618c940ebdf01ddc48a0da3a69c5f45d7fc5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 16 Apr 2020 18:15:37 -0500 Subject: [PATCH] Resolve homekit not updating motion sensors (#34282) Co-Authored-By: Paulus Schoutsen --- .../components/homekit/type_sensors.py | 28 ++++++----- tests/components/homekit/test_type_sensors.py | 47 ++++++++++++++++++- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/homekit/type_sensors.py b/homeassistant/components/homekit/type_sensors.py index 78cb21bc88a..5bdf9a07f04 100644 --- a/homeassistant/components/homekit/type_sensors.py +++ b/homeassistant/components/homekit/type_sensors.py @@ -60,16 +60,16 @@ from .util import convert_to_float, density_to_air_quality, temperature_to_homek _LOGGER = logging.getLogger(__name__) BINARY_SENSOR_SERVICE_MAP = { - DEVICE_CLASS_CO2: (SERV_CARBON_DIOXIDE_SENSOR, CHAR_CARBON_DIOXIDE_DETECTED), - DEVICE_CLASS_DOOR: (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE), - DEVICE_CLASS_GARAGE_DOOR: (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE), - DEVICE_CLASS_GAS: (SERV_CARBON_MONOXIDE_SENSOR, CHAR_CARBON_MONOXIDE_DETECTED), - DEVICE_CLASS_MOISTURE: (SERV_LEAK_SENSOR, CHAR_LEAK_DETECTED), - DEVICE_CLASS_MOTION: (SERV_MOTION_SENSOR, CHAR_MOTION_DETECTED), - DEVICE_CLASS_OCCUPANCY: (SERV_OCCUPANCY_SENSOR, CHAR_OCCUPANCY_DETECTED), - DEVICE_CLASS_OPENING: (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE), - DEVICE_CLASS_SMOKE: (SERV_SMOKE_SENSOR, CHAR_SMOKE_DETECTED), - DEVICE_CLASS_WINDOW: (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE), + DEVICE_CLASS_CO2: (SERV_CARBON_DIOXIDE_SENSOR, CHAR_CARBON_DIOXIDE_DETECTED, int), + DEVICE_CLASS_DOOR: (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, int), + DEVICE_CLASS_GARAGE_DOOR: (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, int), + DEVICE_CLASS_GAS: (SERV_CARBON_MONOXIDE_SENSOR, CHAR_CARBON_MONOXIDE_DETECTED, int), + DEVICE_CLASS_MOISTURE: (SERV_LEAK_SENSOR, CHAR_LEAK_DETECTED, int), + DEVICE_CLASS_MOTION: (SERV_MOTION_SENSOR, CHAR_MOTION_DETECTED, bool), + DEVICE_CLASS_OCCUPANCY: (SERV_OCCUPANCY_SENSOR, CHAR_OCCUPANCY_DETECTED, int), + DEVICE_CLASS_OPENING: (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, int), + DEVICE_CLASS_SMOKE: (SERV_SMOKE_SENSOR, CHAR_SMOKE_DETECTED, int), + DEVICE_CLASS_WINDOW: (SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, int), } @@ -274,8 +274,12 @@ class BinarySensor(HomeAccessory): else BINARY_SENSOR_SERVICE_MAP[DEVICE_CLASS_OCCUPANCY] ) + self.format = service_char[2] service = self.add_preload_service(service_char[0]) - self.char_detected = service.configure_char(service_char[1], value=0) + initial_value = False if self.format is bool else 0 + self.char_detected = service.configure_char( + service_char[1], value=initial_value + ) # Set the state so it is in sync on initial # GET to avoid an event storm after homekit startup self.update_state(state) @@ -283,7 +287,7 @@ class BinarySensor(HomeAccessory): def update_state(self, new_state): """Update accessory after state change.""" state = new_state.state - detected = state in (STATE_ON, STATE_HOME) + detected = self.format(state in (STATE_ON, STATE_HOME)) if self.char_detected.value != detected: self.char_detected.set_value(detected) _LOGGER.debug("%s: Set to %d", self.entity_id, detected) diff --git a/tests/components/homekit/test_type_sensors.py b/tests/components/homekit/test_type_sensors.py index b6bab174962..8eb993ab6f7 100644 --- a/tests/components/homekit/test_type_sensors.py +++ b/tests/components/homekit/test_type_sensors.py @@ -1,6 +1,7 @@ """Test different accessory types: Sensors.""" from homeassistant.components.homekit import get_accessory from homeassistant.components.homekit.const import ( + DEVICE_CLASS_MOTION, PROP_CELSIUS, THRESHOLD_CO, THRESHOLD_CO2, @@ -256,11 +257,55 @@ async def test_binary(hass, hk_driver): assert acc.char_detected.value == 0 +async def test_motion_uses_bool(hass, hk_driver): + """Test if accessory is updated after state change.""" + entity_id = "binary_sensor.motion" + + hass.states.async_set( + entity_id, STATE_UNKNOWN, {ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION} + ) + await hass.async_block_till_done() + + acc = BinarySensor(hass, hk_driver, "Motion Sensor", entity_id, 2, None) + await acc.run_handler() + + assert acc.aid == 2 + assert acc.category == 10 # Sensor + + assert acc.char_detected.value is False + + hass.states.async_set(entity_id, STATE_ON, {ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION}) + await hass.async_block_till_done() + assert acc.char_detected.value is True + + hass.states.async_set( + entity_id, STATE_OFF, {ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION} + ) + await hass.async_block_till_done() + assert acc.char_detected.value is False + + hass.states.async_set( + entity_id, STATE_HOME, {ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION} + ) + await hass.async_block_till_done() + assert acc.char_detected.value is True + + hass.states.async_set( + entity_id, STATE_NOT_HOME, {ATTR_DEVICE_CLASS: DEVICE_CLASS_MOTION} + ) + await hass.async_block_till_done() + assert acc.char_detected.value is False + + hass.states.async_remove(entity_id) + await hass.async_block_till_done() + assert acc.char_detected.value is False + + async def test_binary_device_classes(hass, hk_driver): """Test if services and characteristics are assigned correctly.""" entity_id = "binary_sensor.demo" - for device_class, (service, char) in BINARY_SENSOR_SERVICE_MAP.items(): + for device_class, (service, char, _) in BINARY_SENSOR_SERVICE_MAP.items(): hass.states.async_set(entity_id, STATE_OFF, {ATTR_DEVICE_CLASS: device_class}) await hass.async_block_till_done()