Esempio n. 1
0
    def async_track_point_in_utc_time(hass, action, point_in_time):
        """Add a listener that fires once after a specific point in UTC time."""
        # Ensure point_in_time is UTC
        point_in_time = event.dt_util.as_utc(point_in_time)

        # Since this is called once, we accept a HassJob so we can avoid
        # having to figure out how to call the action every time its called.
        job = action if isinstance(action, ha.HassJob) else ha.HassJob(action)

        @ha.callback
        def point_in_time_listener(event):
            """Listen for matching time_changed events."""
            now = event.data[ATTR_NOW]

            if now < point_in_time or hasattr(point_in_time_listener, "run"):
                return

            # Set variable so that we will never run twice.
            # Because the event bus might have to wait till a thread comes
            # available to execute this listener it might occur that the
            # listener gets lined up twice to be executed. This will make
            # sure the second time it does nothing.
            setattr(point_in_time_listener, "run", True)
            async_unsub()

            hass.async_run_hass_job(job, now)

        async_unsub = hass.bus.async_listen(EVENT_TIME_CHANGED,
                                            point_in_time_listener)

        return async_unsub
Esempio n. 2
0
def async_listen_platform(
    hass: core.HomeAssistant,
    component: str,
    callback: Callable[[str, Optional[Dict[str, Any]]], Any],
) -> None:
    """Register a platform loader listener.

    This method must be run in the event loop.
    """
    service = EVENT_LOAD_PLATFORM.format(component)
    job = core.HassJob(callback)

    async def discovery_platform_listener(event: core.Event) -> None:
        """Listen for platform discovery events."""
        if event.data.get(ATTR_SERVICE) != service:
            return

        platform = event.data.get(ATTR_PLATFORM)

        if not platform:
            return

        task = hass.async_run_hass_job(job, platform,
                                       event.data.get(ATTR_DISCOVERED))
        if task:
            await task

    hass.bus.async_listen(EVENT_PLATFORM_DISCOVERED,
                          discovery_platform_listener)
Esempio n. 3
0
def async_listen_platform(
    hass: core.HomeAssistant,
    component: str,
    callback: Callable[[str, dict[str, Any] | None], Any],
) -> None:
    """Register a platform loader listener.

    This method must be run in the event loop.
    """
    service = EVENT_LOAD_PLATFORM.format(component)
    job = core.HassJob(callback)

    async def discovery_platform_listener(discovered: DiscoveryDict) -> None:
        """Listen for platform discovery events."""
        platform = discovered["platform"]

        if not platform:
            return

        task = hass.async_run_hass_job(job, platform, discovered.get("discovered"))
        if task:
            await task

    async_dispatcher_connect(
        hass, SIGNAL_PLATFORM_DISCOVERED.format(service), discovery_platform_listener
    )
Esempio n. 4
0
def async_listen(
    hass: core.HomeAssistant,
    service: Union[str, Collection[str]],
    callback: CALLBACK_TYPE,
) -> None:
    """Set up listener for discovery of specific service.

    Service can be a string or a list/tuple.
    """
    if isinstance(service, str):
        service = (service, )
    else:
        service = tuple(service)

    job = core.HassJob(callback)

    async def discovery_event_listener(event: core.Event) -> None:
        """Listen for discovery events."""
        if ATTR_SERVICE in event.data and event.data[ATTR_SERVICE] in service:
            task = hass.async_run_hass_job(job, event.data[ATTR_SERVICE],
                                           event.data.get(ATTR_DISCOVERED))
            if task:
                await task

    hass.bus.async_listen(EVENT_PLATFORM_DISCOVERED, discovery_event_listener)
