Ensure homekit state changed listeners are unsubscribed on reload (#37200)
* Ensure homekit state changed listeners are unsubscribed on reload * fix mocking
This commit is contained in:
parent
7ef33a7219
commit
0f72008090
4 changed files with 44 additions and 10 deletions
|
@ -576,6 +576,8 @@ class HomeKit:
|
||||||
self.status = STATUS_STOPPED
|
self.status = STATUS_STOPPED
|
||||||
_LOGGER.debug("Driver stop for %s", self._name)
|
_LOGGER.debug("Driver stop for %s", self._name)
|
||||||
self.hass.add_job(self.driver.stop)
|
self.hass.add_job(self.driver.stop)
|
||||||
|
for acc in self.bridge.accessories.values():
|
||||||
|
acc.async_stop()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_configure_linked_sensors(self, ent_reg_ent, device_lookup, state):
|
def _async_configure_linked_sensors(self, ent_reg_ent, device_lookup, state):
|
||||||
|
|
|
@ -270,6 +270,7 @@ class HomeAccessory(Accessory):
|
||||||
self.entity_id = entity_id
|
self.entity_id = entity_id
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.debounce = {}
|
self.debounce = {}
|
||||||
|
self._subscriptions = []
|
||||||
self._char_battery = None
|
self._char_battery = None
|
||||||
self._char_charging = None
|
self._char_charging = None
|
||||||
self._char_low_battery = None
|
self._char_low_battery = None
|
||||||
|
@ -343,8 +344,10 @@ class HomeAccessory(Accessory):
|
||||||
"""
|
"""
|
||||||
state = self.hass.states.get(self.entity_id)
|
state = self.hass.states.get(self.entity_id)
|
||||||
self.async_update_state_callback(None, None, state)
|
self.async_update_state_callback(None, None, state)
|
||||||
async_track_state_change(
|
self._subscriptions.append(
|
||||||
self.hass, self.entity_id, self.async_update_state_callback
|
async_track_state_change(
|
||||||
|
self.hass, self.entity_id, self.async_update_state_callback
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
battery_charging_state = None
|
battery_charging_state = None
|
||||||
|
@ -357,10 +360,12 @@ class HomeAccessory(Accessory):
|
||||||
battery_charging_state = linked_battery_sensor_state.attributes.get(
|
battery_charging_state = linked_battery_sensor_state.attributes.get(
|
||||||
ATTR_BATTERY_CHARGING
|
ATTR_BATTERY_CHARGING
|
||||||
)
|
)
|
||||||
async_track_state_change(
|
self._subscriptions.append(
|
||||||
self.hass,
|
async_track_state_change(
|
||||||
self.linked_battery_sensor,
|
self.hass,
|
||||||
self.async_update_linked_battery_callback,
|
self.linked_battery_sensor,
|
||||||
|
self.async_update_linked_battery_callback,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
battery_state = state.attributes.get(ATTR_BATTERY_LEVEL)
|
battery_state = state.attributes.get(ATTR_BATTERY_LEVEL)
|
||||||
|
@ -369,10 +374,12 @@ class HomeAccessory(Accessory):
|
||||||
self.hass.states.get(self.linked_battery_charging_sensor).state
|
self.hass.states.get(self.linked_battery_charging_sensor).state
|
||||||
== STATE_ON
|
== STATE_ON
|
||||||
)
|
)
|
||||||
async_track_state_change(
|
self._subscriptions.append(
|
||||||
self.hass,
|
async_track_state_change(
|
||||||
self.linked_battery_charging_sensor,
|
self.hass,
|
||||||
self.async_update_linked_battery_charging_callback,
|
self.linked_battery_charging_sensor,
|
||||||
|
self.async_update_linked_battery_charging_callback,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
elif battery_charging_state is None:
|
elif battery_charging_state is None:
|
||||||
battery_charging_state = state.attributes.get(ATTR_BATTERY_CHARGING)
|
battery_charging_state = state.attributes.get(ATTR_BATTERY_CHARGING)
|
||||||
|
@ -481,6 +488,12 @@ class HomeAccessory(Accessory):
|
||||||
self.hass.bus.async_fire(EVENT_HOMEKIT_CHANGED, event_data)
|
self.hass.bus.async_fire(EVENT_HOMEKIT_CHANGED, event_data)
|
||||||
await self.hass.services.async_call(domain, service, service_data)
|
await self.hass.services.async_call(domain, service, service_data)
|
||||||
|
|
||||||
|
@ha_callback
|
||||||
|
def async_stop(self):
|
||||||
|
"""Cancel any subscriptions when the bridge is stopped."""
|
||||||
|
while self._subscriptions:
|
||||||
|
self._subscriptions.pop(0)()
|
||||||
|
|
||||||
|
|
||||||
class HomeBridge(Bridge):
|
class HomeBridge(Bridge):
|
||||||
"""Adapter class for Bridge."""
|
"""Adapter class for Bridge."""
|
||||||
|
|
|
@ -45,6 +45,7 @@ from homeassistant.const import (
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
__version__,
|
__version__,
|
||||||
)
|
)
|
||||||
|
from homeassistant.helpers.event import TRACK_STATE_CHANGE_CALLBACKS
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from tests.async_mock import Mock, patch
|
from tests.async_mock import Mock, patch
|
||||||
|
@ -83,6 +84,22 @@ async def test_debounce(hass):
|
||||||
assert counter == 2
|
assert counter == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_accessory_cancels_track_state_change_on_stop(hass, hk_driver):
|
||||||
|
"""Ensure homekit state changed listeners are unsubscribed on reload."""
|
||||||
|
entity_id = "sensor.accessory"
|
||||||
|
hass.states.async_set(entity_id, None)
|
||||||
|
acc = HomeAccessory(
|
||||||
|
hass, hk_driver, "Home Accessory", entity_id, 2, {"platform": "isy994"}
|
||||||
|
)
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.homekit.accessories.HomeAccessory.async_update_state"
|
||||||
|
):
|
||||||
|
await acc.run_handler()
|
||||||
|
assert len(hass.data[TRACK_STATE_CHANGE_CALLBACKS][entity_id]) == 1
|
||||||
|
acc.async_stop()
|
||||||
|
assert entity_id not in hass.data[TRACK_STATE_CHANGE_CALLBACKS]
|
||||||
|
|
||||||
|
|
||||||
async def test_home_accessory(hass, hk_driver):
|
async def test_home_accessory(hass, hk_driver):
|
||||||
"""Test HomeAccessory class."""
|
"""Test HomeAccessory class."""
|
||||||
entity_id = "sensor.accessory"
|
entity_id = "sensor.accessory"
|
||||||
|
|
|
@ -605,6 +605,8 @@ async def test_homekit_stop(hass):
|
||||||
entry_id=entry.entry_id,
|
entry_id=entry.entry_id,
|
||||||
)
|
)
|
||||||
homekit.driver = Mock()
|
homekit.driver = Mock()
|
||||||
|
homekit.bridge = Mock()
|
||||||
|
homekit.bridge.accessories = {}
|
||||||
|
|
||||||
await async_init_integration(hass)
|
await async_init_integration(hass)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue