Ejemplo n.º 1
0
    async def async_update(self):
        """Get the latest data and updates the states."""
        # Get previous values of start and end
        p_start, p_end = self._period

        # Parse templates
        self.update_period()
        start, end = self._period

        # Convert times to UTC
        start = dt_util.as_utc(start)
        end = dt_util.as_utc(end)
        p_start = dt_util.as_utc(p_start)
        p_end = dt_util.as_utc(p_end)
        now = datetime.datetime.now()

        # Compute integer timestamps
        start_timestamp = math.floor(dt_util.as_timestamp(start))
        end_timestamp = math.floor(dt_util.as_timestamp(end))
        p_start_timestamp = math.floor(dt_util.as_timestamp(p_start))
        p_end_timestamp = math.floor(dt_util.as_timestamp(p_end))
        now_timestamp = math.floor(dt_util.as_timestamp(now))

        # If period has not changed and current time after the period end...
        if (start_timestamp == p_start_timestamp
                and end_timestamp == p_end_timestamp
                and end_timestamp <= now_timestamp):
            # Don't compute anything as the value cannot have changed
            return

        await self.opp.async_add_executor_job(self._update, start, end,
                                              now_timestamp, start_timestamp,
                                              end_timestamp)
Ejemplo n.º 2
0
def test_as_timestamp():
    """Test as_timestamp method."""
    ts = 1462401234
    utc_dt = dt_util.utc_from_timestamp(ts)
    assert ts == dt_util.as_timestamp(utc_dt)
    utc_iso = utc_dt.isoformat()
    assert ts == dt_util.as_timestamp(utc_iso)

    # confirm the ability to handle a string passed in
    delta = dt_util.as_timestamp("2016-01-01 12:12:12")
    delta -= dt_util.as_timestamp("2016-01-01 12:12:11")
    assert delta == 1
Ejemplo n.º 3
0
async def test_timezone_intervals(opp):
    """Test date sensor behavior in a timezone besides UTC."""
    new_tz = dt_util.get_time_zone("America/New_York")
    assert new_tz is not None
    dt_util.set_default_time_zone(new_tz)

    device = time_date.TimeDateSensor(opp, "date")
    now = dt_util.utc_from_timestamp(50000)
    with patch("openpeerpower.util.dt.utcnow", return_value=now):
        next_time = device.get_next_interval()
    # start of local day in EST was 18000.0
    # so the second day was 18000 + 86400
    assert next_time.timestamp() == 104400

    new_tz = dt_util.get_time_zone("America/Edmonton")
    assert new_tz is not None
    dt_util.set_default_time_zone(new_tz)
    now = dt_util.parse_datetime("2017-11-13 19:47:19-07:00")
    device = time_date.TimeDateSensor(opp, "date")
    with patch("openpeerpower.util.dt.utcnow", return_value=now):
        next_time = device.get_next_interval()
    assert next_time.timestamp() == dt_util.as_timestamp("2017-11-14 00:00:00-07:00")

    # Entering DST
    new_tz = dt_util.get_time_zone("Europe/Prague")
    assert new_tz is not None
    dt_util.set_default_time_zone(new_tz)

    now = dt_util.parse_datetime("2020-03-29 00:00+01:00")
    with patch("openpeerpower.util.dt.utcnow", return_value=now):
        next_time = device.get_next_interval()
    assert next_time.timestamp() == dt_util.as_timestamp("2020-03-30 00:00+02:00")

    now = dt_util.parse_datetime("2020-03-29 03:00+02:00")
    with patch("openpeerpower.util.dt.utcnow", return_value=now):
        next_time = device.get_next_interval()
    assert next_time.timestamp() == dt_util.as_timestamp("2020-03-30 00:00+02:00")

    # Leaving DST
    now = dt_util.parse_datetime("2020-10-25 00:00+02:00")
    with patch("openpeerpower.util.dt.utcnow", return_value=now):
        next_time = device.get_next_interval()
    assert next_time.timestamp() == dt_util.as_timestamp("2020-10-26 00:00:00+01:00")

    now = dt_util.parse_datetime("2020-10-25 23:59+01:00")
    with patch("openpeerpower.util.dt.utcnow", return_value=now):
        next_time = device.get_next_interval()
    assert next_time.timestamp() == dt_util.as_timestamp("2020-10-26 00:00:00+01:00")
Ejemplo n.º 4
0
async def test_connection_state_signalling(opp, aioclient_mock,
                                           mock_unifi_websocket):
    """Verify connection statesignalling and connection state are working."""
    client = {
        "hostname": "client",
        "ip": "10.0.0.1",
        "is_wired": True,
        "last_seen": dt_util.as_timestamp(dt_util.utcnow()),
        "mac": "00:00:00:00:00:01",
    }
    await setup_unifi_integration(opp,
                                  aioclient_mock,
                                  clients_response=[client])

    # Controller is connected
    assert opp.states.get("device_tracker.client").state == "home"

    mock_unifi_websocket(state=STATE_DISCONNECTED)
    await opp.async_block_till_done()

    # Controller is disconnected
    assert opp.states.get("device_tracker.client").state == "unavailable"

    mock_unifi_websocket(state=STATE_RUNNING)
    await opp.async_block_till_done()

    # Controller is once again connected
    assert opp.states.get("device_tracker.client").state == "home"
Ejemplo n.º 5
0
def convert_time_to_utc(timestr):
    """Take a string like 08:00:00 and convert it to a unix timestamp."""
    combined = datetime.combine(dt_util.start_of_local_day(),
                                dt_util.parse_time(timestr))
    if combined < datetime.now():
        combined = combined + timedelta(days=1)
    return dt_util.as_timestamp(combined)
Ejemplo n.º 6
0
async def test_timezone_intervals_empty_parameter(opp):
    """Test get_interval() without parameters."""
    new_tz = dt_util.get_time_zone("America/Edmonton")
    assert new_tz is not None
    dt_util.set_default_time_zone(new_tz)
    device = time_date.TimeDateSensor(opp, "date")
    next_time = device.get_next_interval()
    assert next_time.timestamp() == dt_util.as_timestamp("2017-11-14 00:00:00-07:00")
Ejemplo n.º 7
0
    def get_next_interval(self):
        """Compute next time an update should occur."""
        now = dt_util.utcnow()

        if self.type == "date":
            tomorrow = dt_util.as_local(now) + timedelta(days=1)
            return dt_util.start_of_local_day(tomorrow)

        if self.type == "beat":
            # Add 1 hour because @0 beats is at 23:00:00 UTC.
            timestamp = dt_util.as_timestamp(now + timedelta(hours=1))
            interval = 86.4
        else:
            timestamp = dt_util.as_timestamp(now)
            interval = 60

        delta = interval - (timestamp % interval)
        next_interval = now + timedelta(seconds=delta)
        _LOGGER.debug("%s + %s -> %s (%s)", now, delta, next_interval, self.type)

        return next_interval
Ejemplo n.º 8
0
 def get_next_interval(self, now=None):
     """Compute next time an update should occur."""
     if now is None:
         now = dt_util.utcnow()
     if self.type == "date":
         now = dt_util.start_of_local_day(dt_util.as_local(now))
         return now + timedelta(seconds=86400)
     if self.type == "beat":
         interval = 86.4
     else:
         interval = 60
     timestamp = int(dt_util.as_timestamp(now))
     delta = interval - (timestamp % interval)
     return now + timedelta(seconds=delta)
Ejemplo n.º 9
0
 def update(self):
     """Get the latest data and updates the states."""
     mydata = self.opp.data[DATA_HYDRAWISE].data
     _LOGGER.debug("Updating Hydrawise sensor: %s", self._name)
     relay_data = mydata.relays[self.data["relay"] - 1]
     if self._sensor_type == "watering_time":
         if relay_data["timestr"] == "Now":
             self._state = int(relay_data["run"] / 60)
         else:
             self._state = 0
     else:  # _sensor_type == 'next_cycle'
         next_cycle = min(relay_data["time"], TWO_YEAR_SECONDS)
         _LOGGER.debug("New cycle time: %s", next_cycle)
         self._state = dt.utc_from_timestamp(
             dt.as_timestamp(dt.now()) + next_cycle).isoformat()