Esempio n. 5
0
    def async_track_utc_time_change(
        hass, action, hour=None, minute=None, second=None, local=False
    ):
        """Add a listener that will fire if time matches a pattern."""

        job = ha.HassJob(action)
        # We do not have to wrap the function with time pattern matching logic
        # if no pattern given
        if all(val is None for val in (hour, minute, second)):

            @ha.callback
            def time_change_listener(ev) -> None:
                """Fire every time event that comes in."""
                hass.async_run_hass_job(job, ev.data[ATTR_NOW])

            return hass.bus.async_listen(EVENT_TIME_CHANGED, time_change_listener)

        matching_seconds = event.dt_util.parse_time_expression(second, 0, 59)
        matching_minutes = event.dt_util.parse_time_expression(minute, 0, 59)
        matching_hours = event.dt_util.parse_time_expression(hour, 0, 23)

        next_time = None

        def calculate_next(now) -> None:
            """Calculate and set the next time the trigger should fire."""
            nonlocal next_time

            localized_now = event.dt_util.as_local(now) if local else now
            next_time = event.dt_util.find_next_time_expression_time(
                localized_now, matching_seconds, matching_minutes, matching_hours
            )

        # Make sure rolling back the clock doesn't prevent the timer from
        # triggering.
        last_now = None

        @ha.callback
        def pattern_time_change_listener(ev) -> None:
            """Listen for matching time_changed events."""
            nonlocal next_time, last_now

            now = ev.data[ATTR_NOW]

            if last_now is None or now < last_now:
                # Time rolled back or next time not yet calculated
                calculate_next(now)

            last_now = now

            if next_time <= now:
                hass.async_run_hass_job(
                    job, event.dt_util.as_local(now) if local else now
                )
                calculate_next(now + datetime.timedelta(seconds=1))

        # We can't use async_track_point_in_utc_time here because it would
        # break in the case that the system time abruptly jumps backwards.
        # Our custom last_now logic takes care of resolving that scenario.
        return hass.bus.async_listen(EVENT_TIME_CHANGED, pattern_time_change_listener)
def test_async_add_hass_job_schedule_callback():
    """Test that we schedule coroutines and add jobs to the job pool."""
    hass = MagicMock()
    job = MagicMock()

    ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(ha.callback(job)))
    assert len(hass.loop.call_soon.mock_calls) == 1
    assert len(hass.loop.create_task.mock_calls) == 0
    assert len(hass.add_job.mock_calls) == 0
def test_async_add_hass_job_schedule_partial_callback():
    """Test that we schedule partial coros and add jobs to the job pool."""
    hass = MagicMock()
    job = MagicMock()
    partial = functools.partial(ha.callback(job))

    ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(partial))
    assert len(hass.loop.call_soon.mock_calls) == 1
    assert len(hass.loop.create_task.mock_calls) == 0
    assert len(hass.add_job.mock_calls) == 0
def test_async_add_hass_job_schedule_coroutinefunction(loop):
    """Test that we schedule coroutines and add jobs to the job pool."""
    hass = MagicMock(loop=MagicMock(wraps=loop))

    async def job():
        pass

    ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(job))
    assert len(hass.loop.call_soon.mock_calls) == 0
    assert len(hass.loop.create_task.mock_calls) == 1
    assert len(hass.add_job.mock_calls) == 0
def test_async_run_hass_job_delegates_non_async():
    """Test that the callback annotation is respected."""
    hass = MagicMock()
    calls = []

    def job():
        calls.append(1)

    ha.HomeAssistant.async_run_hass_job(hass, ha.HassJob(job))
    assert len(calls) == 0
    assert len(hass.async_add_hass_job.mock_calls) == 1
def test_async_add_job_add_hass_threaded_job_to_pool():
    """Test that we schedule coroutines and add jobs to the job pool."""
    hass = MagicMock()

    def job():
        pass

    ha.HomeAssistant.async_add_hass_job(hass, ha.HassJob(job))
    assert len(hass.loop.call_soon.mock_calls) == 0
    assert len(hass.loop.create_task.mock_calls) == 0
    assert len(hass.loop.run_in_executor.mock_calls) == 1
async def test_hassjob_forbid_coroutine():
    """Test hassjob forbids coroutines."""
    async def bla():
        pass

    coro = bla()

    with pytest.raises(ValueError):
        ha.HassJob(coro)

    # To avoid warning about unawaited coro
    await coro
def async_listen(
    hass: core.HomeAssistant,
    service: str,
    callback: CALLBACK_TYPE,
) -> None:
    """Set up listener for discovery of specific service.

    Service can be a string or a list/tuple.
    """
    job = core.HassJob(callback)

    async def discovery_event_listener(discovered: DiscoveryDict) -> None:
        """Listen for discovery events."""
        task = hass.async_run_hass_job(job, discovered["service"],
                                       discovered["discovered"])
        if task:
            await task

    async_dispatcher_connect(hass, SIGNAL_PLATFORM_DISCOVERED.format(service),
                             discovery_event_listener)