def _get_location_from_entity(self, entity_id):
        """Get the location from the entity_id."""
        state = self.hass.states.get(entity_id)

        if state is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has location attributes (zone)
        if location.has_location(state):
            return _get_location_from_attributes(state)

        # Check if device is in a zone (device_tracker)
        zone_state = self.hass.states.get('zone.{}'.format(state.state))
        if location.has_location(zone_state):
            _LOGGER.debug(
                "%s is in %s, getting zone location",
                entity_id, zone_state.entity_id
            )
            return _get_location_from_attributes(zone_state)

        # If zone was not found in state then use the state as the location
        if entity_id.startswith('sensor.'):
            return state.state

        # When everything fails just return nothing
        return None
Esempio n. 2
0
    def zone_automation_listener(entity, from_s, to_s):
        """Listen for state changes and calls action."""
        if from_s and not location.has_location(from_s) or \
           not location.has_location(to_s):
            return

        zone_state = hass.states.get(zone_entity_id)
        if from_s:
            from_match = condition.zone(hass, zone_state, from_s)
        else:
            from_match = False
        to_match = condition.zone(hass, zone_state, to_s)

        # pylint: disable=too-many-boolean-expressions
        if event == EVENT_ENTER and not from_match and to_match or \
           event == EVENT_LEAVE and from_match and not to_match:
            hass.async_add_job(action, {
                'trigger': {
                    'platform': 'zone',
                    'entity_id': entity,
                    'from_state': from_s,
                    'to_state': to_s,
                    'zone': zone_state,
                    'event': event,
                },
            })
Esempio n. 3
0
    def _get_location_from_entity(self, entity_id):
        """Get the location from the entity_id."""
        state = self.hass.states.get(entity_id)

        if state is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has location attributes.
        if location.has_location(state):
            _LOGGER.debug("Getting %s location", entity_id)
            return _get_location_from_attributes(state)

        # Check if device is inside a zone.
        zone_state = self.hass.states.get(f"zone.{state.state}")
        if location.has_location(zone_state):
            _LOGGER.debug("%s is in %s, getting zone location", entity_id,
                          zone_state.entity_id)
            return _get_location_from_attributes(zone_state)

        # If zone was not found in state then use the state as the location.
        if entity_id.startswith("sensor."):
            return state.state

        # When everything fails just return nothing.
        return None
Esempio n. 4
0
    def zone_automation_listener(entity, from_s, to_s):
        """Listen for state changes and calls action."""
        if from_s and not location.has_location(from_s) or not location.has_location(to_s):
            return

        zone_state = hass.states.get(zone_entity_id)
        if from_s:
            from_match = condition.zone(hass, zone_state, from_s)
        else:
            from_match = False
        to_match = condition.zone(hass, zone_state, to_s)

        # pylint: disable=too-many-boolean-expressions
        if event == EVENT_ENTER and not from_match and to_match or event == EVENT_LEAVE and from_match and not to_match:
            action(
                {
                    "trigger": {
                        "platform": "zone",
                        "entity_id": entity,
                        "from_state": from_s,
                        "to_state": to_s,
                        "zone": zone_state,
                        "event": event,
                    }
                }
            )
Esempio n. 5
0
    def _get_location_from_entity(self, entity_id):
        """Get the location from the entity_id."""
        state = self.hass.states.get(entity_id)

        if state is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has location attributes (zone)
        if location.has_location(state):
            return _get_location_from_attributes(state)

        # Check if device is in a zone (device_tracker)
        zone_state = self.hass.states.get('zone.{}'.format(state.state))
        if location.has_location(zone_state):
            _LOGGER.debug("%s is in %s, getting zone location", entity_id,
                          zone_state.entity_id)
            return _get_location_from_attributes(zone_state)

        # If zone was not found in state then use the state as the location
        if entity_id.startswith('sensor.'):
            return state.state

        # When everything fails just return nothing
        return None
    def _get_location_from_entity(self, entity_id):
        """Get the location from the entity state or attributes."""
        entity = self._hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            self.valid_api_connection = False
            return None

        # Check if the entity has location attributes
        if location.has_location(entity):
            return self._get_location_from_attributes(entity)

        # Check if device is in a zone
        zone_entity = self._hass.states.get("zone.%s" % entity.state)
        if location.has_location(zone_entity):
            _LOGGER.debug(
                "%s is in %s, getting zone location",
                entity_id, zone_entity.entity_id
            )
            return self._get_location_from_attributes(zone_entity)

        # If zone was not found in state then use the state as the location
        if entity_id.startswith("sensor."):
            return entity.state

        # When everything fails just return nothing
        return None