Ejemplo n.º 10
0
 def __init__(self, opp, name):
     """Initialize Demo mailbox."""
     super().__init__(opp, name)
     self._messages = {}
     txt = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
     for idx in range(0, 10):
         msgtime = int(dt.as_timestamp(dt.utcnow()) - 3600 * 24 * (10 - idx))
         msgtxt = f"Message {idx + 1}. {txt * (1 + idx * (idx % 2))}"
         msgsha = sha1(msgtxt.encode("utf-8")).hexdigest()
         msg = {
             "info": {
                 "origtime": msgtime,
                 "callerid": "John Doe <212-555-1212>",
                 "duration": "10",
             },
             "text": msgtxt,
             "sha": msgsha,
         }
         self._messages[msgsha] = msg
Ejemplo n.º 11
0
    async def async_added_to_opp(self):
        """Subscribe to updates."""
        if KEY_RAIN_DELAY in self._controller.init_data:
            self._state = self._controller.init_data[
                KEY_RAIN_DELAY] / 1000 > as_timestamp(now())

        # If the controller was in a rain delay state during a reboot, this re-sets the timer
        if self._state is True:
            delay_end = utc_from_timestamp(
                self._controller.init_data[KEY_RAIN_DELAY] / 1000)
            _LOGGER.debug("Re-setting rain delay timer for %s", delay_end)
            self._cancel_update = async_track_point_in_utc_time(
                self.opp, self._delay_expiration, delay_end)

        self.async_on_remove(
            async_dispatcher_connect(
                self.opp,
                SIGNAL_RACHIO_RAIN_DELAY_UPDATE,
                self._async_handle_any_update,
            ))
Ejemplo n.º 12
0
def forgiving_as_timestamp(value):
    """Try to convert value to timestamp."""
    try:
        return dt_util.as_timestamp(value)
    except (ValueError, TypeError):
        return None
Ejemplo n.º 13
0
    def get(self, request):
        """Get SpaceAPI data."""
        opp = request.app["opp"]
        spaceapi = dict(opp.data[DATA_SPACEAPI])
        is_sensors = spaceapi.get("sensors")

        location = {ATTR_LAT: opp.config.latitude, ATTR_LON: opp.config.longitude}

        try:
            location[ATTR_ADDRESS] = spaceapi[ATTR_LOCATION][CONF_ADDRESS]
        except KeyError:
            pass
        except TypeError:
            pass

        state_entity = spaceapi["state"][ATTR_ENTITY_ID]
        space_state = opp.states.get(state_entity)

        if space_state is not None:
            state = {
                ATTR_OPEN: space_state.state != "off",
                ATTR_LASTCHANGE: dt_util.as_timestamp(space_state.last_updated),
            }
        else:
            state = {ATTR_OPEN: "null", ATTR_LASTCHANGE: 0}

        with suppress(KeyError):
            state[ATTR_ICON] = {
                ATTR_OPEN: spaceapi["state"][CONF_ICON_OPEN],
                ATTR_CLOSE: spaceapi["state"][CONF_ICON_CLOSED],
            }

        data = {
            ATTR_API: SPACEAPI_VERSION,
            ATTR_CONTACT: spaceapi[CONF_CONTACT],
            ATTR_ISSUE_REPORT_CHANNELS: spaceapi[CONF_ISSUE_REPORT_CHANNELS],
            ATTR_LOCATION: location,
            ATTR_LOGO: spaceapi[CONF_LOGO],
            ATTR_SPACE: spaceapi[CONF_SPACE],
            ATTR_STATE: state,
            ATTR_URL: spaceapi[CONF_URL],
        }

        with suppress(KeyError):
            data[ATTR_CAM] = spaceapi[CONF_CAM]

        with suppress(KeyError):
            data[ATTR_SPACEFED] = spaceapi[CONF_SPACEFED]

        with suppress(KeyError):
            data[ATTR_STREAM] = spaceapi[CONF_STREAM]

        with suppress(KeyError):
            data[ATTR_FEEDS] = spaceapi[CONF_FEEDS]

        with suppress(KeyError):
            data[ATTR_CACHE] = spaceapi[CONF_CACHE]

        with suppress(KeyError):
            data[ATTR_PROJECTS] = spaceapi[CONF_PROJECTS]

        with suppress(KeyError):
            data[ATTR_RADIO_SHOW] = spaceapi[CONF_RADIO_SHOW]

        if is_sensors is not None:
            sensors = {}
            for sensor_type in is_sensors:
                sensors[sensor_type] = []
                for sensor in spaceapi["sensors"][sensor_type]:
                    sensor_data = self.get_sensor_data(opp, spaceapi, sensor)
                    sensors[sensor_type].append(sensor_data)
            data[ATTR_SENSORS] = sensors

        return self.json(data)
