예제 #1
0
async def test_shutdown_run_callback_threadsafe(opp):
    """Test we can shutdown run_callback_threadsafe."""
    hasync.shutdown_run_callback_threadsafe(opp.loop)
    callback = MagicMock()

    with pytest.raises(RuntimeError):
        hasync.run_callback_threadsafe(opp.loop, callback)
예제 #2
0
 def _schedule_add_entities(self, new_entities, update_before_add=False):
     """Schedule adding entities for a single platform, synchronously."""
     run_callback_threadsafe(
         self.opp.loop,
         self._async_schedule_add_entities,
         list(new_entities),
         update_before_add,
     ).result()
예제 #3
0
def listen(opp: core.OpenPeerPower, service: Union[str, Collection[str]],
           callback: Callable) -> None:
    """Set up listener for discovery of specific service.

    Service can be a string or a list/tuple.
    """
    run_callback_threadsafe(opp.loop, async_listen, opp, service,
                            callback).result()
예제 #4
0
    def test_platform(self, mock_setup_component):
        """Test discover platform method."""
        calls = []

        @callback
        def platform_callback(platform, info):
            """Platform callback method."""
            calls.append((platform, info))

        run_callback_threadsafe(
            self.opp.loop,
            discovery.async_listen_platform,
            self.opp,
            "test_component",
            platform_callback,
        ).result()

        discovery.load_platform(
            self.opp,
            "test_component",
            "test_platform",
            "discovery info",
            {"test_component": {}},
        )
        self.opp.block_till_done()
        assert mock_setup_component.called
        assert mock_setup_component.call_args[0] == (
            self.opp,
            "test_component",
            {
                "test_component": {}
            },
        )
        self.opp.block_till_done()

        discovery.load_platform(
            self.opp,
            "test_component_2",
            "test_platform",
            "discovery info",
            {"test_component": {}},
        )
        self.opp.block_till_done()

        assert len(calls) == 1
        assert calls[0] == ("test_platform", "discovery info")

        dispatcher_send(
            self.opp,
            discovery.SIGNAL_PLATFORM_DISCOVERED,
            {
                "service":
                discovery.EVENT_LOAD_PLATFORM.format("test_component")
            },
        )
        self.opp.block_till_done()

        assert len(calls) == 1
예제 #5
0
 def _render_template() -> None:
     try:
         _render_with_context(self.template, compiled, **kwargs)
     except TimeoutError:
         pass
     except Exception:  # pylint: disable=broad-except
         self._exc_info = sys.exc_info()
     finally:
         run_callback_threadsafe(self.opp.loop, finish_event.set)
예제 #6
0
 def token_updater(token):
     """Handle from sync context when token is updated."""
     run_callback_threadsafe(
         opp.loop,
         partial(
             opp.config_entries.async_update_entry,
             entry,
             data={
                 **entry.data, "token": token
             },
         ),
     ).result()
예제 #7
0
async def test_callback_is_always_scheduled(opp):
    """Test run_callback_threadsafe always calls call_soon_threadsafe before checking for shutdown."""
    # We have to check the shutdown state AFTER the callback is scheduled otherwise
    # the function could continue on and the caller call `future.result()` after
    # the point in the main thread where callbacks are no longer run.

    callback = MagicMock()
    hasync.shutdown_run_callback_threadsafe(opp.loop)

    with patch.object(
            opp.loop, "call_soon_threadsafe"
    ) as mock_call_soon_threadsafe, pytest.raises(RuntimeError):
        hasync.run_callback_threadsafe(opp.loop, callback)

    mock_call_soon_threadsafe.assert_called_once()
예제 #8
0
    def register(
        self,
        domain: str,
        service: str,
        service_func: Callable,
        schema: Optional[vol.Schema] = None,
    ) -> None:
        """
        Register a service.

        Schema is called to coerce and validate the service data.
        """
        run_callback_threadsafe(
            self._opp.loop, self.async_register, domain, service, service_func, schema
        ).result()
예제 #9
0
    def render(self, variables: TemplateVarsType = None, **kwargs: Any) -> str:
        """Render given template."""
        if variables is not None:
            kwargs.update(variables)

        return run_callback_threadsafe(self.opp.loop, self.async_render,
                                       kwargs).result()
