def _prev_seen(self, dev_id, last_seen):
        prev_seen, reported = self._dev_data.get(dev_id, (None, False))

        if self._max_update_wait:
            now = dt_util.utcnow()
            most_recent_update = last_seen or prev_seen or self._started
            overdue = now - most_recent_update > self._max_update_wait
            if overdue and not reported and now - self._started > EVENT_DELAY:
                self._hass.bus.fire(
                    EVENT_UPDATE_OVERDUE,
                    {ATTR_ENTITY_ID: DT_ENTITY_ID_FORMAT.format(dev_id)},
                )
                reported = True
            elif not overdue and reported:
                self._hass.bus.fire(
                    EVENT_UPDATE_RESTORED,
                    {
                        ATTR_ENTITY_ID: DT_ENTITY_ID_FORMAT.format(dev_id),
                        ATTR_WAIT: str(last_seen - (prev_seen or self._started)).split(
                            "."
                        )[0],
                    },
                )
                reported = False

        self._dev_data[dev_id] = last_seen or prev_seen, reported

        return prev_seen
async def test_matching_source_type(hass, mock_device_tracker_conf):
    """Test setting source type."""
    dev_id = "paulus"
    entity_id = ENTITY_ID_FORMAT.format(dev_id)
    topic = "/location/paulus"
    source_type = SOURCE_TYPE_BLUETOOTH
    location = "work"

    hass.config.components = set(["mqtt", "zone"])
    assert await async_setup_component(
        hass,
        device_tracker.DOMAIN,
        {
            device_tracker.DOMAIN: {
                CONF_PLATFORM: "mqtt",
                "devices": {
                    dev_id: topic
                },
                "source_type": source_type,
            }
        },
    )

    async_fire_mqtt_message(hass, topic, location)
    await hass.async_block_till_done()
    assert hass.states.get(
        entity_id).attributes["source_type"] == SOURCE_TYPE_BLUETOOTH
async def test_multi_level_wildcard_topic_not_matching(
        hass, mock_device_tracker_conf):
    """Test not matching multi level wildcard topic."""
    dev_id = "paulus"
    entity_id = ENTITY_ID_FORMAT.format(dev_id)
    subscription = "/location/#"
    topic = "/somewhere/room/paulus"
    location = "work"

    hass.config.components = set(["mqtt", "zone"])
    assert await async_setup_component(
        hass,
        device_tracker.DOMAIN,
        {
            device_tracker.DOMAIN: {
                CONF_PLATFORM: "mqtt",
                "devices": {
                    dev_id: subscription
                },
            }
        },
    )
    async_fire_mqtt_message(hass, topic, location)
    await hass.async_block_till_done()
    assert hass.states.get(entity_id) is None
async def test_not_matching_custom_payload_for_home_and_not_home(
        hass, mock_device_tracker_conf):
    """Test not matching payload does not set state to home or not_home."""
    dev_id = "paulus"
    entity_id = ENTITY_ID_FORMAT.format(dev_id)
    topic = "/location/paulus"
    payload_home = "present"
    payload_not_home = "not present"
    payload_not_matching = "test"

    hass.config.components = set(["mqtt", "zone"])
    assert await async_setup_component(
        hass,
        device_tracker.DOMAIN,
        {
            device_tracker.DOMAIN: {
                CONF_PLATFORM: "mqtt",
                "devices": {
                    dev_id: topic
                },
                "payload_home": payload_home,
                "payload_not_home": payload_not_home,
            }
        },
    )
    async_fire_mqtt_message(hass, topic, payload_not_matching)
    await hass.async_block_till_done()
    assert hass.states.get(entity_id).state != STATE_HOME
    assert hass.states.get(entity_id).state != STATE_NOT_HOME
    def __init__(self, hass, config, see):
        self._hass = hass
        self._see = see
        entities = config[CONF_ENTITY_ID]
        self._entities = {}
        for entity_id in entities:
            self._entities[entity_id] = {
                WARNED: False,
                SEEN: None,
                SOURCE_TYPE: None,
                DATA: None}
        self._dev_id = config[CONF_NAME]
        self._entity_id = ENTITY_ID_FORMAT.format(self._dev_id)
        self._time_as = config[CONF_TIME_AS]
        if self._time_as in [TZ_DEVICE_UTC, TZ_DEVICE_LOCAL]:
            from timezonefinderL import TimezoneFinder
            self._tf = TimezoneFinder()
        self._req_movement = config[CONF_REQ_MOVEMENT]
        self._lock = threading.Lock()
        self._prev_seen = None
        self._init_complete = False

        self._remove = track_state_change(
            hass, entities, self._update_info)

        for entity_id in entities:
            self._update_info(entity_id, None, hass.states.get(entity_id))

        def init_complete(event):
            self._init_complete = True

        hass.bus.listen_once(EVENT_HOMEASSISTANT_START, init_complete)