Esempio n. 7
0
    def zone_automation_listener(zone_event):
        """Listen for state changes and calls action."""
        entity = zone_event.data.get("entity_id")
        from_s = zone_event.data.get("old_state")
        to_s = zone_event.data.get("new_state")

        if (from_s and not location.has_location(from_s)
                or not location.has_location(to_s)):
            return

        zone_state = hass.states.get(zone_entity_id)
        from_match = condition.zone(hass, zone_state,
                                    from_s) if from_s else False
        to_match = condition.zone(hass, zone_state, to_s) if to_s else False

        if (event == EVENT_ENTER and not from_match and to_match
                or event == EVENT_LEAVE and from_match and not to_match):
            description = f"{entity} {_EVENT_DESCRIPTION[event]} {zone_state.attributes[ATTR_FRIENDLY_NAME]}"
            hass.async_run_hass_job(
                job,
                {
                    "trigger": {
                        **trigger_data,
                        "platform": platform_type,
                        "entity_id": entity,
                        "from_state": from_s,
                        "to_state": to_s,
                        "zone": zone_state,
                        "event": event,
                        "description": description,
                    }
                },
                to_s.context,
            )
Esempio n. 8
0
def get_location_from_entity(hass, logger, entity_id):
    """Get the location from the entity state or attributes."""
    entity = hass.states.get(entity_id)

    if entity is None:
        logger.error("Unable to find entity %s", entity_id)
        return None

    # Check if the entity has location attributes
    if location.has_location(entity):
        return get_location_from_attributes(entity)

    # Check if device is in a zone
    zone_entity = hass.states.get(f"zone.{entity.state}")
    if location.has_location(zone_entity):
        logger.debug("%s is in %s, getting zone location", entity_id,
                     zone_entity.entity_id)
        return get_location_from_attributes(zone_entity)

    # If zone was not found in state then use the state as the location
    if entity_id.startswith("sensor."):
        return entity.state

    # When everything fails just return nothing
    return None
Esempio n. 9
0
    def zone_automation_listener(entity, from_s, to_s):
        """Listen for state changes and calls action."""
        if (from_s and not location.has_location(from_s)
                or not location.has_location(to_s)):
            return

        zone_state = hass.states.get(zone_entity_id)
        if from_s:
            from_match = condition.zone(hass, zone_state, from_s)
        else:
            from_match = False
        to_match = condition.zone(hass, zone_state, to_s)

        # pylint: disable=too-many-boolean-expressions
        if (event == EVENT_ENTER and not from_match and to_match
                or event == EVENT_LEAVE and from_match and not to_match):
            hass.async_run_job(
                action(
                    {
                        "trigger": {
                            "platform": "zone",
                            "entity_id": entity,
                            "from_state": from_s,
                            "to_state": to_s,
                            "zone": zone_state,
                            "event": event,
                        }
                    },
                    context=to_s.context,
                ))
Esempio n. 10
0
    async def _get_location_from_entity(self, entity_id: str) -> str | None:
        """Get the location from the entity state or attributes."""
        entity = self.hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has location attributes
        if location.has_location(entity):
            return self._get_location_from_attributes(entity)

        # Check if device is in a zone
        zone_entity = self.hass.states.get(f"zone.{entity.state}")
        if location.has_location(zone_entity):
            _LOGGER.debug("%s is in %s, getting zone location", entity_id,
                          zone_entity.entity_id)
            return self._get_location_from_attributes(zone_entity)

        # Check if state is valid coordinate set
        if self._entity_state_is_valid_coordinate_set(entity.state):
            return entity.state

        _LOGGER.error(
            "The state of %s is not a valid set of coordinates: %s",
            entity_id,
            entity.state,
        )
        return None