예제 #10
0
def template(
    opp: OpenPeerPower, value_template: Template, variables: TemplateVarsType = None
) -> bool:
    """Test if template condition matches."""
    return run_callback_threadsafe(
        opp.loop, async_template, opp, value_template, variables
    ).result()
예제 #11
0
    def remove(self, entity_id: str) -> bool:
        """Remove the state of an entity.

        Returns boolean to indicate if an entity was removed.
        """
        return run_callback_threadsafe(  # type: ignore
            self._loop, self.async_remove, entity_id
        ).result()
예제 #12
0
def request_config(opp, *args, **kwargs):
    """Create a new request for configuration.

    Will return an ID to be used for sequent calls.
    """
    return run_callback_threadsafe(
        opp.loop, ft.partial(async_request_config, opp, *args,
                             **kwargs)).result()
예제 #13
0
def dispatcher_connect(opp: OpenPeerPower, signal: str,
                       target: Callable[..., None]) -> Callable[[], None]:
    """Connect a callable function to a signal."""
    async_unsub = run_callback_threadsafe(opp.loop, async_dispatcher_connect,
                                          opp, signal, target).result()

    def remove_dispatcher() -> None:
        """Remove signal listener."""
        run_callback_threadsafe(opp.loop, async_unsub).result()

    return remove_dispatcher
예제 #14
0
    def render_with_possible_json_value(self, value, error_value=_SENTINEL):
        """Render template with value exposed.

        If valid JSON will expose value_json too.
        """
        return run_callback_threadsafe(
            self.opp.loop,
            self.async_render_with_possible_json_value,
            value,
            error_value,
        ).result()
예제 #15
0
async def test_run_callback_threadsafe(opp):
    """Test run_callback_threadsafe runs code in the event loop."""
    it_ran = False

    def callback():
        nonlocal it_ran
        it_ran = True

    assert hasync.run_callback_threadsafe(opp.loop, callback)
    assert it_ran is False

    # Verify that async_block_till_done will flush
    # out the callback
    await opp.async_block_till_done()
    assert it_ran is True
예제 #16
0
    def listen(self, event_type: str, listener: Callable) -> CALLBACK_TYPE:
        """Listen for all events or events of a specific type.

        To listen to all events specify the constant ``MATCH_ALL``
        as event_type.
        """
        async_remove_listener = run_callback_threadsafe(
            self._opp.loop, self.async_listen, event_type, listener
        ).result()

        def remove_listener() -> None:
            """Remove the listener."""
            run_callback_threadsafe(self._opp.loop, async_remove_listener).result()

        return remove_listener
예제 #17
0
    def factory(*args: Any, **kwargs: Any) -> CALLBACK_TYPE:
        """Call async event helper safely."""
        opp = args[0]

        if not isinstance(opp, OpenPeerPower):
            raise TypeError("First parameter needs to be a opp instance")

        async_remove = run_callback_threadsafe(
            opp.loop, ft.partial(async_factory, *args, **kwargs)).result()

        def remove() -> None:
            """Threadsafe removal."""
            run_callback_threadsafe(opp.loop, async_remove).result()

        return remove
예제 #18
0
    def set(
        self,
        entity_id: str,
        new_state: str,
        attributes: Optional[Dict] = None,
        force_update: bool = False,
        context: Optional[Context] = None,
    ) -> None:
        """Set the state of an entity, add entity if it does not exist.

        Attributes is an optional dict to specify attributes of this state.

        If you just update the attributes and not the state, last changed will
        not be affected.
        """
        run_callback_threadsafe(
            self._loop,
            self.async_set,
            entity_id,
            new_state,
            attributes,
            force_update,
            context,
        ).result()
예제 #19
0
def numeric_state(
    opp: OpenPeerPower,
    entity: None | str | State,
    below: float | str | None = None,
    above: float | str | None = None,
    value_template: Template | None = None,
    variables: TemplateVarsType = None,
) -> bool:
    """Test a numeric state condition."""
    return run_callback_threadsafe(
        opp.loop,
        async_numeric_state,
        opp,
        entity,
        below,
        above,
        value_template,
        variables,
    ).result()