Ejemplo n.º 14
0
    def update(self):
        """Get the latest data and updates the states."""
        # Get previous values of start and end
        p_start, p_end = self._period

        # Parse templates
        self.update_period()
        start, end = self._period

        # Convert times to UTC
        start = dt_util.as_utc(start)
        end = dt_util.as_utc(end)
        p_start = dt_util.as_utc(p_start)
        p_end = dt_util.as_utc(p_end)
        now = datetime.datetime.now()

        # Compute integer timestamps
        start_timestamp = math.floor(dt_util.as_timestamp(start))
        end_timestamp = math.floor(dt_util.as_timestamp(end))
        p_start_timestamp = math.floor(dt_util.as_timestamp(p_start))
        p_end_timestamp = math.floor(dt_util.as_timestamp(p_end))
        now_timestamp = math.floor(dt_util.as_timestamp(now))

        # If period has not changed and current time after the period end...
        if (start_timestamp == p_start_timestamp
                and end_timestamp == p_end_timestamp
                and end_timestamp <= now_timestamp):
            # Don't compute anything as the value cannot have changed
            return

        # Get history between start and end
        history_list = history.state_changes_during_period(
            self.opptart, end, str(self._entity_id))

        if self._entity_id not in history_list.keys():
            return

        # Get the first state
        last_state = history.get_state(self.opptart, self._entity_id)
        last_state = last_state is not None and last_state == self._entity_state
        last_time = start_timestamp
        elapsed = 0
        count = 0

        # Make calculations
        for item in history_list.get(self._entity_id):
            current_state = item.state == self._entity_state
            current_time = item.last_changed.timestamp()

            if last_state:
                elapsed += current_time - last_time
            if current_state and not last_state:
                count += 1

            last_state = current_state
            last_time = current_time

        # Count time elapsed between last history state and end of measure
        if last_state:
            measure_end = min(end_timestamp, now_timestamp)
            elapsed += measure_end - last_time

        # Save value in hours
        self.value = elapsed / 3600

        # Save counter
        self.count = count
Ejemplo n.º 15
0
async def test_option_ignore_wired_bug(opp, aioclient_mock,
                                       mock_unifi_websocket):
    """Test option to ignore wired bug."""
    client = {
        "ap_mac": "00:00:00:00:02:01",
        "essid": "ssid",
        "hostname": "client",
        "ip": "10.0.0.1",
        "is_wired": False,
        "last_seen": dt_util.as_timestamp(dt_util.utcnow()),
        "mac": "00:00:00:00:00:01",
    }

    config_entry = await setup_unifi_integration(
        opp,
        aioclient_mock,
        options={CONF_IGNORE_WIRED_BUG: True},
        clients_response=[client],
    )
    controller = opp.data[UNIFI_DOMAIN][config_entry.entry_id]
    assert len(opp.states.async_entity_ids(TRACKER_DOMAIN)) == 1

    # Client is wireless
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_HOME
    assert client_state.attributes["is_wired"] is False

    # Trigger wired bug
    client["is_wired"] = True
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client],
    })
    await opp.async_block_till_done()

    # Wired bug in effect
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_HOME
    assert client_state.attributes["is_wired"] is True

    # pass time
    new_time = dt_util.utcnow() + controller.option_detection_time
    with patch("openpeerpower.util.dt.utcnow", return_value=new_time):
        async_fire_time_changed(opp, new_time)
        await opp.async_block_till_done()

    # Timer marks client as away
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_NOT_HOME
    assert client_state.attributes["is_wired"] is True

    # Mark client as connected again
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client],
    })
    await opp.async_block_till_done()

    # Ignoring wired bug allows client to go home again even while affected
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_HOME
    assert client_state.attributes["is_wired"] is True

    # Make client wireless
    client["is_wired"] = False
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client],
    })
    await opp.async_block_till_done()

    # Client is wireless and still connected
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_HOME
    assert client_state.attributes["is_wired"] is False
