From 9db7b872884f358167365783ae5da1cb51738de9 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 2 Mar 2019 13:51:09 +0100 Subject: [PATCH 01/13] Copy state in schedule_update_ha_state --- homeassistant/helpers/entity.py | 62 ++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index dd9677f651556..a5e88c640cab2 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -200,28 +200,8 @@ def async_set_context(self, context): self._context = context self._context_set = dt_util.utcnow() - async def async_update_ha_state(self, force_refresh=False): - """Update Home Assistant with current state of entity. - - If force_refresh == True will update entity before setting state. - - This method must be run in the event loop. - """ - if self.hass is None: - raise RuntimeError("Attribute hass is None for {}".format(self)) - - if self.entity_id is None: - raise NoEntitySpecifiedError( - "No entity id specified for entity {}".format(self.name)) - - # update entity data - if force_refresh: - try: - await self.async_device_update() - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Update for %s fails", self.entity_id) - return - + def _get_state_and_attributes(self): + """Get state and attributes""" start = timer() if not self.available: @@ -281,6 +261,32 @@ async def async_update_ha_state(self, force_refresh=False): "https://goo.gl/Nvioub", self.entity_id, type(self), end - start) + return (state, attr) + + async def async_update_ha_state(self, force_refresh=False, stateattr=None): + """Update Home Assistant with current state of entity. + + If force_refresh == True will update entity before setting state. + + This method must be run in the event loop. + """ + if self.hass is None: + raise RuntimeError("Attribute hass is None for {}".format(self)) + + if self.entity_id is None: + raise NoEntitySpecifiedError( + "No entity id specified for entity {}".format(self.name)) + + # update entity data + if force_refresh: + try: + await self.async_device_update() + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Update for %s fails", self.entity_id) + return + + state, attr = stateattr or self._get_state_and_attributes() + # Overwrite properties that have been set in the config file. if DATA_CUSTOMIZE in self.hass.data: attr.update(self.hass.data[DATA_CUSTOMIZE].get(self.entity_id)) @@ -313,12 +319,20 @@ def schedule_update_ha_state(self, force_refresh=False): That avoid executor dead looks. """ - self.hass.add_job(self.async_update_ha_state(force_refresh)) + stateattr = None + if not force_refresh: + stateattr = self._get_state_and_attributes() + self.hass.add_job( + self.async_update_ha_state(force_refresh, stateattr)) @callback def async_schedule_update_ha_state(self, force_refresh=False): """Schedule an update ha state change task.""" - self.hass.async_create_task(self.async_update_ha_state(force_refresh)) + stateattr = None + if not force_refresh: + stateattr = self._get_state_and_attributes() + self.hass.async_create_task( + self.async_update_ha_state(force_refresh, stateattr)) async def async_device_update(self, warning=True): """Process 'update' or 'async_update' from entity. From 1176f1587b490d373914bd8d3ba2bc40b7ce489f Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 2 Mar 2019 14:27:46 +0100 Subject: [PATCH 02/13] Lint --- homeassistant/helpers/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index a5e88c640cab2..addbc993cabe6 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -201,7 +201,7 @@ def async_set_context(self, context): self._context_set = dt_util.utcnow() def _get_state_and_attributes(self): - """Get state and attributes""" + """Get state and attributes.""" start = timer() if not self.available: From 8b5ea222bef61342439853b1fb6dca61b5608ae4 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 3 Mar 2019 09:42:28 +0100 Subject: [PATCH 03/13] Fix broken test --- tests/components/cast/test_media_player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index b5d6220904ff7..66a975a226e6b 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -275,16 +275,16 @@ async def test_entity_media_states(hass: HomeAssistantType): state = hass.states.get('media_player.speaker') assert state.state == 'playing' - entity.new_media_status(media_status) media_status.player_is_playing = False media_status.player_is_paused = True + entity.new_media_status(media_status) await hass.async_block_till_done() state = hass.states.get('media_player.speaker') assert state.state == 'paused' - entity.new_media_status(media_status) media_status.player_is_paused = False media_status.player_is_idle = True + entity.new_media_status(media_status) await hass.async_block_till_done() state = hass.states.get('media_player.speaker') assert state.state == 'idle' From d6d5d7629017c7708ad01f92491ec1accc8ef48a Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 4 Mar 2019 07:40:14 +0100 Subject: [PATCH 04/13] Review comment, improve docstring --- homeassistant/helpers/entity.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index addbc993cabe6..c2e0920640dd9 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -285,7 +285,10 @@ async def async_update_ha_state(self, force_refresh=False, stateattr=None): _LOGGER.exception("Update for %s fails", self.entity_id) return - state, attr = stateattr or self._get_state_and_attributes() + if force_refresh or stateattr is None: + state, attr = self._get_state_and_attributes() + else: + state, attr = stateattr # Overwrite properties that have been set in the config file. if DATA_CUSTOMIZE in self.hass.data: @@ -317,7 +320,9 @@ async def async_update_ha_state(self, force_refresh=False, stateattr=None): def schedule_update_ha_state(self, force_refresh=False): """Schedule an update ha state change task. - That avoid executor dead looks. + Scheduling the update avoids executor dead looks. + The state is copied unless force_refresh is set to true to not miss + state transitions happening before async_update_ha_state is called. """ stateattr = None if not force_refresh: @@ -327,7 +332,12 @@ def schedule_update_ha_state(self, force_refresh=False): @callback def async_schedule_update_ha_state(self, force_refresh=False): - """Schedule an update ha state change task.""" + """Schedule an update ha state change task. + + Scheduling the update avoids executor dead looks. + The state is copied unless force_refresh is set to true to not miss + state transitions happening before async_update_ha_state is called. + """ stateattr = None if not force_refresh: stateattr = self._get_state_and_attributes() From 260e3b050631e692c232917841dfacc48780faad Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 4 Mar 2019 15:09:33 +0100 Subject: [PATCH 05/13] Preserve order of state updates --- homeassistant/helpers/entity.py | 62 ++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index c2e0920640dd9..ac3b246742526 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -263,10 +263,9 @@ def _get_state_and_attributes(self): return (state, attr) - async def async_update_ha_state(self, force_refresh=False, stateattr=None): - """Update Home Assistant with current state of entity. - - If force_refresh == True will update entity before setting state. + @callback + def _async_update_ha_state(self, state, attr): + """Update Home Assistant with state of entity. This method must be run in the event loop. """ @@ -277,19 +276,6 @@ async def async_update_ha_state(self, force_refresh=False, stateattr=None): raise NoEntitySpecifiedError( "No entity id specified for entity {}".format(self.name)) - # update entity data - if force_refresh: - try: - await self.async_device_update() - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Update for %s fails", self.entity_id) - return - - if force_refresh or stateattr is None: - state, attr = self._get_state_and_attributes() - else: - state, attr = stateattr - # Overwrite properties that have been set in the config file. if DATA_CUSTOMIZE in self.hass.data: attr.update(self.hass.data[DATA_CUSTOMIZE].get(self.entity_id)) @@ -317,6 +303,31 @@ async def async_update_ha_state(self, force_refresh=False, stateattr=None): self.hass.states.async_set( self.entity_id, state, attr, self.force_update, self._context) + async def async_update_ha_state(self, force_refresh=False): + """Update Home Assistant with current state of entity. + + If force_refresh == True will update entity before setting state. + + This method must be run in the event loop. + """ + if self.hass is None: + raise RuntimeError("Attribute hass is None for {}".format(self)) + + if self.entity_id is None: + raise NoEntitySpecifiedError( + "No entity id specified for entity {}".format(self.name)) + + # update entity data + if force_refresh: + try: + await self.async_device_update() + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Update for %s fails", self.entity_id) + return + + state, attr = self._get_state_and_attributes() + self._async_update_ha_state(state, attr) + def schedule_update_ha_state(self, force_refresh=False): """Schedule an update ha state change task. @@ -324,11 +335,11 @@ def schedule_update_ha_state(self, force_refresh=False): The state is copied unless force_refresh is set to true to not miss state transitions happening before async_update_ha_state is called. """ - stateattr = None if not force_refresh: - stateattr = self._get_state_and_attributes() - self.hass.add_job( - self.async_update_ha_state(force_refresh, stateattr)) + state, attr = self._get_state_and_attributes() + self.hass.add_job(self._async_update_ha_state, state, attr) + else: + self.hass.add_job(self.async_update_ha_state(force_refresh)) @callback def async_schedule_update_ha_state(self, force_refresh=False): @@ -338,11 +349,12 @@ def async_schedule_update_ha_state(self, force_refresh=False): The state is copied unless force_refresh is set to true to not miss state transitions happening before async_update_ha_state is called. """ - stateattr = None if not force_refresh: - stateattr = self._get_state_and_attributes() - self.hass.async_create_task( - self.async_update_ha_state(force_refresh, stateattr)) + state, attr = self._get_state_and_attributes() + self.hass.add_job(self._async_update_ha_state, state, attr) + else: + self.hass.async_create_task( + self.async_update_ha_state(force_refresh)) async def async_device_update(self, warning=True): """Process 'update' or 'async_update' from entity. From 02ff5e79cc421a3eab873991c48a09c45d76a63f Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 6 Mar 2019 16:35:59 +0100 Subject: [PATCH 06/13] Rewrite --- .../components/mqtt/binary_sensor.py | 6 +-- homeassistant/helpers/entity.py | 52 +++++++++---------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index cb93712776cb6..99d0cf1d9fb2a 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -117,7 +117,7 @@ async def discovery_update(self, discovery_payload): await self.availability_discovery_update(config) await self.device_info_discovery_update(config) await self._subscribe_topics() - self.async_schedule_update_ha_state() + self.update_ha_state() async def _subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -130,7 +130,7 @@ def off_delay_listener(now): """Switch device off after a delay.""" self._delay_listener = None self._state = False - self.async_schedule_update_ha_state() + self.update_ha_state() @callback def state_message_received(_topic, payload, _qos): @@ -159,7 +159,7 @@ def state_message_received(_topic, payload, _qos): self._delay_listener = evt.async_call_later( self.hass, off_delay, off_delay_listener) - self.async_schedule_update_ha_state() + self.update_ha_state() self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index ac3b246742526..2560718823a4f 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -263,19 +263,11 @@ def _get_state_and_attributes(self): return (state, attr) - @callback def _async_update_ha_state(self, state, attr): """Update Home Assistant with state of entity. This method must be run in the event loop. """ - if self.hass is None: - raise RuntimeError("Attribute hass is None for {}".format(self)) - - if self.entity_id is None: - raise NoEntitySpecifiedError( - "No entity id specified for entity {}".format(self.name)) - # Overwrite properties that have been set in the config file. if DATA_CUSTOMIZE in self.hass.data: attr.update(self.hass.data[DATA_CUSTOMIZE].get(self.entity_id)) @@ -303,10 +295,24 @@ def _async_update_ha_state(self, state, attr): self.hass.states.async_set( self.entity_id, state, attr, self.force_update, self._context) - async def async_update_ha_state(self, force_refresh=False): + @callback + def update_ha_state(self): """Update Home Assistant with current state of entity. - If force_refresh == True will update entity before setting state. + This method must be run in the event loop. + """ + if self.hass is None: + raise RuntimeError("Attribute hass is None for {}".format(self)) + + if self.entity_id is None: + raise NoEntitySpecifiedError( + "No entity id specified for entity {}".format(self.name)) + + state, attr = self._get_state_and_attributes() + self._async_update_ha_state(state, attr) + + async def async_update_ha_state(self, force_refresh=False): + """Update Home Assistant with current state of entity. This method must be run in the event loop. """ @@ -329,32 +335,24 @@ async def async_update_ha_state(self, force_refresh=False): self._async_update_ha_state(state, attr) def schedule_update_ha_state(self, force_refresh=False): - """Schedule an update ha state change task. + """Schedule an update ha state change task from other thread. - Scheduling the update avoids executor dead looks. - The state is copied unless force_refresh is set to true to not miss - state transitions happening before async_update_ha_state is called. + If state is changed again before the ha state change task has been + executed, state transitions will be missed. """ - if not force_refresh: - state, attr = self._get_state_and_attributes() - self.hass.add_job(self._async_update_ha_state, state, attr) - else: - self.hass.add_job(self.async_update_ha_state(force_refresh)) + self.hass.add_job(self.async_update_ha_state(force_refresh)) @callback def async_schedule_update_ha_state(self, force_refresh=False): """Schedule an update ha state change task. Scheduling the update avoids executor dead looks. - The state is copied unless force_refresh is set to true to not miss - state transitions happening before async_update_ha_state is called. + If state is changed again before the ha state change task has been + executed, state transitions will be missed. + + This method must be run in the event loop. """ - if not force_refresh: - state, attr = self._get_state_and_attributes() - self.hass.add_job(self._async_update_ha_state, state, attr) - else: - self.hass.async_create_task( - self.async_update_ha_state(force_refresh)) + self.hass.async_create_task(self.async_update_ha_state(force_refresh)) async def async_device_update(self, warning=True): """Process 'update' or 'async_update' from entity. From fc477bc8dc58482bd88c75fe560962b53822dfdb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 6 Mar 2019 22:09:18 -0800 Subject: [PATCH 07/13] Break up async_update_ha_state --- homeassistant/helpers/entity.py | 89 +++++++++++---------------------- 1 file changed, 30 insertions(+), 59 deletions(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 2560718823a4f..ab1d440c6e567 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -200,8 +200,33 @@ def async_set_context(self, context): self._context = context self._context_set = dt_util.utcnow() - def _get_state_and_attributes(self): - """Get state and attributes.""" + async def async_update_ha_state(self, force_refresh=False): + """Update Home Assistant with current state of entity. + + If force_refresh == True will update entity before setting state. + + This method must be run in the event loop. + """ + # update entity data + if force_refresh: + try: + await self.async_device_update() + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Update for %s fails", self.entity_id) + return + + self.async_write_ha_state() + + @callback + def async_write_ha_state(self): + """Write the state to the state machine.""" + if self.hass is None: + raise RuntimeError("Attribute hass is None for {}".format(self)) + + if self.entity_id is None: + raise NoEntitySpecifiedError( + "No entity id specified for entity {}".format(self.name)) + start = timer() if not self.available: @@ -261,13 +286,6 @@ def _get_state_and_attributes(self): "https://goo.gl/Nvioub", self.entity_id, type(self), end - start) - return (state, attr) - - def _async_update_ha_state(self, state, attr): - """Update Home Assistant with state of entity. - - This method must be run in the event loop. - """ # Overwrite properties that have been set in the config file. if DATA_CUSTOMIZE in self.hass.data: attr.update(self.hass.data[DATA_CUSTOMIZE].get(self.entity_id)) @@ -295,63 +313,16 @@ def _async_update_ha_state(self, state, attr): self.hass.states.async_set( self.entity_id, state, attr, self.force_update, self._context) - @callback - def update_ha_state(self): - """Update Home Assistant with current state of entity. - - This method must be run in the event loop. - """ - if self.hass is None: - raise RuntimeError("Attribute hass is None for {}".format(self)) - - if self.entity_id is None: - raise NoEntitySpecifiedError( - "No entity id specified for entity {}".format(self.name)) - - state, attr = self._get_state_and_attributes() - self._async_update_ha_state(state, attr) - - async def async_update_ha_state(self, force_refresh=False): - """Update Home Assistant with current state of entity. - - This method must be run in the event loop. - """ - if self.hass is None: - raise RuntimeError("Attribute hass is None for {}".format(self)) - - if self.entity_id is None: - raise NoEntitySpecifiedError( - "No entity id specified for entity {}".format(self.name)) - - # update entity data - if force_refresh: - try: - await self.async_device_update() - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Update for %s fails", self.entity_id) - return - - state, attr = self._get_state_and_attributes() - self._async_update_ha_state(state, attr) - def schedule_update_ha_state(self, force_refresh=False): - """Schedule an update ha state change task from other thread. + """Schedule an update ha state change task. - If state is changed again before the ha state change task has been - executed, state transitions will be missed. + That avoid executor dead looks. """ self.hass.add_job(self.async_update_ha_state(force_refresh)) @callback def async_schedule_update_ha_state(self, force_refresh=False): - """Schedule an update ha state change task. - - Scheduling the update avoids executor dead looks. - If state is changed again before the ha state change task has been - executed, state transitions will be missed. - - This method must be run in the event loop. - """ + """Schedule an update ha state change task.""" self.hass.async_create_task(self.async_update_ha_state(force_refresh)) async def async_device_update(self, warning=True): From c4b57895a22751be30919897d2ad5c7588d43ba0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 6 Mar 2019 22:09:51 -0800 Subject: [PATCH 08/13] Update binary_sensor.py --- homeassistant/components/mqtt/binary_sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 99d0cf1d9fb2a..103958376c00f 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -117,7 +117,7 @@ async def discovery_update(self, discovery_payload): await self.availability_discovery_update(config) await self.device_info_discovery_update(config) await self._subscribe_topics() - self.update_ha_state() + self.async_write_ha_state() async def _subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -130,7 +130,7 @@ def off_delay_listener(now): """Switch device off after a delay.""" self._delay_listener = None self._state = False - self.update_ha_state() + self.async_write_ha_state() @callback def state_message_received(_topic, payload, _qos): @@ -159,7 +159,7 @@ def state_message_received(_topic, payload, _qos): self._delay_listener = evt.async_call_later( self.hass, off_delay, off_delay_listener) - self.update_ha_state() + self.async_write_ha_state() self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, From 94591705556b9daf712455b869f5dc30fa740210 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 7 Mar 2019 15:56:53 +0100 Subject: [PATCH 09/13] Review comments --- homeassistant/components/mqtt/binary_sensor.py | 6 +++--- homeassistant/helpers/entity.py | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 103958376c00f..2247093083be9 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -117,7 +117,7 @@ async def discovery_update(self, discovery_payload): await self.availability_discovery_update(config) await self.device_info_discovery_update(config) await self._subscribe_topics() - self.async_write_ha_state() + self.async_write_hass_state() async def _subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -130,7 +130,7 @@ def off_delay_listener(now): """Switch device off after a delay.""" self._delay_listener = None self._state = False - self.async_write_ha_state() + self.async_write_hass_state() @callback def state_message_received(_topic, payload, _qos): @@ -159,7 +159,7 @@ def state_message_received(_topic, payload, _qos): self._delay_listener = evt.async_call_later( self.hass, off_delay, off_delay_listener) - self.async_write_ha_state() + self.async_write_hass_state() self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index ab1d440c6e567..2d01576ad3140 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -207,6 +207,13 @@ async def async_update_ha_state(self, force_refresh=False): This method must be run in the event loop. """ + if self.hass is None: + raise RuntimeError("Attribute hass is None for {}".format(self)) + + if self.entity_id is None: + raise NoEntitySpecifiedError( + "No entity id specified for entity {}".format(self.name)) + # update entity data if force_refresh: try: @@ -215,10 +222,10 @@ async def async_update_ha_state(self, force_refresh=False): _LOGGER.exception("Update for %s fails", self.entity_id) return - self.async_write_ha_state() + self._async_write_hass_state() @callback - def async_write_ha_state(self): + def async_write_hass_state(self): """Write the state to the state machine.""" if self.hass is None: raise RuntimeError("Attribute hass is None for {}".format(self)) @@ -227,6 +234,11 @@ def async_write_ha_state(self): raise NoEntitySpecifiedError( "No entity id specified for entity {}".format(self.name)) + self._async_write_hass_state() + + @callback + def _async_write_hass_state(self): + """Write the state to the state machine.""" start = timer() if not self.available: From cf641cd6642bff8f1dd52f74e10331c1ee1133f1 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 7 Mar 2019 16:05:01 +0100 Subject: [PATCH 10/13] Update docstring --- homeassistant/helpers/entity.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 2d01576ad3140..ed82942bfaac0 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -328,13 +328,27 @@ def _async_write_hass_state(self): def schedule_update_ha_state(self, force_refresh=False): """Schedule an update ha state change task. - That avoid executor dead looks. + Scheduling the update avoids executor dead looks. + + Entity state and attributes are read when the update ha state change + task is executed. + If state is changed more than once before the ha state change task has + been executed, the intermediate state transitions will be missed. """ self.hass.add_job(self.async_update_ha_state(force_refresh)) @callback def async_schedule_update_ha_state(self, force_refresh=False): - """Schedule an update ha state change task.""" + """Schedule an update ha state change task. + + This method must be run in the event loop. + Scheduling the update avoids executor dead looks. + + Entity state and attributes are read when the update ha state change + task is executed. + If state is changed more than once before the ha state change task has + been executed, the intermediate state transitions will be missed. + """ self.hass.async_create_task(self.async_update_ha_state(force_refresh)) async def async_device_update(self, warning=True): From 83bd8ae129f6040c3de94de310c0d3fbf75b49da Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 7 Mar 2019 16:15:21 +0100 Subject: [PATCH 11/13] hass -> ha --- homeassistant/components/mqtt/binary_sensor.py | 6 +++--- homeassistant/helpers/entity.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 2247093083be9..103958376c00f 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -117,7 +117,7 @@ async def discovery_update(self, discovery_payload): await self.availability_discovery_update(config) await self.device_info_discovery_update(config) await self._subscribe_topics() - self.async_write_hass_state() + self.async_write_ha_state() async def _subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -130,7 +130,7 @@ def off_delay_listener(now): """Switch device off after a delay.""" self._delay_listener = None self._state = False - self.async_write_hass_state() + self.async_write_ha_state() @callback def state_message_received(_topic, payload, _qos): @@ -159,7 +159,7 @@ def state_message_received(_topic, payload, _qos): self._delay_listener = evt.async_call_later( self.hass, off_delay, off_delay_listener) - self.async_write_hass_state() + self.async_write_ha_state() self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index ed82942bfaac0..00ccad629b9c5 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -222,10 +222,10 @@ async def async_update_ha_state(self, force_refresh=False): _LOGGER.exception("Update for %s fails", self.entity_id) return - self._async_write_hass_state() + self._async_write_ha_state() @callback - def async_write_hass_state(self): + def async_write_ha_state(self): """Write the state to the state machine.""" if self.hass is None: raise RuntimeError("Attribute hass is None for {}".format(self)) @@ -237,7 +237,7 @@ def async_write_hass_state(self): self._async_write_hass_state() @callback - def _async_write_hass_state(self): + def _async_write_ha_state(self): """Write the state to the state machine.""" start = timer() From a37f23bcce66bfa1d632db3e0824f835d7ab879c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 7 Mar 2019 09:58:15 -0800 Subject: [PATCH 12/13] Update entity.py --- homeassistant/helpers/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 00ccad629b9c5..6b7fdd08c9e4a 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -234,7 +234,7 @@ def async_write_ha_state(self): raise NoEntitySpecifiedError( "No entity id specified for entity {}".format(self.name)) - self._async_write_hass_state() + self._async_write_ha_state() @callback def _async_write_ha_state(self): From 6c000203cd692195bef61687180c3d46d62be8ac Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 7 Mar 2019 09:59:08 -0800 Subject: [PATCH 13/13] Update entity.py --- homeassistant/helpers/entity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 6b7fdd08c9e4a..4ef5513baf7b1 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -328,7 +328,7 @@ def _async_write_ha_state(self): def schedule_update_ha_state(self, force_refresh=False): """Schedule an update ha state change task. - Scheduling the update avoids executor dead looks. + Scheduling the update avoids executor deadlocks. Entity state and attributes are read when the update ha state change task is executed. @@ -342,7 +342,7 @@ def async_schedule_update_ha_state(self, force_refresh=False): """Schedule an update ha state change task. This method must be run in the event loop. - Scheduling the update avoids executor dead looks. + Scheduling the update avoids executor deadlocks. Entity state and attributes are read when the update ha state change task is executed.