Beispiel #6
0
    def __init__(self, hass, config, see):
        self._hass = hass
        self._see = see
        entities = config[CONF_ENTITY_ID]
        self._entities = {}
        for entity_id in entities:
            self._entities[entity_id] = {
                WARNED: False,
                SOURCE_TYPE: None,
                STATE: None
            }
        self._dev_id = config[CONF_NAME]
        self._entity_id = ENTITY_ID_FORMAT.format(self._dev_id)
        self._time_as = config[CONF_TIME_AS]
        if self._time_as in [TZ_DEVICE_UTC, TZ_DEVICE_LOCAL]:
            from timezonefinderL import TimezoneFinder
            self._tf = TimezoneFinder()
        self._lock = threading.Lock()
        self._prev_seen = None

        self._remove = track_state_change(hass, entities, self._update_info)

        for entity_id in entities:
            self._update_info(entity_id,
                              None,
                              hass.states.get(entity_id),
                              init=True)
Beispiel #7
0
 def __init__(self, tracker_id, get_token_callback, get_tracker_callback,
              tracker):
     """Set up Georide entity."""
     self._tracker_id = tracker_id
     self._get_token_callback = get_token_callback
     self._get_tracker_callback = get_tracker_callback
     self._name = tracker.tracker_name
     self._data = tracker or {}
     self.entity_id = ENTITY_ID_FORMAT.format(tracker_id)
    def update_device(self, devicename):
        """Update the device_tracker entity."""
        from pyicloud.exceptions import PyiCloudNoDevicesException

        # An entity will not be created by see() when track=false in
        # 'known_devices.yaml', but we need to see() it at least once
        entity = self.hass.states.get(ENTITY_ID_FORMAT.format(devicename))
        if entity is None and devicename in self.seen_devices:
            return
        attrs = {}
        kwargs = {}

        if self.api is None:
            return

        try:
            for device in self.api.devices:
                if str(device) != str(self.devices[devicename]):
                    continue

                status = device.status(DEVICESTATUSSET)
                _LOGGER.debug("Device Status is %s", status)
                dev_id = status["name"].replace(" ", "", 99)
                dev_id = slugify(dev_id)
                attrs[ATTR_DEVICESTATUS] = DEVICESTATUSCODES.get(
                    status["deviceStatus"], "error")
                attrs[ATTR_LOWPOWERMODE] = status["lowPowerMode"]
                attrs[ATTR_BATTERYSTATUS] = status["batteryStatus"]
                attrs[ATTR_ACCOUNTNAME] = self.accountname
                status = device.status(DEVICESTATUSSET)
                battery = status.get("batteryLevel", 0) * 100
                location = status["location"]
                if location and location["horizontalAccuracy"]:
                    horizontal_accuracy = int(location["horizontalAccuracy"])
                    if horizontal_accuracy < self._gps_accuracy_threshold:
                        self.determine_interval(
                            devicename,
                            location["latitude"],
                            location["longitude"],
                            battery,
                        )
                        interval = self._intervals.get(devicename, 1)
                        attrs[ATTR_INTERVAL] = interval
                        accuracy = location["horizontalAccuracy"]
                        kwargs["dev_id"] = dev_id
                        kwargs["host_name"] = status["name"]
                        kwargs["gps"] = (location["latitude"],
                                         location["longitude"])
                        kwargs["battery"] = battery
                        kwargs["gps_accuracy"] = accuracy
                        kwargs[ATTR_ATTRIBUTES] = attrs
                        self.see(**kwargs)
                        self.seen_devices[devicename] = True
        except PyiCloudNoDevicesException:
            _LOGGER.error("No iCloud Devices found")