Esempio n. 11
0
    def zone_automation_listener(entity, from_s, to_s):
        """Listen for state changes and calls action."""
        if from_s and not location.has_location(from_s) or \
           not location.has_location(to_s):
            return

        zone_state = hass.states.get(zone_entity_id)
        if from_s:
            from_match = condition.zone(hass, zone_state, from_s)
        else:
            from_match = False
        to_match = condition.zone(hass, zone_state, to_s)

        # pylint: disable=too-many-boolean-expressions
        if event == EVENT_ENTER and not from_match and to_match or \
           event == EVENT_LEAVE and from_match and not to_match:
            hass.async_run_job(
                action, {
                    'trigger': {
                        'platform': 'zone',
                        'entity_id': entity,
                        'from_state': from_s,
                        'to_state': to_s,
                        'zone': zone_state,
                        'event': event,
                    },
                })
Esempio n. 12
0
    def _get_location_from_entity(self, entity_id):
        """Get the location from the entity state or attributes."""
        entity = self._hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            self.valid_api_connection = False
            return None

        # Check if the entity has location attributes
        if location.has_location(entity):
            return self._get_location_from_attributes(entity)

        # Check if device is in a zone
        zone_entity = self._hass.states.get("zone.%s" % entity.state)
        if location.has_location(zone_entity):
            _LOGGER.debug("%s is in %s, getting zone location", entity_id,
                          zone_entity.entity_id)
            return self._get_location_from_attributes(zone_entity)

        # If zone was not found in state then use the state as the location
        if entity_id.startswith("sensor."):
            return entity.state

        # When everything fails just return nothing
        return None
Esempio n. 13
0
 def test_has_location_with_states_with_invalid_locations(self):
     """Set up the tests."""
     state = State('hello.world', 'invalid', {
         ATTR_LATITUDE: 'no number',
         ATTR_LONGITUDE: 123.12
     })
     assert not location.has_location(state)
Esempio n. 14
0
def test_has_location_with_states_with_valid_location():
    """Set up the tests."""
    state = State("hello.world", "invalid", {
        ATTR_LATITUDE: 123.12,
        ATTR_LONGITUDE: 123.12
    })
    assert location.has_location(state)
Esempio n. 15
0
    def closest(self, *args):
        """Find closest entity.

        Closest to home:
          closest(states)
          closest(states.device_tracker)
          closest('group.children')
          closest(states.group.children)

        Closest to a point:
          closest(23.456, 23.456, 'group.children')
          closest('zone.school', 'group.children')
          closest(states.zone.school, 'group.children')
        """
        if len(args) == 1:
            latitude = self._hass.config.latitude
            longitude = self._hass.config.longitude
            entities = args[0]

        elif len(args) == 2:
            point_state = self._resolve_state(args[0])

            if point_state is None:
                _LOGGER.warning('Closest:Unable to find state %s', args[0])
                return None
            elif not loc_helper.has_location(point_state):
                _LOGGER.warning(
                    'Closest:State does not contain valid location: %s',
                    point_state)
                return None

            latitude = point_state.attributes.get(ATTR_LATITUDE)
            longitude = point_state.attributes.get(ATTR_LONGITUDE)

            entities = args[1]

        else:
            latitude = convert(args[0], float)
            longitude = convert(args[1], float)

            if latitude is None or longitude is None:
                _LOGGER.warning(
                    'Closest:Received invalid coordinates: %s, %s',
                    args[0], args[1])
                return None

            entities = args[2]

        if isinstance(entities, (AllStates, DomainStates)):
            states = list(entities)
        else:
            if isinstance(entities, State):
                gr_entity_id = entities.entity_id
            else:
                gr_entity_id = str(entities)

            states = [self._hass.states.get(entity_id) for entity_id
                      in group.expand_entity_ids(self._hass, [gr_entity_id])]

        return loc_helper.closest(latitude, longitude, states)
