Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix automations listening to HOMEASSISTANT_START #6936

Merged
merged 14 commits into from
Apr 6, 2017
Merged

Conversation

balloob
Copy link
Member

@balloob balloob commented Apr 4, 2017

Description:

To prevent false triggers, we only enable automations after all components are setup. This however introduced a breaking change: we no longer allow people to listen to HOMEASSISTANT_START.

This PR is just to start discussion, the included fix is actually a bad one.

I still want people to be able to run something on startup.

  • Make it backwards compatible by checking if listening for HOMEASSISTANT_START event and hass.state == CoreState.starting. This is a hack.
  • Add a new special automation platform that runs things on startup (and also checks against hass.state)

This PR forced me to fix how CoreState is set and ensures we wait till all tasks done before we start firing the timer. This caused some changes to how we mock HASS in tests.

Breaking changes

  • Deprecation: using automation event platform to listen for homeassistant_start on startup is deprecated and will be removed in Home Assistant 0.45. Use the new automation homeassistant platform instead.
  • Devs: during startup, hass.state is set to CoreState.starting. All tasks will be awaited during startup and the timer will only start when that is done. At that point in time hass.state is CoreState.running.

Configuration

automation:
  trigger:
    platform: homeassistant
    event: start
  action:
    service: light.turn_on

automation 2:
  trigger:
    platform: homeassistant
    event: shutdown
  action:
    service: light.turn_on

# Will work but raises a warning.
automation deprecated:
  trigger:
    platform: event
    event: homeassistant_start
  action:
    service: light.turn_on

Checklist:

If the code does not interact with devices:

  • Local tests with tox run successfully. Your PR cannot be merged unless tests pass
  • Tests have been added to verify that the new code works.

@mention-bot
Copy link

@balloob, thanks for your PR! By analyzing the history of the files in this pull request, we identified @andythigpen, @fabaff and @pvizeli to be potential reviewers.

@pvizeli
Copy link
Member

pvizeli commented Apr 4, 2017

I think the seconds variant is not realy bad. With this special automation platform we allow to trigger on startup and shutdown. Other automation make it also in this way. Maybe we have other case for that platform in future.

like:

action:
  platform: homeassistant
  state: startup
...
action:
  platform: homeassistant
  state: shutdown

I like that.

@balloob
Copy link
Member Author

balloob commented Apr 5, 2017

Alright. Will support the old method but deprecate it. Then will also add the new platform.

@balloob balloob force-pushed the automations-on-startup branch from 4da2c63 to 3b46e44 Compare April 5, 2017 04:52
assert automation.is_on(hass, 'automation.hello')
assert len(calls) == 0

with patch.object(hass.loop, 'stop'), patch.object(hass.executor, 'shutdown'):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (82 > 79 characters)

@@ -74,8 +74,6 @@ def async_from_config_dict(config: Dict[str, Any],
This method is a coroutine.
"""
start = time()
hass.async_track_tasks()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Track tasks is now enabled by default. It's what we want during startup.

@@ -368,10 +367,6 @@ def async_fire(self, event_type: str, event_data=None,

This method must be run in the event loop.
"""
if event_type != EVENT_HOMEASSISTANT_STOP and \
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was silently (because exception suppressed) killing shutdown tasks (noticed it when adding shutdown event in automation). It should be fine to just wait till all is done. Timer is already stopped so nothing new should come in.

self.bus.async_fire(EVENT_HOMEASSISTANT_START)
yield from self.async_stop_track_tasks()
Copy link
Member Author

@balloob balloob Apr 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this fixes the fact that CoreState.starting was never detectable before, as we would immediately overwrite it.

However, this has the downside that it is impossible for components/platforms to start a permanent async job as a result from EVENT_HOMEASSISTANT_START.

(note that things like MQTT server rely on loop.create_server which will work. Z-Wave depends on a thread and that will work)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a timeout to async_stop_track_tasks after which we will no longer stop tracking tasks?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that should not be needed to add a timeout

@@ -252,7 +246,8 @@ def async_block_till_done(self):

def stop(self) -> None:
"""Stop Home Assistant and shuts down all threads."""
run_coroutine_threadsafe(self.async_stop(), self.loop)
self.loop.call_soon_threadsafe(
self.loop.create_task, self.async_stop())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not only call loop.create_task?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because that's not threadsafe

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are sure? I found no comments in doc that will be not threadsafe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.python.org/3/library/asyncio-dev.html#concurrency-and-multithreading

To schedule a coroutine object from a different thread, the run_coroutine_threadsafe() function should be used.

Also, the tests won't pass if I just call create_task.

Copy link
Member Author

@balloob balloob Apr 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(note, we include the run_coroutine_threadsafe function from Python in HASS because it was only introduced in Python 3.5.1)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asyncio.ensure_future maby the same but don't support with python 3.4.2

@balloob balloob merged commit 29f385e into dev Apr 6, 2017
@balloob balloob deleted the automations-on-startup branch April 6, 2017 06:23
@fabaff fabaff mentioned this pull request Apr 6, 2017
@home-assistant home-assistant locked and limited conversation to collaborators Jul 17, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants