From 5cd6370822bfb7a7c1925492b8247e6d0287a04e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Jul 2020 09:40:01 -1000 Subject: [PATCH] Switch async_track_state_change to the faster async_track_state_change_event part 3 (#37852) async_track_state_change_event is faster than async_track_state_change --- .../components/bayesian/binary_sensor.py | 14 ++- homeassistant/components/esphome/__init__.py | 29 ++++-- homeassistant/components/filter/sensor.py | 91 ++++++++++--------- 3 files changed, 76 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/bayesian/binary_sensor.py b/homeassistant/components/bayesian/binary_sensor.py index bd0fa797fd0..1107a682039 100644 --- a/homeassistant/components/bayesian/binary_sensor.py +++ b/homeassistant/components/bayesian/binary_sensor.py @@ -18,7 +18,7 @@ from homeassistant.const import ( from homeassistant.core import callback from homeassistant.helpers import condition import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.event import async_track_state_change +from homeassistant.helpers.event import async_track_state_change_event ATTR_OBSERVATIONS = "observations" ATTR_OCCURRED_OBSERVATION_ENTITIES = "occurred_observation_entities" @@ -151,16 +151,20 @@ class BayesianBinarySensor(BinarySensorEntity): """ @callback - def async_threshold_sensor_state_listener(entity, _old_state, new_state): + def async_threshold_sensor_state_listener(event): """ Handle sensor state changes. When a state changes, we must update our list of current observations, then calculate the new probability. """ - if new_state.state == STATE_UNKNOWN: + new_state = event.data.get("new_state") + + if new_state is None or new_state.state == STATE_UNKNOWN: return + entity = event.data.get("entity_id") + self.current_observations.update(self._record_entity_observations(entity)) self.probability = self._calculate_new_probability() @@ -168,9 +172,9 @@ class BayesianBinarySensor(BinarySensorEntity): self.current_observations.update(self._initialize_current_observations()) self.probability = self._calculate_new_probability() - async_track_state_change( + async_track_state_change_event( self.hass, - self.observations_by_entity, + list(self.observations_by_entity), async_threshold_sensor_state_listener, ) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 915eed773dd..cdc9074c5ab 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -31,7 +31,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.helpers.device_registry as dr from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import async_track_state_change +from homeassistant.helpers.event import async_track_state_change_event from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.storage import Store from homeassistant.helpers.template import Template @@ -133,23 +133,32 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool ) ) - async def send_home_assistant_state( - entity_id: str, _, new_state: Optional[State] - ) -> None: - """Forward Home Assistant states to ESPHome.""" + async def send_home_assistant_state_event(event: Event) -> None: + """Forward Home Assistant states updates to ESPHome.""" + new_state = event.data.get("new_state") if new_state is None: return + entity_id = event.data.get("entity_id") + await cli.send_home_assistant_state(entity_id, new_state.state) + + async def _send_home_assistant_state( + entity_id: str, new_state: Optional[State] + ) -> None: + """Forward Home Assistant states to ESPHome.""" await cli.send_home_assistant_state(entity_id, new_state.state) @callback def async_on_state_subscription(entity_id: str) -> None: """Subscribe and forward states for requested entities.""" - unsub = async_track_state_change(hass, entity_id, send_home_assistant_state) - entry_data.disconnect_callbacks.append(unsub) - # Send initial state - hass.async_create_task( - send_home_assistant_state(entity_id, None, hass.states.get(entity_id)) + unsub = async_track_state_change_event( + hass, [entity_id], send_home_assistant_state_event ) + entry_data.disconnect_callbacks.append(unsub) + new_state = hass.states.get(entity_id) + if new_state is None: + return + # Send initial state + hass.async_create_task(_send_home_assistant_state(entity_id, new_state)) async def on_login() -> None: """Subscribe to states and list entities on successful API login.""" diff --git a/homeassistant/components/filter/sensor.py b/homeassistant/components/filter/sensor.py index 7c2a35938b2..e46af9fa138 100644 --- a/homeassistant/components/filter/sensor.py +++ b/homeassistant/components/filter/sensor.py @@ -24,7 +24,7 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import async_track_state_change +from homeassistant.helpers.event import async_track_state_change_event from homeassistant.util.decorator import Registry import homeassistant.util.dt as dt_util @@ -173,47 +173,52 @@ class SensorFilter(Entity): self._filters = filters self._icon = None + @callback + def _update_filter_sensor_state_event(self, event): + """Handle device state changes.""" + self._update_filter_sensor_state(event.data.get("new_state")) + + @callback + def _update_filter_sensor_state(self, new_state, update_ha=True): + """Process device state changes.""" + if new_state is None or new_state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]: + return + + temp_state = new_state + + try: + for filt in self._filters: + filtered_state = filt.filter_state(copy(temp_state)) + _LOGGER.debug( + "%s(%s=%s) -> %s", + filt.name, + self._entity, + temp_state.state, + "skip" if filt.skip_processing else filtered_state.state, + ) + if filt.skip_processing: + return + temp_state = filtered_state + except ValueError: + _LOGGER.error("Could not convert state: %s to number", self._state) + return + + self._state = temp_state.state + + if self._icon is None: + self._icon = new_state.attributes.get(ATTR_ICON, ICON) + + if self._unit_of_measurement is None: + self._unit_of_measurement = new_state.attributes.get( + ATTR_UNIT_OF_MEASUREMENT + ) + + if update_ha: + self.async_write_ha_state() + async def async_added_to_hass(self): """Register callbacks.""" - @callback - def filter_sensor_state_listener(entity, old_state, new_state, update_ha=True): - """Handle device state changes.""" - if new_state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]: - return - - temp_state = new_state - - try: - for filt in self._filters: - filtered_state = filt.filter_state(copy(temp_state)) - _LOGGER.debug( - "%s(%s=%s) -> %s", - filt.name, - self._entity, - temp_state.state, - "skip" if filt.skip_processing else filtered_state.state, - ) - if filt.skip_processing: - return - temp_state = filtered_state - except ValueError: - _LOGGER.error("Could not convert state: %s to number", self._state) - return - - self._state = temp_state.state - - if self._icon is None: - self._icon = new_state.attributes.get(ATTR_ICON, ICON) - - if self._unit_of_measurement is None: - self._unit_of_measurement = new_state.attributes.get( - ATTR_UNIT_OF_MEASUREMENT - ) - - if update_ha: - self.async_write_ha_state() - if "recorder" in self.hass.config.components: history_list = [] largest_window_items = 0 @@ -271,12 +276,12 @@ class SensorFilter(Entity): ) # Replay history through the filter chain - prev_state = None for state in history_list: - filter_sensor_state_listener(self._entity, prev_state, state, False) - prev_state = state + self._update_filter_sensor_state(state, False) - async_track_state_change(self.hass, self._entity, filter_sensor_state_listener) + async_track_state_change_event( + self.hass, [self._entity], self._update_filter_sensor_state_event + ) @property def name(self):