Esempio n. 16
0
def test_has_location_with_states_with_invalid_locations():
    """Set up the tests."""
    state = State('hello.world', 'invalid', {
        ATTR_LATITUDE: 'no number',
        ATTR_LONGITUDE: 123.12
    })
    assert not location.has_location(state)
Esempio n. 17
0
 def test_has_location_with_states_with_valid_location(self):
     """Set up the tests."""
     state = State('hello.world', 'invalid', {
         ATTR_LATITUDE: 123.12,
         ATTR_LONGITUDE: 123.12
     })
     self.assertTrue(location.has_location(state))
Esempio n. 18
0
def closest(hass, *args):
    """Find closest entity.

    Closest to home:
        closest(states)
        closest(states.device_tracker)
        closest('group.children')
        closest(states.group.children)

    Closest to a point:
        closest(23.456, 23.456, 'group.children')
        closest('zone.school', 'group.children')
        closest(states.zone.school, 'group.children')

    As a filter:
        states | closest
        states.device_tracker | closest
        ['group.children', states.device_tracker] | closest
        'group.children' | closest(23.456, 23.456)
        states.device_tracker | closest('zone.school')
        'group.children' | closest(states.zone.school)

    """
    if len(args) == 1:
        latitude = hass.config.latitude
        longitude = hass.config.longitude
        entities = args[0]

    elif len(args) == 2:
        point_state = _resolve_state(hass, args[0])

        if point_state is None:
            _LOGGER.warning("Closest:Unable to find state %s", args[0])
            return None
        if not loc_helper.has_location(point_state):
            _LOGGER.warning(
                "Closest:State does not contain valid location: %s",
                point_state)
            return None

        latitude = point_state.attributes.get(ATTR_LATITUDE)
        longitude = point_state.attributes.get(ATTR_LONGITUDE)

        entities = args[1]

    else:
        latitude = convert(args[0], float)
        longitude = convert(args[1], float)

        if latitude is None or longitude is None:
            _LOGGER.warning("Closest:Received invalid coordinates: %s, %s",
                            args[0], args[1])
            return None

        entities = args[2]

    states = expand(hass, entities)

    # state will already be wrapped here
    return loc_helper.closest(latitude, longitude, states)
Esempio n. 19
0
 def test_has_location_with_states_with_valid_location(self):
     """Setup the tests."""
     state = State('hello.world', 'invalid', {
         ATTR_LATITUDE: 123.12,
         ATTR_LONGITUDE: 123.12
     })
     self.assertTrue(location.has_location(state))
Esempio n. 20
0
    def zone_automation_listener(zone_event):
        """Listen for state changes and calls action."""
        entity = zone_event.data.get("entity_id")
        from_s = zone_event.data.get("old_state")
        to_s = zone_event.data.get("new_state")

        if (from_s and not location.has_location(from_s)
                or not location.has_location(to_s)):
            return

        if not (zone_state := hass.states.get(zone_entity_id)):
            _LOGGER.warning(
                "Automation '%s' is referencing non-existing zone '%s' in a zone trigger",
                automation_info["name"],
                zone_entity_id,
            )
            return