Beispiel #9
0
async def test_lights_turn_on_when_coming_home_after_sun_set(hass, scanner):
    """Test lights turn on when coming home after sun set."""
    test_time = datetime(2017, 4, 5, 3, 2, 3, tzinfo=dt_util.UTC)
    with patch("homeassistant.util.dt.utcnow", return_value=test_time):
        await common_light.async_turn_off(hass)

        assert await async_setup_component(
            hass, device_sun_light_trigger.DOMAIN, {device_sun_light_trigger.DOMAIN: {}}
        )

        hass.states.async_set(DT_ENTITY_ID_FORMAT.format("device_2"), STATE_HOME)

        await hass.async_block_till_done()
    assert light.is_on(hass)
async def test_new_message(hass, mock_device_tracker_conf):
    """Test new message."""
    dev_id = "paulus"
    entity_id = ENTITY_ID_FORMAT.format(dev_id)
    topic = "/location/paulus"
    location = "work"

    hass.config.components = set(["mqtt", "zone"])
    assert await async_setup_component(
        hass,
        device_tracker.DOMAIN,
        {
            device_tracker.DOMAIN: {
                CONF_PLATFORM: "mqtt",
                "devices": {
                    dev_id: topic
                }
            }
        },
    )
    async_fire_mqtt_message(hass, topic, location)
    await hass.async_block_till_done()
    assert hass.states.get(entity_id).state == location
Beispiel #11
0
async def test_lights_turn_on_when_coming_home_after_sun_set_person(
        hass, scanner):
    """Test lights turn on when coming home after sun set."""
    device_1 = DT_ENTITY_ID_FORMAT.format("device_1")
    device_2 = DT_ENTITY_ID_FORMAT.format("device_2")

    test_time = datetime(2017, 4, 5, 3, 2, 3, tzinfo=dt_util.UTC)
    with patch("homeassistant.util.dt.utcnow", return_value=test_time):
        await common_light.async_turn_off(hass)
        hass.states.async_set(device_1, STATE_NOT_HOME)
        hass.states.async_set(device_2, STATE_NOT_HOME)
        await hass.async_block_till_done()

        assert not light.is_on(hass)
        assert hass.states.get(
            device_tracker.ENTITY_ID_ALL_DEVICES).state == "not_home"
        assert hass.states.get(device_1).state == "not_home"
        assert hass.states.get(device_2).state == "not_home"

        assert await async_setup_component(
            hass,
            "person",
            {
                "person": [{
                    "id": "me",
                    "name": "Me",
                    "device_trackers": [device_1]
                }]
            },
        )

        await group.Group.async_create_group(hass, "person_me", ["person.me"])

        assert await async_setup_component(
            hass,
            device_sun_light_trigger.DOMAIN,
            {
                device_sun_light_trigger.DOMAIN: {
                    "device_group": "group.person_me"
                }
            },
        )

        assert not light.is_on(hass)
        assert hass.states.get(device_1).state == "not_home"
        assert hass.states.get(device_2).state == "not_home"
        assert hass.states.get("person.me").state == "not_home"

        # Unrelated device has no impact
        hass.states.async_set(device_2, STATE_HOME)
        await hass.async_block_till_done()

        assert not light.is_on(hass)
        assert hass.states.get(device_1).state == "not_home"
        assert hass.states.get(device_2).state == "home"
        assert hass.states.get("person.me").state == "not_home"

        # person home switches on
        hass.states.async_set(device_1, STATE_HOME)
        await hass.async_block_till_done()

        assert light.is_on(hass)
        assert hass.states.get(device_1).state == "home"
        assert hass.states.get(device_2).state == "home"
        assert hass.states.get("person.me").state == "home"