예제 #20
0
    def render(
        self,
        variables: TemplateVarsType = None,
        parse_result: bool = True,
        limited: bool = False,
        **kwargs: Any,
    ) -> Any:
        """Render given template.

        If limited is True, the template is not allowed to access any function or filter depending on opp or the state machine.
        """
        if self.is_static:
            if not parse_result or self.opp.config.legacy_templates:
                return self.template
            return self._parse_result(self.template)

        return run_callback_threadsafe(
            self.opp.loop,
            partial(self.async_render, variables, parse_result, limited,
                    **kwargs),
        ).result()
예제 #21
0
def generate_entity_id(
    entity_id_format: str,
    name: Optional[str],
    current_ids: Optional[List[str]] = None,
    opp: Optional[OpenPeerPower] = None,
) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    if current_ids is None:
        if opp is None:
            raise ValueError("Missing required parameter currentids or opp")
        return run_callback_threadsafe(
            opp.loop,
            async_generate_entity_id,
            entity_id_format,
            name,
            current_ids,
            opp,
        ).result()

    name = (slugify(name or "") or slugify(DEVICE_DEFAULT_NAME)).lower()

    return ensure_unique_string(entity_id_format.format(name), current_ids)
예제 #22
0
def numeric_state(
    opp: OpenPeerPower,
    entity: Union[None, str, State],
    below: Optional[float] = None,
    above: Optional[float] = None,
    value_template: Optional[Template] = None,
    variables: TemplateVarsType = None,
) -> bool:
    """Test a numeric state condition."""
    return cast(
        bool,
        run_callback_threadsafe(
            opp.loop,
            async_numeric_state,
            opp,
            entity,
            below,
            above,
            value_template,
            variables,
        ).result(),
    )
예제 #23
0
def test_run_callback_threadsafe_from_inside_event_loop(mock_ident, _):
    """Testing calling run_callback_threadsafe from inside an event loop."""
    callback = MagicMock()

    loop = Mock(spec=["call_soon_threadsafe"])

    loop._thread_ident = None
    mock_ident.return_value = 5
    hasync.run_callback_threadsafe(loop, callback)
    assert len(loop.call_soon_threadsafe.mock_calls) == 1

    loop._thread_ident = 5
    mock_ident.return_value = 5
    with pytest.raises(RuntimeError):
        hasync.run_callback_threadsafe(loop, callback)
    assert len(loop.call_soon_threadsafe.mock_calls) == 1

    loop._thread_ident = 1
    mock_ident.return_value = 5
    hasync.run_callback_threadsafe(loop, callback)
    assert len(loop.call_soon_threadsafe.mock_calls) == 2
예제 #24
0
 def remove_dispatcher() -> None:
     """Remove signal listener."""
     run_callback_threadsafe(opp.loop, async_unsub).result()
예제 #25
0
 def remove():
     """Remove listener convert."""
     run_callback_threadsafe(opp.loop, async_remove).result()
예제 #26
0
 def _do_nothing(*_):
     run_callback_threadsafe(opp.loop, finish_event.set)
예제 #27
0
 def remove() -> None:
     """Threadsafe removal."""
     run_callback_threadsafe(opp.loop, async_remove).result()
예제 #28
0
 def process_plates(self, plates, vehicles):
     """Send event with new plates and store data."""
     run_callback_threadsafe(self.opp.loop, self.async_process_plates,
                             plates, vehicles).result()