Esempio n. 21
0
def distance(hass, *args):
    """Calculate distance.

    Will calculate distance from home to a point or between points.
    Points can be passed in using state objects or lat/lng coordinates.
    """
    locations = []

    to_process = list(args)

    while to_process:
        value = to_process.pop(0)
        if isinstance(value, str) and not valid_entity_id(value):
            point_state = None
        else:
            point_state = _resolve_state(hass, value)

        if point_state is None:
            # We expect this and next value to be lat&lng
            if not to_process:
                _LOGGER.warning(
                    "Distance:Expected latitude and longitude, got %s", value
                )
                return None

            value_2 = to_process.pop(0)
            latitude = convert(value, float)
            longitude = convert(value_2, float)

            if latitude is None or longitude is None:
                _LOGGER.warning(
                    "Distance:Unable to process latitude and longitude: %s, %s",
                    value,
                    value_2,
                )
                return None

        else:
            if not loc_helper.has_location(point_state):
                _LOGGER.warning(
                    "distance:State does not contain valid location: %s", point_state
                )
                return None

            latitude = point_state.attributes.get(ATTR_LATITUDE)
            longitude = point_state.attributes.get(ATTR_LONGITUDE)

        locations.append((latitude, longitude))

    if len(locations) == 1:
        return hass.config.distance(*locations[0])

    return hass.config.units.length(
        loc_util.distance(*locations[0] + locations[1]), LENGTH_METERS
    )
Esempio n. 22
0
    def distance(self, *args):
        """Calculate distance.

        Will calculate distance from home to a point or between points.
        Points can be passed in using state objects or lat/lng coordinates.
        """
        locations = []

        to_process = list(args)

        while to_process:
            value = to_process.pop(0)
            point_state = self._resolve_state(value)

            if point_state is None:
                # We expect this and next value to be lat&lng
                if not to_process:
                    _LOGGER.warning(
                        "Distance:Expected latitude and longitude, got %s",
                        value)
                    return None

                value_2 = to_process.pop(0)
                latitude = convert(value, float)
                longitude = convert(value_2, float)

                if latitude is None or longitude is None:
                    _LOGGER.warning("Distance:Unable to process latitude and "
                                    "longitude: %s, %s", value, value_2)
                    return None

            else:
                if not loc_helper.has_location(point_state):
                    _LOGGER.warning(
                        "distance:State does not contain valid location: %s",
                        point_state)
                    return None

                latitude = point_state.attributes.get(ATTR_LATITUDE)
                longitude = point_state.attributes.get(ATTR_LONGITUDE)

                if latitude is None or longitude is None:
                    _LOGGER.warning(
                        "Distance:State does not contains a location: %s",
                        value)
                    return None

            locations.append((latitude, longitude))

        if len(locations) == 1:
            return self._hass.config.distance(*locations[0])

        return self._hass.config.units.length(
            loc_util.distance(*locations[0] + locations[1]), 'm')
Esempio n. 23
0
    def zone_automation_listener(zone_event):
        """Listen for state changes and calls action."""
        entity = zone_event.data.get("entity_id")
        from_s = zone_event.data.get("old_state")
        to_s = zone_event.data.get("new_state")

        if (
            from_s
            and not location.has_location(from_s)
            or not location.has_location(to_s)
        ):
            return

        zone_state = hass.states.get(zone_entity_id)
        from_match = condition.zone(hass, zone_state, from_s) if from_s else False
        to_match = condition.zone(hass, zone_state, to_s)

        if (
            event == EVENT_ENTER
            and not from_match
            and to_match
            or event == EVENT_LEAVE
            and from_match
            and not to_match
        ):
            hass.async_run_job(
                action(
                    {
                        "trigger": {
                            "platform": "zone",
                            "entity_id": entity,
                            "from_state": from_s,
                            "to_state": to_s,
                            "zone": zone_state,
                            "event": event,
                        }
                    },
                    context=to_s.context,
                )
            )
Esempio n. 24
0
    async def _get_location_from_entity(self, entity_id: str) -> Optional[str]:
        """Get the location from the entity state or attributes."""
        entity = self._hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has location attributes
        if location.has_location(entity):
            return self._get_location_from_attributes(entity)

        # Check if device is in a zone
        zone_entity = self._hass.states.get("zone.{}".format(entity.state))
        if location.has_location(zone_entity):
            _LOGGER.debug("%s is in %s, getting zone location", entity_id,
                          zone_entity.entity_id)
            return self._get_location_from_attributes(zone_entity)

        # If zone was not found in state then use the state as the location
        if entity_id.startswith("sensor."):
            return entity.state