Ejemplo n.º 16
0
async def test_wireless_client_go_wired_issue(opp, aioclient_mock,
                                              mock_unifi_websocket):
    """Test the solution to catch wireless device go wired UniFi issue.

    UniFi has a known issue that when a wireless device goes away it sometimes gets marked as wired.
    """
    client = {
        "essid": "ssid",
        "hostname": "client",
        "ip": "10.0.0.1",
        "is_wired": False,
        "last_seen": dt_util.as_timestamp(dt_util.utcnow()),
        "mac": "00:00:00:00:00:01",
    }

    config_entry = await setup_unifi_integration(opp,
                                                 aioclient_mock,
                                                 clients_response=[client])
    controller = opp.data[UNIFI_DOMAIN][config_entry.entry_id]

    assert len(opp.states.async_entity_ids(TRACKER_DOMAIN)) == 1

    # Client is wireless
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_HOME
    assert client_state.attributes["is_wired"] is False

    # Trigger wired bug
    client["is_wired"] = True
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client],
    })
    await opp.async_block_till_done()

    # Wired bug fix keeps client marked as wireless
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_HOME
    assert client_state.attributes["is_wired"] is False

    # Pass time
    new_time = dt_util.utcnow() + controller.option_detection_time
    with patch("openpeerpower.util.dt.utcnow", return_value=new_time):
        async_fire_time_changed(opp, new_time)
        await opp.async_block_till_done()

    # Marked as home according to the timer
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_NOT_HOME
    assert client_state.attributes["is_wired"] is False

    # Try to mark client as connected
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client],
    })
    await opp.async_block_till_done()

    # Make sure it don't go online again until wired bug disappears
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_NOT_HOME
    assert client_state.attributes["is_wired"] is False

    # Make client wireless
    client["is_wired"] = False
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client],
    })
    await opp.async_block_till_done()

    # Client is no longer affected by wired bug and can be marked online
    client_state = opp.states.get("device_tracker.client")
    assert client_state.state == STATE_HOME
    assert client_state.attributes["is_wired"] is False
Ejemplo n.º 17
0
async def test_option_ssid_filter(opp, aioclient_mock, mock_unifi_websocket):
    """Test the SSID filter works.

    Client will travel from a supported SSID to an unsupported ssid.
    Client on SSID2 will be removed on change of options.
    """
    client = {
        "essid": "ssid",
        "hostname": "client",
        "is_wired": False,
        "last_seen": dt_util.as_timestamp(dt_util.utcnow()),
        "mac": "00:00:00:00:00:01",
    }
    client_on_ssid2 = {
        "essid": "ssid2",
        "hostname": "client_on_ssid2",
        "is_wired": False,
        "last_seen": 1562600145,
        "mac": "00:00:00:00:00:02",
    }

    config_entry = await setup_unifi_integration(
        opp, aioclient_mock, clients_response=[client, client_on_ssid2])
    controller = opp.data[UNIFI_DOMAIN][config_entry.entry_id]

    assert len(opp.states.async_entity_ids(TRACKER_DOMAIN)) == 2

    assert opp.states.get("device_tracker.client").state == STATE_HOME
    assert opp.states.get(
        "device_tracker.client_on_ssid2").state == STATE_NOT_HOME

    # Setting SSID filter will remove clients outside of filter
    opp.config_entries.async_update_entry(
        config_entry,
        options={CONF_SSID_FILTER: ["ssid"]},
    )
    await opp.async_block_till_done()

    # Not affected by SSID filter
    assert opp.states.get("device_tracker.client").state == STATE_HOME

    # Removed due to SSID filter
    assert not opp.states.get("device_tracker.client_on_ssid2")

    # Roams to SSID outside of filter
    client["essid"] = "other_ssid"
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client],
    })
    # Data update while SSID filter is in effect shouldn't create the client
    client_on_ssid2["last_seen"] = dt_util.as_timestamp(dt_util.utcnow())
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client_on_ssid2],
    })
    await opp.async_block_till_done()

    # SSID filter marks client as away
    assert opp.states.get("device_tracker.client").state == STATE_NOT_HOME

    # SSID still outside of filter
    assert not opp.states.get("device_tracker.client_on_ssid2")

    # Remove SSID filter
    opp.config_entries.async_update_entry(
        config_entry,
        options={CONF_SSID_FILTER: []},
    )
    await opp.async_block_till_done()

    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client],
    })
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client_on_ssid2],
    })
    await opp.async_block_till_done()

    assert opp.states.get("device_tracker.client").state == STATE_HOME
    assert opp.states.get("device_tracker.client_on_ssid2").state == STATE_HOME

    # Time pass to mark client as away

    new_time = dt_util.utcnow() + controller.option_detection_time
    with patch("openpeerpower.util.dt.utcnow", return_value=new_time):
        async_fire_time_changed(opp, new_time)
        await opp.async_block_till_done()

    assert opp.states.get("device_tracker.client").state == STATE_NOT_HOME

    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client_on_ssid2],
    })
    await opp.async_block_till_done()

    # Client won't go away until after next update
    assert opp.states.get("device_tracker.client_on_ssid2").state == STATE_HOME

    # Trigger update to get client marked as away
    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client_on_ssid2],
    })
    await opp.async_block_till_done()

    new_time = (dt_util.utcnow() + controller.option_detection_time +
                timedelta(seconds=1))
    with patch("openpeerpower.util.dt.utcnow", return_value=new_time):
        async_fire_time_changed(opp, new_time)
        await opp.async_block_till_done()

    assert opp.states.get(
        "device_tracker.client_on_ssid2").state == STATE_NOT_HOME