예제 #29
0
    def _update_member(self, member, dev_id):
        loc = member.get("location")
        try:
            last_seen = _utc_from_ts(loc.get("timestamp"))
        except AttributeError:
            last_seen = None
        prev_seen = self._prev_seen(dev_id, last_seen)

        if not loc:
            err_msg = member["issues"]["title"]
            if err_msg:
                if member["issues"]["dialog"]:
                    err_msg += f": {member['issues']['dialog']}"
            else:
                err_msg = "Location information missing"
            self._err(dev_id, err_msg)
            return

        # Only update when we truly have an update.
        if not last_seen:
            _LOGGER.warning("%s: Ignoring update because timestamp is missing",
                            dev_id)
            return
        if prev_seen and last_seen < prev_seen:
            _LOGGER.warning(
                "%s: Ignoring update because timestamp is older than last timestamp",
                dev_id,
            )
            _LOGGER.debug("%s < %s", last_seen, prev_seen)
            return
        if last_seen == prev_seen:
            return

        lat = loc.get("latitude")
        lon = loc.get("longitude")
        gps_accuracy = loc.get("accuracy")
        try:
            lat = float(lat)
            lon = float(lon)
            # Life360 reports accuracy in feet, but Device Tracker expects
            # gps_accuracy in meters.
            gps_accuracy = round(
                convert(float(gps_accuracy), LENGTH_FEET, LENGTH_METERS))
        except (TypeError, ValueError):
            self._err(dev_id,
                      f"GPS data invalid: {lat}, {lon}, {gps_accuracy}")
            return

        self._ok(dev_id)

        msg = f"Updating {dev_id}"
        if prev_seen:
            msg += f"; Time since last update: {last_seen - prev_seen}"
        _LOGGER.debug(msg)

        if self._max_gps_accuracy is not None and gps_accuracy > self._max_gps_accuracy:
            _LOGGER.warning(
                "%s: Ignoring update because expected GPS "
                "accuracy (%.0f) is not met: %.0f",
                dev_id,
                self._max_gps_accuracy,
                gps_accuracy,
            )
            return

        # Get raw attribute data, converting empty strings to None.
        place = loc.get("name") or None
        address1 = loc.get("address1") or None
        address2 = loc.get("address2") or None
        if address1 and address2:
            address = ", ".join([address1, address2])
        else:
            address = address1 or address2
        raw_speed = loc.get("speed") or None
        driving = _bool_attr_from_int(loc.get("isDriving"))
        moving = _bool_attr_from_int(loc.get("inTransit"))
        try:
            battery = int(float(loc.get("battery")))
        except (TypeError, ValueError):
            battery = None

        # Try to convert raw speed into real speed.
        try:
            speed = float(raw_speed) * SPEED_FACTOR_MPH
            if self._opp.config.units.is_metric:
                speed = convert(speed, LENGTH_MILES, LENGTH_KILOMETERS)
            speed = max(0, round(speed))
        except (TypeError, ValueError):
            speed = STATE_UNKNOWN

        # Make driving attribute True if it isn't and we can derive that it
        # should be True from other data.
        if (driving in (STATE_UNKNOWN, False)
                and self._driving_speed is not None
                and speed != STATE_UNKNOWN):
            driving = speed >= self._driving_speed

        attrs = {
            ATTR_ADDRESS: address,
            ATTR_AT_LOC_SINCE: _dt_attr_from_ts(loc.get("since")),
            ATTR_BATTERY_CHARGING: _bool_attr_from_int(loc.get("charge")),
            ATTR_DRIVING: driving,
            ATTR_LAST_SEEN: last_seen,
            ATTR_MOVING: moving,
            ATTR_PLACE: place,
            ATTR_RAW_SPEED: raw_speed,
            ATTR_SPEED: speed,
            ATTR_WIFI_ON: _bool_attr_from_int(loc.get("wifiState")),
        }

        # If user wants driving or moving to be shown as state, and current
        # location is not in a OPP zone, then set location name accordingly.
        loc_name = None
        active_zone = run_callback_threadsafe(self._opp.loop,
                                              async_active_zone, self._opp,
                                              lat, lon, gps_accuracy).result()
        if not active_zone:
            if SHOW_DRIVING in self._show_as_state and driving is True:
                loc_name = SHOW_DRIVING
            elif SHOW_MOVING in self._show_as_state and moving is True:
                loc_name = SHOW_MOVING

        self._see(
            dev_id=dev_id,
            location_name=loc_name,
            gps=(lat, lon),
            gps_accuracy=gps_accuracy,
            battery=battery,
            attributes=attrs,
            picture=member.get("avatar"),
        )
예제 #30
0
 def threadsafe(*args, **kwargs):
     """Call func threadsafe."""
     opp = args[0]
     return run_callback_threadsafe(opp.loop,
                                    ft.partial(func, *args,
                                               **kwargs)).result()