Esempio n. 25
0
    async def _get_location_from_entity(self, entity_id: str) -> Optional[str]:
        """Get the location from the entity state or attributes."""
        entity = self.hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has location attributes
        if location.has_location(entity):
            return self._get_location_from_attributes(entity)

        # Check if device is in a zone
        zone_entity = self.hass.states.get("zone.{}".format(entity.state))
        if location.has_location(zone_entity):
            _LOGGER.debug(
                "%s is in %s, getting zone location", entity_id, zone_entity.entity_id
            )
            return self._get_location_from_attributes(zone_entity)

        # Resolve nested entity
        if entity.state is not entity_id:
            _LOGGER.debug("Getting nested entity for state: %s", entity.state)
            nested_entity = self.hass.states.get(entity.state)
            if nested_entity is not None:
                _LOGGER.debug("Resolving nested entity_id: %s", entity.state)
                return await self._get_location_from_entity(entity.state)

        # Check if state is valid coordinate set
        pattern = r"-?\d{1,2}\.\d+,-?\d{1,3}\.\d+"
        if re.fullmatch(pattern, entity.state):
            return entity.state

        _LOGGER.error(
            "The state of %s is not a valid set of coordinates: %s",
            entity_id,
            entity.state,
        )
        return None
Esempio n. 26
0
    def _get_location_from_entity(self):
        """Get the origin from the entity state or attributes."""
        entity = self._hass.states.get(self._origin_entity_id)

        if entity is None:
            _LOGGER.error("Unable to find entity %s", self._origin_entity_id)
            return None

        # Check if the entity has origin attributes
        if location.has_location(entity):
            return "%s,%s" % (entity.attributes.get(ATTR_LATITUDE), entity.attributes.get(ATTR_LONGITUDE))

        # When everything fails just return nothing
        return None
    def _get_location_from_entity(self, entity_id):
        """Get the origin from the entity state or attributes."""
        entity = self._hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has origin attributes
        if location.has_location(entity):
            return self._get_location_from_attributes(entity)

        # When everything fails just return nothing
        return None
Esempio n. 28
0
    def _get_location_from_entity(self, entity_id):
        """Get the origin from the entity state or attributes."""
        entity = self._hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has origin attributes
        if location.has_location(entity):
            return self._get_location_from_attributes(entity)

        # When everything fails just return nothing
        return None
Esempio n. 29
0
def resolve_location(hass, logger, loc):
    """Resolve a location."""
    if re.fullmatch(ENTITY_ID_PATTERN, loc):
        return get_location_from_entity(hass, logger, loc)

    return resolve_zone(hass, loc)


def get_location_from_entity(hass, logger, entity_id):
    """Get the location from the entity_id."""
    if (state := hass.states.get(entity_id)) is None:
        logger.error("Unable to find entity %s", entity_id)
        return None

    # Check if the entity has location attributes.
    if location.has_location(state):
        logger.debug("Getting %s location", entity_id)
        return _get_location_from_attributes(state)

    # Check if device is inside a zone.
    zone_state = hass.states.get(f"zone.{state.state}")
    if location.has_location(zone_state):
        logger.debug("%s is in %s, getting zone location", entity_id,
                     zone_state.entity_id)
        return _get_location_from_attributes(zone_state)

    # If zone was not found in state then use the state as the location.
    if entity_id.startswith("sensor."):
        return state.state

    # When everything fails just return nothing.
 def test_has_location_with_states_with_invalid_locations(self):
     state = State('hello.world', 'invalid', {
         ATTR_LATITUDE: 'no number',
         ATTR_LONGITUDE: 123.12
     })
     self.assertFalse(location.has_location(state))
Esempio n. 31
0
def test_has_location_with_invalid_states():
    """Set up the tests."""
    for state in (None, 1, "hello", object):
        assert not location.has_location(state)
