1
mirror of https://github.com/home-assistant/core synced 2024-07-27 18:58:57 +02:00

Switch homekit to use async_track_state_change_event (#37253)

* Switch homekit to use async_track_state_change_event

Calling async_track_state_change_event directly
is faster than async_track_state_change and has
slightly lower latency triggering state updates
in homekit

* check for deleted entities

* Update additional tests for linked sensor removals

* Ensure removing entities does not result in an exception
This commit is contained in:
J. Nick Koston 2020-07-05 21:05:13 -05:00 committed by GitHub
parent 9b77e16ffc
commit 711b8e10a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 24 deletions

View File

@ -33,7 +33,7 @@ from homeassistant.const import (
)
from homeassistant.core import callback as ha_callback, split_entity_id
from homeassistant.helpers.event import (
async_track_state_change,
async_track_state_change_event,
track_point_in_utc_time,
)
from homeassistant.util import dt as dt_util
@ -346,10 +346,10 @@ class HomeAccessory(Accessory):
Run inside the Home Assistant event loop.
"""
state = self.hass.states.get(self.entity_id)
self.async_update_state_callback(None, None, state)
self.async_update_state_callback(state)
self._subscriptions.append(
async_track_state_change(
self.hass, self.entity_id, self.async_update_state_callback
async_track_state_change_event(
self.hass, [self.entity_id], self.async_update_event_state_callback
)
)
@ -364,36 +364,37 @@ class HomeAccessory(Accessory):
ATTR_BATTERY_CHARGING
)
self._subscriptions.append(
async_track_state_change(
async_track_state_change_event(
self.hass,
self.linked_battery_sensor,
[self.linked_battery_sensor],
self.async_update_linked_battery_callback,
)
)
else:
elif state is not None:
battery_state = state.attributes.get(ATTR_BATTERY_LEVEL)
if self.linked_battery_charging_sensor:
battery_charging_state = (
self.hass.states.get(self.linked_battery_charging_sensor).state
== STATE_ON
)
state = self.hass.states.get(self.linked_battery_charging_sensor)
battery_charging_state = state and state.state == STATE_ON
self._subscriptions.append(
async_track_state_change(
async_track_state_change_event(
self.hass,
self.linked_battery_charging_sensor,
[self.linked_battery_charging_sensor],
self.async_update_linked_battery_charging_callback,
)
)
elif battery_charging_state is None:
elif battery_charging_state is None and state is not None:
battery_charging_state = state.attributes.get(ATTR_BATTERY_CHARGING)
if battery_state is not None or battery_charging_state is not None:
self.async_update_battery(battery_state, battery_charging_state)
@ha_callback
def async_update_state_callback(
self, entity_id=None, old_state=None, new_state=None
):
def async_update_event_state_callback(self, event):
"""Handle state change event listener callback."""
self.async_update_state_callback(event.data.get("new_state"))
@ha_callback
def async_update_state_callback(self, new_state):
"""Handle state change listener callback."""
_LOGGER.debug("New_state: %s", new_state)
if new_state is None:
@ -415,10 +416,11 @@ class HomeAccessory(Accessory):
self.async_update_state(new_state)
@ha_callback
def async_update_linked_battery_callback(
self, entity_id=None, old_state=None, new_state=None
):
def async_update_linked_battery_callback(self, event):
"""Handle linked battery sensor state change listener callback."""
new_state = event.data.get("new_state")
if new_state is None:
return
if self.linked_battery_charging_sensor:
battery_charging_state = None
else:
@ -426,10 +428,11 @@ class HomeAccessory(Accessory):
self.async_update_battery(new_state.state, battery_charging_state)
@ha_callback
def async_update_linked_battery_charging_callback(
self, entity_id=None, old_state=None, new_state=None
):
def async_update_linked_battery_charging_callback(self, event):
"""Handle linked battery charging sensor state change listener callback."""
new_state = event.data.get("new_state")
if new_state is None:
return
self.async_update_battery(None, new_state.state == STATE_ON)
@ha_callback

View File

@ -207,6 +207,7 @@ async def test_battery_service(hass, hk_driver, caplog):
await hass.async_block_till_done()
state = hass.states.get(entity_id)
mock_async_update_state.assert_called_with(state)
assert acc._char_battery.value == 15
assert acc._char_low_battery.value == 1
assert acc._char_charging.value == 2
@ -218,6 +219,7 @@ async def test_battery_service(hass, hk_driver, caplog):
await hass.async_block_till_done()
state = hass.states.get(entity_id)
mock_async_update_state.assert_called_with(state)
assert acc._char_battery.value == 15
assert acc._char_low_battery.value == 1
assert acc._char_charging.value == 2
@ -341,7 +343,12 @@ async def test_linked_battery_sensor(hass, hk_driver, caplog):
hass.states.async_set(linked_battery, 100, {ATTR_BATTERY_CHARGING: False})
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert acc._char_battery.value == 100
assert acc._char_low_battery.value == 0
assert acc._char_charging.value == 0
hass.states.async_remove(linked_battery)
await hass.async_block_till_done()
assert acc._char_battery.value == 100
assert acc._char_low_battery.value == 0
assert acc._char_charging.value == 0
@ -396,6 +403,14 @@ async def test_linked_battery_charging_sensor(hass, hk_driver, caplog):
mock_async_update_state.assert_called_with(state)
assert acc._char_charging.value == 1
with patch(
"homeassistant.components.homekit.accessories.HomeAccessory.async_update_state"
) as mock_async_update_state:
hass.states.async_remove(linked_battery_charging_sensor)
await acc.run_handler()
await hass.async_block_till_done()
assert acc._char_charging.value == 1
async def test_linked_battery_sensor_and_linked_battery_charging_sensor(
hass, hk_driver, caplog
@ -439,6 +454,12 @@ async def test_linked_battery_sensor_and_linked_battery_charging_sensor(
assert acc._char_low_battery.value == 0
assert acc._char_charging.value == 0
hass.states.async_remove(linked_battery_charging_sensor)
await hass.async_block_till_done()
assert acc._char_battery.value == 50
assert acc._char_low_battery.value == 0
assert acc._char_charging.value == 0
async def test_missing_linked_battery_charging_sensor(hass, hk_driver, caplog):
"""Test battery service with linked_battery_charging_sensor that is mapping to a missing entity."""
@ -457,6 +478,24 @@ async def test_missing_linked_battery_charging_sensor(hass, hk_driver, caplog):
)
assert acc.linked_battery_charging_sensor is None
# Make sure we don't throw if the linked_battery_charging_sensor
# is removed
hass.states.async_remove(linked_battery_charging_sensor)
with patch(
"homeassistant.components.homekit.accessories.HomeAccessory.async_update_state"
):
await acc.run_handler()
await hass.async_block_till_done()
# Make sure we don't throw if the entity_id
# is removed
hass.states.async_remove(entity_id)
with patch(
"homeassistant.components.homekit.accessories.HomeAccessory.async_update_state"
):
await acc.run_handler()
await hass.async_block_till_done()
async def test_missing_linked_battery_sensor(hass, hk_driver, caplog):
"""Test battery service with missing linked_battery_sensor."""
@ -488,6 +527,18 @@ async def test_missing_linked_battery_sensor(hass, hk_driver, caplog):
assert acc._char_low_battery is None
assert acc._char_charging is None
with patch(
"homeassistant.components.homekit.accessories.HomeAccessory.async_update_state"
) as mock_async_update_state:
hass.states.async_remove(entity_id)
await acc.run_handler()
await hass.async_block_till_done()
assert not acc.linked_battery_sensor
assert acc._char_battery is None
assert acc._char_low_battery is None
assert acc._char_charging is None
async def test_battery_appears_after_startup(hass, hk_driver, caplog):
"""Test battery level appears after homekit is started."""
@ -514,6 +565,13 @@ async def test_battery_appears_after_startup(hass, hk_driver, caplog):
await hass.async_block_till_done()
assert acc._char_battery is None
with patch(
"homeassistant.components.homekit.accessories.HomeAccessory.async_update_state"
):
hass.states.async_remove(entity_id)
await hass.async_block_till_done()
assert acc._char_battery is None
async def test_call_service(hass, hk_driver, events):
"""Test call_service method."""