Beispiel #12
0
 def __init__(self, dev_id, data=None):
     """Set up OwnTracks entity."""
     self._dev_id = dev_id
     self._data = data or {}
     self.entity_id = ENTITY_ID_FORMAT.format(dev_id)
    def _update_member(self, member, dev_id):
        prev_seen, reported = self._dev_data.get(dev_id, (None, False))

        loc = member.get('location')
        try:
            last_seen = _utc_from_ts(loc.get('timestamp'))
        except AttributeError:
            last_seen = None

        if self._max_update_wait:
            now = dt_util.utcnow()
            update = last_seen or prev_seen or self._started
            overdue = now - update > self._max_update_wait
            if overdue and not reported and now - self._started > EVENT_DELAY:
                self._hass.bus.fire(
                    'life360_update_overdue',
                    {'entity_id': DT_ENTITY_ID_FORMAT.format(dev_id)})
                reported = True
            elif not overdue and reported:
                self._hass.bus.fire(
                    'life360_update_restored', {
                        'entity_id':
                        DT_ENTITY_ID_FORMAT.format(dev_id),
                        'wait':
                        str(last_seen -
                            (prev_seen or self._started)).split('.')[0]
                    })
                reported = False

        self._dev_data[dev_id] = last_seen or prev_seen, reported

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

        if last_seen and (not prev_seen or last_seen > prev_seen):
            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, 'GPS data invalid: {}, {}, {}'.format(
                        lat, lon, gps_accuracy))
                return

            self._ok(dev_id)

            msg = 'Updating {}'.format(dev_id)
            if prev_seen:
                msg += '; Time since last update: {}'.format(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

            place_name = loc.get('name') or None

            # Does user want location name to be shown as state?
            if SHOW_PLACES in self._show_as_state:
                loc_name = place_name
                # Make sure Home Place is always seen exactly as home,
                # which is the special device_tracker state for home.
                if loc_name and loc_name.lower() == self._home_place_name:
                    loc_name = STATE_HOME
            else:
                loc_name = None

            # If a place name is given, then address will just be a copy of
            # it, so don't bother with address. Otherwise, piece address
            # lines together, depending on which are present.
            if place_name:
                address = None
            else:
                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')
            try:
                speed = float(raw_speed) * SPEED_FACTOR_MPH
                if self._hass.config.units.is_metric:
                    speed = convert(speed, LENGTH_MILES, LENGTH_KILOMETERS)
                speed = max(0, round(speed))
            except (TypeError, ValueError):
                speed = STATE_UNKNOWN
            driving = _bool_attr_from_int(loc.get('isDriving'))
            if (driving in (STATE_UNKNOWN, False)
                    and self._driving_speed is not None
                    and speed != STATE_UNKNOWN):
                driving = speed >= self._driving_speed
            moving = _bool_attr_from_int(loc.get('inTransit'))

            if self._time_as in [TZ_DEVICE_UTC, TZ_DEVICE_LOCAL]:
                # timezone_at will return a string or None.
                tzname = self._tf.timezone_at(lng=lon, lat=lat)
                # get_time_zone will return a tzinfo or None.
                time_zone = dt_util.get_time_zone(tzname)
                attrs = {ATTR_TIME_ZONE: tzname or STATE_UNKNOWN}
            else:
                time_zone = None
                attrs = {}

            attrs.update({
                ATTR_ADDRESS:
                address,
                ATTR_AT_LOC_SINCE:
                self._dt_attr_from_ts(loc.get('since'), time_zone),
                ATTR_BATTERY_CHARGING:
                _bool_attr_from_int(loc.get('charge')),
                ATTR_DRIVING:
                driving,
                ATTR_LAST_SEEN:
                self._dt_attr_from_utc(last_seen, time_zone),
                ATTR_MOVING:
                moving,
                ATTR_RAW_SPEED:
                raw_speed,
                ATTR_SPEED:
                speed,
                ATTR_WIFI_ON:
                _bool_attr_from_int(loc.get('wifiState')),
            })

            # If we don't have a location name yet and user wants driving or
            # moving to be shown as state, and current location is not in a HA
            # zone, then update location name accordingly.
            if not loc_name:
                active_zone = run_callback_threadsafe(self._hass.loop,
                                                      async_active_zone,
                                                      self._hass, 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.capitalize()
                    elif SHOW_MOVING in self._show_as_state and moving is True:
                        loc_name = SHOW_MOVING.capitalize()

            try:
                battery = int(float(loc.get('battery')))
            except (TypeError, ValueError):
                battery = None

            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'))