Esempio n. 32
0
    def closest(self, *args):
        """Find closest entity.

        Closest to home:
          closest(states)
          closest(states.device_tracker)
          closest('group.children')
          closest(states.group.children)

        Closest to a point:
          closest(23.456, 23.456, 'group.children')
          closest('zone.school', 'group.children')
          closest(states.zone.school, 'group.children')
        """
        if len(args) == 1:
            latitude = self._hass.config.latitude
            longitude = self._hass.config.longitude
            entities = args[0]

        elif len(args) == 2:
            point_state = self._resolve_state(args[0])

            if point_state is None:
                _LOGGER.warning('Closest:Unable to find state %s', args[0])
                return None
            elif not loc_helper.has_location(point_state):
                _LOGGER.warning(
                    'Closest:State does not contain valid location: %s',
                    point_state)
                return None

            latitude = point_state.attributes.get(ATTR_LATITUDE)
            longitude = point_state.attributes.get(ATTR_LONGITUDE)

            entities = args[1]

        else:
            latitude = convert(args[0], float)
            longitude = convert(args[1], float)

            if latitude is None or longitude is None:
                _LOGGER.warning('Closest:Received invalid coordinates: %s, %s',
                                args[0], args[1])
                return None

            entities = args[2]

        if isinstance(entities, (AllStates, DomainStates)):
            states = list(entities)
        else:
            if isinstance(entities, State):
                gr_entity_id = entities.entity_id
            else:
                gr_entity_id = str(entities)

            group = get_component('group')

            states = [
                self._hass.states.get(entity_id)
                for entity_id in group.expand_entity_ids(
                    self._hass, [gr_entity_id])
            ]

        return loc_helper.closest(latitude, longitude, states)
Esempio n. 33
0
class HERETravelTimeSensor(SensorEntity):
    """Representation of a HERE travel time sensor."""
    def __init__(
        self,
        name: str,
        origin: str,
        destination: str,
        origin_entity_id: str,
        destination_entity_id: str,
        here_data: HERETravelTimeData,
    ) -> None:
        """Initialize the sensor."""
        self._name = name
        self._origin_entity_id = origin_entity_id
        self._destination_entity_id = destination_entity_id
        self._here_data = here_data
        self._unit_of_measurement = TIME_MINUTES
        self._attrs = {
            ATTR_UNIT_SYSTEM: self._here_data.units,
            ATTR_MODE: self._here_data.travel_mode,
            ATTR_TRAFFIC_MODE: self._here_data.traffic_mode,
        }
        if self._origin_entity_id is None:
            self._here_data.origin = origin

        if self._destination_entity_id is None:
            self._here_data.destination = destination

    async def async_added_to_hass(self) -> None:
        """Delay the sensor update to avoid entity not found warnings."""
        @callback
        def delayed_sensor_update(event):
            """Update sensor after Home Assistant started."""
            self.async_schedule_update_ha_state(True)

        self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START,
                                        delayed_sensor_update)

    @property
    def native_value(self) -> str | None:
        """Return the state of the sensor."""
        if self._here_data.traffic_mode and self._here_data.traffic_time is not None:
            return str(round(self._here_data.traffic_time / 60))
        if self._here_data.base_time is not None:
            return str(round(self._here_data.base_time / 60))

        return None

    @property
    def name(self) -> str:
        """Get the name of the sensor."""
        return self._name

    @property
    def extra_state_attributes(
        self, ) -> dict[str, None | float | str | bool] | None:
        """Return the state attributes."""
        if self._here_data.base_time is None:
            return None

        res = self._attrs
        if self._here_data.attribution is not None:
            res[ATTR_ATTRIBUTION] = self._here_data.attribution
        res[ATTR_DURATION] = self._here_data.base_time / 60
        res[ATTR_DISTANCE] = self._here_data.distance
        res[ATTR_ROUTE] = self._here_data.route
        res[ATTR_DURATION_IN_TRAFFIC] = self._here_data.traffic_time / 60
        res[ATTR_ORIGIN] = self._here_data.origin
        res[ATTR_DESTINATION] = self._here_data.destination
        res[ATTR_ORIGIN_NAME] = self._here_data.origin_name
        res[ATTR_DESTINATION_NAME] = self._here_data.destination_name
        return res

    @property
    def native_unit_of_measurement(self) -> str:
        """Return the unit this state is expressed in."""
        return self._unit_of_measurement

    @property
    def icon(self) -> str:
        """Icon to use in the frontend depending on travel_mode."""
        if self._here_data.travel_mode == TRAVEL_MODE_BICYCLE:
            return ICON_BICYCLE
        if self._here_data.travel_mode == TRAVEL_MODE_PEDESTRIAN:
            return ICON_PEDESTRIAN
        if self._here_data.travel_mode in TRAVEL_MODES_PUBLIC:
            return ICON_PUBLIC
        if self._here_data.travel_mode == TRAVEL_MODE_TRUCK:
            return ICON_TRUCK
        return ICON_CAR

    async def async_update(self) -> None:
        """Update Sensor Information."""
        # Convert device_trackers to HERE friendly location
        if self._origin_entity_id is not None:
            self._here_data.origin = await self._get_location_from_entity(
                self._origin_entity_id)

        if self._destination_entity_id is not None:
            self._here_data.destination = await self._get_location_from_entity(
                self._destination_entity_id)

        await self.hass.async_add_executor_job(self._here_data.update)

    async def _get_location_from_entity(self, entity_id: str) -> str | None:
        """Get the location from the entity state or attributes."""
        if (entity := self.hass.states.get(entity_id)) is None:
            _LOGGER.error("Unable to find entity %s", entity_id)
            return None

        # Check if the entity has location attributes
        if location.has_location(entity):
            return self._get_location_from_attributes(entity)

        # Check if device is in a zone
        zone_entity = self.hass.states.get(f"zone.{entity.state}")
        if location.has_location(zone_entity):
            _LOGGER.debug("%s is in %s, getting zone location", entity_id,
                          zone_entity.entity_id)
            return self._get_location_from_attributes(zone_entity)

        # Check if state is valid coordinate set
        if self._entity_state_is_valid_coordinate_set(entity.state):
            return entity.state

        _LOGGER.error(
            "The state of %s is not a valid set of coordinates: %s",
            entity_id,
            entity.state,
        )
        return None
