示例#1
0
    def async_track_point_in_utc_time(opp, 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 OppJob so we can avoid
        # having to figure out how to call the action every time its called.
        job = action if isinstance(action, ha.OppJob) else ha.OppJob(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()

            opp.async_run_opp_job(job, now)

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

        return async_unsub
示例#2
0
def async_listen_platform(
    opp: core.OpenPeerPower,
    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.OppJob(callback)

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

        if not platform:
            return

        task = opp.async_run_opp_job(job, platform,
                                     discovered.get("discovered"))
        if task:
            await task

    async_dispatcher_connect(opp, SIGNAL_PLATFORM_DISCOVERED.format(service),
                             discovery_platform_listener)
示例#3
0
    def async_track_utc_time_change(
        opp, action, hour=None, minute=None, second=None, local=False
    ):
        """Add a listener that will fire if time matches a pattern."""

        job = ha.OppJob(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."""
                opp.async_run_opp_job(job, ev.data[ATTR_NOW])

            return opp.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:
                opp.async_run_opp_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 opp.bus.async_listen(EVENT_TIME_CHANGED, pattern_time_change_listener)
示例#4
0
def test_async_add_opp_job_schedule_callback():
    """Test that we schedule coroutines and add jobs to the job pool."""
    opp = MagicMock()
    job = MagicMock()

    ha.OpenPeerPower.async_add_opp_job(opp, ha.OppJob(ha.callback(job)))
    assert len(opp.loop.call_soon.mock_calls) == 1
    assert len(opp.loop.create_task.mock_calls) == 0
    assert len(opp.add_job.mock_calls) == 0
示例#5
0
def test_async_add_opp_job_schedule_partial_callback():
    """Test that we schedule partial coros and add jobs to the job pool."""
    opp = MagicMock()
    job = MagicMock()
    partial = functools.partial(ha.callback(job))

    ha.OpenPeerPower.async_add_opp_job(opp, ha.OppJob(partial))
    assert len(opp.loop.call_soon.mock_calls) == 1
    assert len(opp.loop.create_task.mock_calls) == 0
    assert len(opp.add_job.mock_calls) == 0
示例#6
0
def test_async_add_opp_job_schedule_coroutinefunction(loop):
    """Test that we schedule coroutines and add jobs to the job pool."""
    opp = MagicMock(loop=MagicMock(wraps=loop))

    async def job():
        pass

    ha.OpenPeerPower.async_add_opp_job(opp, ha.OppJob(job))
    assert len(opp.loop.call_soon.mock_calls) == 0
    assert len(opp.loop.create_task.mock_calls) == 1
    assert len(opp.add_job.mock_calls) == 0
示例#7
0
def test_async_run_opp_job_delegates_non_async():
    """Test that the callback annotation is respected."""
    opp = MagicMock()
    calls = []

    def job():
        calls.append(1)

    ha.OpenPeerPower.async_run_opp_job(opp, ha.OppJob(job))
    assert len(calls) == 0
    assert len(opp.async_add_opp_job.mock_calls) == 1
示例#8
0
def test_async_add_job_add_opp_threaded_job_to_pool():
    """Test that we schedule coroutines and add jobs to the job pool."""
    opp = MagicMock()

    def job():
        pass

    ha.OpenPeerPower.async_add_opp_job(opp, ha.OppJob(job))
    assert len(opp.loop.call_soon.mock_calls) == 0
    assert len(opp.loop.create_task.mock_calls) == 0
    assert len(opp.loop.run_in_executor.mock_calls) == 1
示例#9
0
async def test_oppjob_forbid_coroutine():
    """Test oppjob forbids coroutines."""
    async def bla():
        pass

    coro = bla()

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

    # To avoid warning about unawaited coro
    await coro
示例#10
0
def async_listen(
    opp: core.OpenPeerPower,
    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.OppJob(callback)

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

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