Cleanup async_track_state_change and augment docstring (#37251)

* Cleanup async_track_state_change and augment docstrings.

Skip from_state and to_state matching in
async_track_state_change when they are None

Optimize the state change listener for the most
common use case: no to_state and from_state
matching.

* Update benchmark to be more realistic (previously we assumed only one entity was present in the whole instance)

* Add more tests to ensure behavior is preserved

* Ensure new behavior matches test

* remove MATCH_ALL from zone automation since its the default anyways

* Might as well use async_track_state_change_event instead since MATCH_ALL is removed
This commit is contained in:
J. Nick Koston 2020-07-05 17:31:33 -05:00 committed by GitHub
parent 2088092f7c
commit 34ccb6588c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 163 additions and 32 deletions

View file

@ -72,10 +72,16 @@ def async_track_state_change(
Returns a function that can be called to remove the listener.
If entity_ids are not MATCH_ALL along with from_state and to_state
being None, async_track_state_change_event should be used instead
as it is slightly faster.
Must be run within the event loop.
"""
match_from_state = process_state_match(from_state)
match_to_state = process_state_match(to_state)
if from_state is not None:
match_from_state = process_state_match(from_state)
if to_state is not None:
match_to_state = process_state_match(to_state)
# Ensure it is a lowercase list with entity ids we want to match on
if entity_ids == MATCH_ALL:
@ -88,21 +94,27 @@ def async_track_state_change(
@callback
def state_change_listener(event: Event) -> None:
"""Handle specific state changes."""
old_state = event.data.get("old_state")
if old_state is not None:
old_state = old_state.state
if from_state is not None:
old_state = event.data.get("old_state")
if old_state is not None:
old_state = old_state.state
new_state = event.data.get("new_state")
if new_state is not None:
new_state = new_state.state
if not match_from_state(old_state):
return
if to_state is not None:
new_state = event.data.get("new_state")
if new_state is not None:
new_state = new_state.state
if match_from_state(old_state) and match_to_state(new_state):
hass.async_run_job(
action,
event.data.get("entity_id"),
event.data.get("old_state"),
event.data.get("new_state"),
)
if not match_to_state(new_state):
return
hass.async_run_job(
action,
event.data.get("entity_id"),
event.data.get("old_state"),
event.data.get("new_state"),
)
if entity_ids != MATCH_ALL:
# If we have a list of entity ids we use
@ -565,5 +577,5 @@ def process_state_match(
if isinstance(parameter, str) or not hasattr(parameter, "__iter__"):
return lambda state: state == parameter
parameter_tuple = tuple(parameter)
return lambda state: state in parameter_tuple
parameter_set = set(parameter)
return lambda state: state in parameter_set