Esempio n. 34
0
 def test_has_location_with_states_with_invalid_locations(self):
     state = State('hello.world', 'invalid', {
         ATTR_LATITUDE: 'no number',
         ATTR_LONGITUDE: 123.12
     })
     self.assertFalse(location.has_location(state))
Esempio n. 35
0
 def test_has_location_with_invalid_states(self):
     """Setup the tests."""
     for state in (None, 1, "hello", object):
         self.assertFalse(location.has_location(state))
Esempio n. 36
0
def resolve_location(hass, logger, loc):
    """Resolve a location."""
    if loc.split(".", 1)[0] in TRACKABLE_DOMAINS:
        return get_location_from_entity(hass, logger, loc)

    return resolve_zone(hass, loc)


def get_location_from_entity(hass, logger, entity_id):
    """Get the location from the entity state or attributes."""
    if (entity := hass.states.get(entity_id)) is None:
        logger.error("Unable to find entity %s", entity_id)
        return None

    # Check if the entity has location attributes
    if location.has_location(entity):
        return get_location_from_attributes(entity)

    # Check if device is in a zone
    zone_entity = hass.states.get(f"zone.{entity.state}")
    if location.has_location(zone_entity):
        logger.debug("%s is in %s, getting zone location", entity_id,
                     zone_entity.entity_id)
        return get_location_from_attributes(zone_entity)

    # If zone was not found in state then use the state as the location
    if entity_id.startswith("sensor."):
        return entity.state

    # When everything fails just return nothing
    return None
Esempio n. 37
0
 def test_has_location_with_invalid_states(self):
     """Set up the tests."""
     for state in (None, 1, "hello", object):
         self.assertFalse(location.has_location(state))