Ejemplo n.º 18
0
async def test_tracked_clients(opp, aioclient_mock, mock_unifi_websocket):
    """Test the update_items function with some clients."""
    client_1 = {
        "ap_mac": "00:00:00:00:02:01",
        "essid": "ssid",
        "hostname": "client_1",
        "ip": "10.0.0.1",
        "is_wired": False,
        "last_seen": 1562600145,
        "mac": "00:00:00:00:00:01",
    }
    client_2 = {
        "ip": "10.0.0.2",
        "is_wired": True,
        "last_seen": 1562600145,
        "mac": "00:00:00:00:00:02",
        "name": "Client 2",
    }
    client_3 = {
        "essid": "ssid2",
        "hostname": "client_3",
        "ip": "10.0.0.3",
        "is_wired": False,
        "last_seen": 1562600145,
        "mac": "00:00:00:00:00:03",
    }
    client_4 = {
        "essid": "ssid",
        "hostname": "client_4",
        "ip": "10.0.0.4",
        "is_wired": True,
        "last_seen": dt_util.as_timestamp(dt_util.utcnow()),
        "mac": "00:00:00:00:00:04",
    }
    client_5 = {
        "essid": "ssid",
        "hostname": "client_5",
        "ip": "10.0.0.5",
        "is_wired": True,
        "last_seen": None,
        "mac": "00:00:00:00:00:05",
    }

    await setup_unifi_integration(
        opp,
        aioclient_mock,
        options={CONF_SSID_FILTER: ["ssid"]},
        clients_response=[client_1, client_2, client_3, client_4, client_5],
        known_wireless_clients=(client_4["mac"], ),
    )

    assert len(opp.states.async_entity_ids(TRACKER_DOMAIN)) == 4
    assert opp.states.get("device_tracker.client_1").state == STATE_NOT_HOME
    assert opp.states.get("device_tracker.client_2").state == STATE_NOT_HOME

    # Client on SSID not in SSID filter
    assert not opp.states.get("device_tracker.client_3")

    # Wireless client with wired bug, if bug active on restart mark device away
    assert opp.states.get("device_tracker.client_4").state == STATE_NOT_HOME

    # A client that has never been seen should be marked away.
    assert opp.states.get("device_tracker.client_5").state == STATE_NOT_HOME

    # State change signalling works

    mock_unifi_websocket(data={
        "meta": {
            "message": MESSAGE_CLIENT
        },
        "data": [client_1],
    })
    await opp.async_block_till_done()

    assert opp.states.get("device_tracker.client_1").state == STATE_HOME