mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Improve person tests (#172997)
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
"""The tests for the person component."""
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import person
|
||||
@@ -46,11 +48,13 @@ async def test_minimal_setup(hass: HomeAssistant) -> None:
|
||||
|
||||
state = hass.states.get("person.test_person")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
||||
assert state.attributes.get(ATTR_SOURCE) is None
|
||||
assert state.attributes.get(ATTR_USER_ID) is None
|
||||
assert state.attributes.get(ATTR_ENTITY_PICTURE) is None
|
||||
assert state.attributes == {
|
||||
ATTR_DEVICE_TRACKERS: [],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "test person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
}
|
||||
|
||||
|
||||
async def test_setup_no_id(hass: HomeAssistant) -> None:
|
||||
@@ -73,11 +77,14 @@ async def test_setup_user_id(hass: HomeAssistant, hass_admin_user: MockUser) ->
|
||||
|
||||
state = hass.states.get("person.test_person")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
||||
assert state.attributes.get(ATTR_SOURCE) is None
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes == {
|
||||
ATTR_DEVICE_TRACKERS: [],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "test person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_USER_ID: user_id,
|
||||
}
|
||||
|
||||
|
||||
async def test_valid_invalid_user_ids(
|
||||
@@ -95,11 +102,14 @@ async def test_valid_invalid_user_ids(
|
||||
|
||||
state = hass.states.get("person.test_valid_user")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
||||
assert state.attributes.get(ATTR_SOURCE) is None
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes == {
|
||||
ATTR_DEVICE_TRACKERS: [],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "test valid user",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_USER_ID: user_id,
|
||||
}
|
||||
state = hass.states.get("person.test_bad_user")
|
||||
assert state is None
|
||||
|
||||
@@ -211,26 +221,26 @@ async def test_setup_two_trackers(
|
||||
}
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
|
||||
expected_attributes = {
|
||||
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER, DEVICE_TRACKER_2],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "tracked person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_USER_ID: user_id,
|
||||
}
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
||||
assert state.attributes.get(ATTR_SOURCE) is None
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes == expected_attributes
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
# Router tracker at home with gps_accuracy — the person entity should get
|
||||
# coordinates from the home zone (which has no gps_accuracy),not from the
|
||||
# router tracker's attributes.
|
||||
# Note: This is not a realistic test case, a router tracker would not have
|
||||
# gps_accuracy, but we want to assert that the person entity uses latitude
|
||||
# longitude and accuracy from the home zone, not from the state attributes
|
||||
# of the device tracker.
|
||||
# Router tracker at home — person gets coordinates from the home zone,
|
||||
# not from the router tracker. The router tracker has gps_accuracy=99
|
||||
# and in_zones=["zone.fake"] to verify these are NOT propagated.
|
||||
# Router tracker at home with gps_accuracy — the person entity gets latitude,
|
||||
# longitude and accuracy from the home zone (the coordinates source), not
|
||||
# from the router tracker's own attributes.
|
||||
# Note: a router tracker would not really have gps_accuracy; it is set here
|
||||
# (together with in_zones=["zone.fake"]) only to assert it is NOT propagated.
|
||||
hass.states.async_set(
|
||||
DEVICE_TRACKER,
|
||||
"home",
|
||||
@@ -244,19 +254,11 @@ async def test_setup_two_trackers(
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "home"
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) == 32.87336
|
||||
assert state.attributes.get(ATTR_LONGITUDE) == -117.22743
|
||||
# GPS accuracy and in_zones come from the coordinates source (home zone),
|
||||
# not from the state source (router tracker).
|
||||
assert state.attributes.get(ATTR_GPS_ACCURACY) is None
|
||||
assert state.attributes.get(ATTR_IN_ZONES) == []
|
||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes.get(ATTR_DEVICE_TRACKERS) == [
|
||||
DEVICE_TRACKER,
|
||||
DEVICE_TRACKER_2,
|
||||
]
|
||||
assert state.attributes == expected_attributes | {
|
||||
ATTR_LATITUDE: 32.87336,
|
||||
ATTR_LONGITUDE: -117.22743,
|
||||
ATTR_SOURCE: DEVICE_TRACKER,
|
||||
}
|
||||
|
||||
hass.states.async_set(
|
||||
DEVICE_TRACKER_2,
|
||||
@@ -277,24 +279,20 @@ async def test_setup_two_trackers(
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "not_home"
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) == 12.123456
|
||||
assert state.attributes.get(ATTR_LONGITUDE) == 13.123456
|
||||
assert state.attributes.get(ATTR_GPS_ACCURACY) == 12
|
||||
assert state.attributes.get(ATTR_IN_ZONES) == ["zone.work"]
|
||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER_2
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes.get(ATTR_DEVICE_TRACKERS) == [
|
||||
DEVICE_TRACKER,
|
||||
DEVICE_TRACKER_2,
|
||||
]
|
||||
assert state.attributes == expected_attributes | {
|
||||
ATTR_GPS_ACCURACY: 12,
|
||||
ATTR_LATITUDE: 12.123456,
|
||||
ATTR_LONGITUDE: 13.123456,
|
||||
ATTR_IN_ZONES: ["zone.work"],
|
||||
ATTR_SOURCE: DEVICE_TRACKER_2,
|
||||
}
|
||||
|
||||
hass.states.async_set(DEVICE_TRACKER_2, "zone1", {ATTR_SOURCE_TYPE: SourceType.GPS})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "zone1"
|
||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER_2
|
||||
assert state.attributes == expected_attributes | {ATTR_SOURCE: DEVICE_TRACKER_2}
|
||||
|
||||
hass.states.async_set(DEVICE_TRACKER, "home", {ATTR_SOURCE_TYPE: SourceType.ROUTER})
|
||||
await hass.async_block_till_done()
|
||||
@@ -303,7 +301,11 @@ async def test_setup_two_trackers(
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "home"
|
||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
||||
assert state.attributes == expected_attributes | {
|
||||
ATTR_LATITUDE: 32.87336,
|
||||
ATTR_LONGITUDE: -117.22743,
|
||||
ATTR_SOURCE: DEVICE_TRACKER,
|
||||
}
|
||||
|
||||
|
||||
async def test_setup_router_ble_trackers(
|
||||
@@ -326,13 +328,18 @@ async def test_setup_router_ble_trackers(
|
||||
}
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
|
||||
expected_attributes = {
|
||||
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER, DEVICE_TRACKER_2],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "tracked person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_USER_ID: user_id,
|
||||
}
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
||||
assert state.attributes.get(ATTR_SOURCE) is None
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes == expected_attributes
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
@@ -343,16 +350,7 @@ async def test_setup_router_ble_trackers(
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "not_home"
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
||||
assert state.attributes.get(ATTR_GPS_ACCURACY) is None
|
||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes.get(ATTR_DEVICE_TRACKERS) == [
|
||||
DEVICE_TRACKER,
|
||||
DEVICE_TRACKER_2,
|
||||
]
|
||||
assert state.attributes == expected_attributes | {ATTR_SOURCE: DEVICE_TRACKER}
|
||||
|
||||
# Set the BLE tracker to the "office" zone.
|
||||
hass.states.async_set(
|
||||
@@ -371,17 +369,292 @@ async def test_setup_router_ble_trackers(
|
||||
# The person should be in the office.
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "office"
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) == 12.123456
|
||||
assert state.attributes.get(ATTR_LONGITUDE) == 13.123456
|
||||
assert state.attributes.get(ATTR_GPS_ACCURACY) == 12
|
||||
assert state.attributes.get(ATTR_IN_ZONES) == ["zone.office"]
|
||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER_2
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes.get(ATTR_DEVICE_TRACKERS) == [
|
||||
DEVICE_TRACKER,
|
||||
DEVICE_TRACKER_2,
|
||||
]
|
||||
assert state.attributes == expected_attributes | {
|
||||
ATTR_GPS_ACCURACY: 12,
|
||||
ATTR_LATITUDE: 12.123456,
|
||||
ATTR_LONGITUDE: 13.123456,
|
||||
ATTR_IN_ZONES: ["zone.office"],
|
||||
ATTR_SOURCE: DEVICE_TRACKER_2,
|
||||
}
|
||||
|
||||
|
||||
# Representative device tracker states for the three priority buckets used by
|
||||
# `Person._update_state`, in priority order:
|
||||
# 1. a non-GPS tracker reporting "home" (highest priority)
|
||||
# 2. any GPS tracker, regardless of its state (middle priority)
|
||||
# 3. everything else, e.g. a non-GPS scanner associated with a non-home zone
|
||||
# or a non-GPS tracker reporting "not_home" (lowest priority)
|
||||
# Each value is a (state, attributes) tuple passed to `hass.states.async_set`.
|
||||
_ROUTER_HOME: tuple[str, dict[str, Any]] = (
|
||||
"home",
|
||||
{ATTR_SOURCE_TYPE: SourceType.ROUTER, ATTR_IN_ZONES: ["zone.home"]},
|
||||
)
|
||||
_ROUTER_NOT_HOME: tuple[str, dict[str, Any]] = (
|
||||
"not_home",
|
||||
{ATTR_SOURCE_TYPE: SourceType.ROUTER, ATTR_IN_ZONES: []},
|
||||
)
|
||||
# A scanner tracker associated with a non-home zone reports the zone's name as
|
||||
# its state and lists the zone in `in_zones` (see device_tracker PR #172157).
|
||||
_SCANNER_OFFICE: tuple[str, dict[str, Any]] = (
|
||||
"office",
|
||||
{ATTR_SOURCE_TYPE: SourceType.ROUTER, ATTR_IN_ZONES: ["zone.office"]},
|
||||
)
|
||||
_GPS_NOT_HOME: tuple[str, dict[str, Any]] = (
|
||||
"not_home",
|
||||
{
|
||||
ATTR_SOURCE_TYPE: SourceType.GPS,
|
||||
ATTR_LATITUDE: 1.0,
|
||||
ATTR_LONGITUDE: 2.0,
|
||||
ATTR_GPS_ACCURACY: 5,
|
||||
ATTR_IN_ZONES: [],
|
||||
},
|
||||
)
|
||||
_GPS_WORK: tuple[str, dict[str, Any]] = (
|
||||
"work",
|
||||
{
|
||||
ATTR_SOURCE_TYPE: SourceType.GPS,
|
||||
ATTR_LATITUDE: 3.0,
|
||||
ATTR_LONGITUDE: 4.0,
|
||||
ATTR_GPS_ACCURACY: 7,
|
||||
ATTR_IN_ZONES: ["zone.work"],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def _async_setup_person_two_trackers(hass: HomeAssistant, user_id: str) -> None:
|
||||
"""Set up a person tracked by two device trackers, with hass running."""
|
||||
hass.set_state(CoreState.not_running)
|
||||
config = {
|
||||
DOMAIN: {
|
||||
"id": "1234",
|
||||
"name": "tracked person",
|
||||
"user_id": user_id,
|
||||
"device_trackers": [DEVICE_TRACKER, DEVICE_TRACKER_2],
|
||||
}
|
||||
}
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("high_priority", "low_priority", "expected_state", "expected_extra"),
|
||||
[
|
||||
# A non-GPS "home" tracker outranks a GPS tracker reporting coordinates.
|
||||
# Its coordinates come from the home zone (it has none of its own).
|
||||
pytest.param(
|
||||
_ROUTER_HOME,
|
||||
_GPS_NOT_HOME,
|
||||
"home",
|
||||
{
|
||||
ATTR_LATITUDE: 32.87336,
|
||||
ATTR_LONGITUDE: -117.22743,
|
||||
ATTR_SOURCE: DEVICE_TRACKER,
|
||||
},
|
||||
id="home_beats_gps",
|
||||
),
|
||||
# A non-GPS "home" tracker outranks a scanner in another zone.
|
||||
pytest.param(
|
||||
_ROUTER_HOME,
|
||||
_SCANNER_OFFICE,
|
||||
"home",
|
||||
{
|
||||
ATTR_LATITUDE: 32.87336,
|
||||
ATTR_LONGITUDE: -117.22743,
|
||||
ATTR_SOURCE: DEVICE_TRACKER,
|
||||
},
|
||||
id="home_beats_other_zone",
|
||||
),
|
||||
# A GPS tracker outranks a scanner associated with another zone.
|
||||
pytest.param(
|
||||
_GPS_WORK,
|
||||
_SCANNER_OFFICE,
|
||||
"work",
|
||||
{
|
||||
ATTR_GPS_ACCURACY: 7,
|
||||
ATTR_LATITUDE: 3.0,
|
||||
ATTR_LONGITUDE: 4.0,
|
||||
ATTR_IN_ZONES: ["zone.work"],
|
||||
ATTR_SOURCE: DEVICE_TRACKER,
|
||||
},
|
||||
id="gps_beats_other_zone",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_state_priority_overrides_recency(
|
||||
hass: HomeAssistant,
|
||||
hass_admin_user: MockUser,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
high_priority: tuple[str, dict[str, Any]],
|
||||
low_priority: tuple[str, dict[str, Any]],
|
||||
expected_state: str,
|
||||
expected_extra: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test the higher-priority bucket wins even when its state is stale.
|
||||
|
||||
There is no time-based expiry: a long-stale state from a higher-priority
|
||||
bucket still wins over a fresh state from a lower-priority bucket.
|
||||
"""
|
||||
await _async_setup_person_two_trackers(hass, hass_admin_user.id)
|
||||
|
||||
# The higher-priority tracker reports first and then goes stale.
|
||||
hass.states.async_set(DEVICE_TRACKER, high_priority[0], high_priority[1])
|
||||
await hass.async_block_till_done()
|
||||
freezer.tick(timedelta(hours=2))
|
||||
# The lower-priority tracker reports a much more recent update.
|
||||
hass.states.async_set(DEVICE_TRACKER_2, low_priority[0], low_priority[1])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == expected_state
|
||||
assert (
|
||||
state.attributes
|
||||
== {
|
||||
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER, DEVICE_TRACKER_2],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "tracked person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_USER_ID: hass_admin_user.id,
|
||||
}
|
||||
| expected_extra
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("older", "newer", "expected_state", "expected_extra"),
|
||||
[
|
||||
# GPS bucket: the most recent GPS state wins.
|
||||
pytest.param(
|
||||
_GPS_WORK,
|
||||
_GPS_NOT_HOME,
|
||||
"not_home",
|
||||
{
|
||||
ATTR_GPS_ACCURACY: 5,
|
||||
ATTR_LATITUDE: 1.0,
|
||||
ATTR_LONGITUDE: 2.0,
|
||||
ATTR_SOURCE: DEVICE_TRACKER_2,
|
||||
},
|
||||
id="gps_newer_not_home",
|
||||
),
|
||||
pytest.param(
|
||||
_GPS_NOT_HOME,
|
||||
_GPS_WORK,
|
||||
"work",
|
||||
{
|
||||
ATTR_GPS_ACCURACY: 7,
|
||||
ATTR_LATITUDE: 3.0,
|
||||
ATTR_LONGITUDE: 4.0,
|
||||
ATTR_IN_ZONES: ["zone.work"],
|
||||
ATTR_SOURCE: DEVICE_TRACKER_2,
|
||||
},
|
||||
id="gps_newer_work",
|
||||
),
|
||||
# Lowest-priority bucket: a fresh scanner in another zone wins over a
|
||||
# stale "not_home", and vice versa.
|
||||
pytest.param(
|
||||
_ROUTER_NOT_HOME,
|
||||
_SCANNER_OFFICE,
|
||||
"office",
|
||||
{ATTR_IN_ZONES: ["zone.office"], ATTR_SOURCE: DEVICE_TRACKER_2},
|
||||
id="other_newer_office",
|
||||
),
|
||||
pytest.param(
|
||||
_SCANNER_OFFICE,
|
||||
_ROUTER_NOT_HOME,
|
||||
"not_home",
|
||||
{ATTR_SOURCE: DEVICE_TRACKER_2},
|
||||
id="other_newer_not_home",
|
||||
),
|
||||
# "home" bucket: the most recent "home" tracker becomes the source and
|
||||
# its coordinates come from the home zone.
|
||||
pytest.param(
|
||||
_ROUTER_HOME,
|
||||
_ROUTER_HOME,
|
||||
"home",
|
||||
{
|
||||
ATTR_LATITUDE: 32.87336,
|
||||
ATTR_LONGITUDE: -117.22743,
|
||||
ATTR_SOURCE: DEVICE_TRACKER_2,
|
||||
},
|
||||
id="home_newer",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_most_recent_state_in_bucket_wins(
|
||||
hass: HomeAssistant,
|
||||
hass_admin_user: MockUser,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
older: tuple[str, dict[str, Any]],
|
||||
newer: tuple[str, dict[str, Any]],
|
||||
expected_state: str,
|
||||
expected_extra: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test that within a bucket the most recently updated state is picked."""
|
||||
await _async_setup_person_two_trackers(hass, hass_admin_user.id)
|
||||
|
||||
hass.states.async_set(DEVICE_TRACKER, older[0], older[1])
|
||||
await hass.async_block_till_done()
|
||||
freezer.tick(timedelta(minutes=5))
|
||||
hass.states.async_set(DEVICE_TRACKER_2, newer[0], newer[1])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == expected_state
|
||||
# The newer tracker is the source.
|
||||
assert (
|
||||
state.attributes
|
||||
== {
|
||||
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER, DEVICE_TRACKER_2],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "tracked person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_USER_ID: hass_admin_user.id,
|
||||
}
|
||||
| expected_extra
|
||||
)
|
||||
|
||||
|
||||
async def test_scanner_associated_with_other_zone(
|
||||
hass: HomeAssistant, hass_admin_user: MockUser
|
||||
) -> None:
|
||||
"""Test a person tracked by a scanner associated with a non-home zone.
|
||||
|
||||
A connected scanner associated with a non-home zone reports the zone name
|
||||
and lists the zone in `in_zones`. As a non-GPS tracker not reporting "home"
|
||||
it lands in the lowest-priority bucket, so it has no coordinate fallback.
|
||||
"""
|
||||
hass.set_state(CoreState.not_running)
|
||||
user_id = hass_admin_user.id
|
||||
config = {
|
||||
DOMAIN: {
|
||||
"id": "1234",
|
||||
"name": "tracked person",
|
||||
"user_id": user_id,
|
||||
"device_trackers": DEVICE_TRACKER,
|
||||
}
|
||||
}
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
hass.states.async_set(DEVICE_TRACKER, _SCANNER_OFFICE[0], _SCANNER_OFFICE[1])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# No coordinates: a scanner tracker provides none of its own, and as a
|
||||
# lowest-priority state it gets no coordinate fallback from the home zone.
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "office"
|
||||
assert state.attributes == {
|
||||
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "tracked person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: ["zone.office"],
|
||||
ATTR_SOURCE: DEVICE_TRACKER,
|
||||
ATTR_USER_ID: user_id,
|
||||
}
|
||||
|
||||
|
||||
async def test_ignore_unavailable_states(
|
||||
@@ -400,8 +673,18 @@ async def test_ignore_unavailable_states(
|
||||
}
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
|
||||
expected_attributes = {
|
||||
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER, DEVICE_TRACKER_2],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_FRIENDLY_NAME: "tracked person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_USER_ID: user_id,
|
||||
}
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes == expected_attributes
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
@@ -413,6 +696,7 @@ async def test_ignore_unavailable_states(
|
||||
# Unknown, as only 1 device tracker has a state, but we ignore that one
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes == expected_attributes
|
||||
|
||||
hass.states.async_set(DEVICE_TRACKER_2, "not_home")
|
||||
await hass.async_block_till_done()
|
||||
@@ -420,6 +704,7 @@ async def test_ignore_unavailable_states(
|
||||
# Take state of tracker 2
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "not_home"
|
||||
assert state.attributes == expected_attributes | {ATTR_SOURCE: DEVICE_TRACKER_2}
|
||||
|
||||
# state 1 is newer but ignored, keep tracker 2 state
|
||||
hass.states.async_set(DEVICE_TRACKER, "unknown")
|
||||
@@ -427,6 +712,7 @@ async def test_ignore_unavailable_states(
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "not_home"
|
||||
assert state.attributes == expected_attributes | {ATTR_SOURCE: DEVICE_TRACKER_2}
|
||||
|
||||
|
||||
async def test_restore_home_state(
|
||||
@@ -456,15 +742,21 @@ async def test_restore_home_state(
|
||||
}
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
|
||||
# When restoring state the entity_id of the person will be used as source.
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "home"
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) == 10.12346
|
||||
assert state.attributes.get(ATTR_LONGITUDE) == 11.12346
|
||||
# When restoring state the entity_id of the person will be used as source.
|
||||
assert state.attributes.get(ATTR_SOURCE) == "person.tracked_person"
|
||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||
assert state.attributes.get(ATTR_ENTITY_PICTURE) == "/bla"
|
||||
assert state.attributes == {
|
||||
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER],
|
||||
ATTR_EDITABLE: False,
|
||||
ATTR_ENTITY_PICTURE: "/bla",
|
||||
ATTR_FRIENDLY_NAME: "tracked person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_LATITUDE: 10.12346,
|
||||
ATTR_LONGITUDE: 11.12346,
|
||||
ATTR_SOURCE: "person.tracked_person",
|
||||
ATTR_USER_ID: user_id,
|
||||
}
|
||||
|
||||
|
||||
async def test_duplicate_ids(hass: HomeAssistant, hass_admin_user: MockUser) -> None:
|
||||
@@ -502,13 +794,18 @@ async def test_load_person_storage(
|
||||
hass: HomeAssistant, hass_admin_user: MockUser, storage_setup
|
||||
) -> None:
|
||||
"""Test set up person from storage."""
|
||||
expected_attributes = {
|
||||
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER],
|
||||
ATTR_EDITABLE: True,
|
||||
ATTR_FRIENDLY_NAME: "tracked person",
|
||||
ATTR_ID: "1234",
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_USER_ID: hass_admin_user.id,
|
||||
}
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
||||
assert state.attributes.get(ATTR_SOURCE) is None
|
||||
assert state.attributes.get(ATTR_USER_ID) == hass_admin_user.id
|
||||
assert state.attributes == expected_attributes
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
@@ -517,11 +814,11 @@ async def test_load_person_storage(
|
||||
|
||||
state = hass.states.get("person.tracked_person")
|
||||
assert state.state == "home"
|
||||
assert state.attributes.get(ATTR_ID) == "1234"
|
||||
assert state.attributes.get(ATTR_LATITUDE) == 32.87336
|
||||
assert state.attributes.get(ATTR_LONGITUDE) == -117.22743
|
||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
||||
assert state.attributes.get(ATTR_USER_ID) == hass_admin_user.id
|
||||
assert state.attributes == expected_attributes | {
|
||||
ATTR_LATITUDE: 32.87336,
|
||||
ATTR_LONGITUDE: -117.22743,
|
||||
ATTR_SOURCE: DEVICE_TRACKER,
|
||||
}
|
||||
|
||||
|
||||
async def test_load_person_storage_two_nonlinked(
|
||||
|
||||
Reference in New Issue
Block a user