mirror of
https://github.com/home-assistant/core.git
synced 2025-09-11 07:41:35 +02:00
Person: Use the home zone lat/lon coordinates when detected home by a stationary tracker (#134075)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
@@ -15,6 +15,7 @@ from homeassistant.components.device_tracker import (
|
|||||||
DOMAIN as DEVICE_TRACKER_DOMAIN,
|
DOMAIN as DEVICE_TRACKER_DOMAIN,
|
||||||
SourceType,
|
SourceType,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.zone import ENTITY_ID_HOME
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_EDITABLE,
|
ATTR_EDITABLE,
|
||||||
ATTR_GPS_ACCURACY,
|
ATTR_GPS_ACCURACY,
|
||||||
@@ -464,7 +465,7 @@ class Person(
|
|||||||
"""Register device trackers."""
|
"""Register device trackers."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
if state := await self.async_get_last_state():
|
if state := await self.async_get_last_state():
|
||||||
self._parse_source_state(state)
|
self._parse_source_state(state, state)
|
||||||
|
|
||||||
if self.hass.is_running:
|
if self.hass.is_running:
|
||||||
# Update person now if hass is already running.
|
# Update person now if hass is already running.
|
||||||
@@ -514,7 +515,7 @@ class Person(
|
|||||||
@callback
|
@callback
|
||||||
def _update_state(self) -> None:
|
def _update_state(self) -> None:
|
||||||
"""Update the state."""
|
"""Update the state."""
|
||||||
latest_non_gps_home = latest_not_home = latest_gps = latest = None
|
latest_non_gps_home = latest_not_home = latest_gps = latest = coordinates = None
|
||||||
for entity_id in self._config[CONF_DEVICE_TRACKERS]:
|
for entity_id in self._config[CONF_DEVICE_TRACKERS]:
|
||||||
state = self.hass.states.get(entity_id)
|
state = self.hass.states.get(entity_id)
|
||||||
|
|
||||||
@@ -530,13 +531,23 @@ class Person(
|
|||||||
|
|
||||||
if latest_non_gps_home:
|
if latest_non_gps_home:
|
||||||
latest = latest_non_gps_home
|
latest = latest_non_gps_home
|
||||||
|
if (
|
||||||
|
latest_non_gps_home.attributes.get(ATTR_LATITUDE) is None
|
||||||
|
and latest_non_gps_home.attributes.get(ATTR_LONGITUDE) is None
|
||||||
|
and (home_zone := self.hass.states.get(ENTITY_ID_HOME))
|
||||||
|
):
|
||||||
|
coordinates = home_zone
|
||||||
|
else:
|
||||||
|
coordinates = latest_non_gps_home
|
||||||
elif latest_gps:
|
elif latest_gps:
|
||||||
latest = latest_gps
|
latest = latest_gps
|
||||||
|
coordinates = latest_gps
|
||||||
else:
|
else:
|
||||||
latest = latest_not_home
|
latest = latest_not_home
|
||||||
|
coordinates = latest_not_home
|
||||||
|
|
||||||
if latest:
|
if latest and coordinates:
|
||||||
self._parse_source_state(latest)
|
self._parse_source_state(latest, coordinates)
|
||||||
else:
|
else:
|
||||||
self._attr_state = None
|
self._attr_state = None
|
||||||
self._source = None
|
self._source = None
|
||||||
@@ -548,15 +559,15 @@ class Person(
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _parse_source_state(self, state: State) -> None:
|
def _parse_source_state(self, state: State, coordinates: State) -> None:
|
||||||
"""Parse source state and set person attributes.
|
"""Parse source state and set person attributes.
|
||||||
|
|
||||||
This is a device tracker state or the restored person state.
|
This is a device tracker state or the restored person state.
|
||||||
"""
|
"""
|
||||||
self._attr_state = state.state
|
self._attr_state = state.state
|
||||||
self._source = state.entity_id
|
self._source = state.entity_id
|
||||||
self._latitude = state.attributes.get(ATTR_LATITUDE)
|
self._latitude = coordinates.attributes.get(ATTR_LATITUDE)
|
||||||
self._longitude = state.attributes.get(ATTR_LONGITUDE)
|
self._longitude = coordinates.attributes.get(ATTR_LONGITUDE)
|
||||||
self._gps_accuracy = state.attributes.get(ATTR_GPS_ACCURACY)
|
self._gps_accuracy = state.attributes.get(ATTR_GPS_ACCURACY)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"domain": "person",
|
"domain": "person",
|
||||||
"name": "Person",
|
"name": "Person",
|
||||||
"codeowners": [],
|
"codeowners": [],
|
||||||
"dependencies": ["image_upload", "http"],
|
"dependencies": ["image_upload", "http", "zone"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/person",
|
"documentation": "https://www.home-assistant.io/integrations/person",
|
||||||
"integration_type": "system",
|
"integration_type": "system",
|
||||||
"iot_class": "calculated",
|
"iot_class": "calculated",
|
||||||
|
@@ -14,7 +14,9 @@ from homeassistant.components.person import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
ATTR_EDITABLE,
|
||||||
ATTR_ENTITY_PICTURE,
|
ATTR_ENTITY_PICTURE,
|
||||||
|
ATTR_FRIENDLY_NAME,
|
||||||
ATTR_GPS_ACCURACY,
|
ATTR_GPS_ACCURACY,
|
||||||
ATTR_ID,
|
ATTR_ID,
|
||||||
ATTR_LATITUDE,
|
ATTR_LATITUDE,
|
||||||
@@ -112,14 +114,19 @@ async def test_setup_tracker(hass: HomeAssistant, hass_admin_user: MockUser) ->
|
|||||||
}
|
}
|
||||||
assert await async_setup_component(hass, DOMAIN, config)
|
assert await async_setup_component(hass, DOMAIN, config)
|
||||||
|
|
||||||
|
expected_attributes = {
|
||||||
|
ATTR_DEVICE_TRACKERS: [DEVICE_TRACKER],
|
||||||
|
ATTR_EDITABLE: False,
|
||||||
|
ATTR_FRIENDLY_NAME: "tracked person",
|
||||||
|
ATTR_ID: "1234",
|
||||||
|
ATTR_USER_ID: user_id,
|
||||||
|
}
|
||||||
|
|
||||||
state = hass.states.get("person.tracked_person")
|
state = hass.states.get("person.tracked_person")
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
assert state.attributes.get(ATTR_ID) == "1234"
|
assert state.attributes == expected_attributes
|
||||||
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
|
|
||||||
|
|
||||||
|
# Test home without coordinates
|
||||||
hass.states.async_set(DEVICE_TRACKER, "home")
|
hass.states.async_set(DEVICE_TRACKER, "home")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
@@ -131,13 +138,41 @@ async def test_setup_tracker(hass: HomeAssistant, hass_admin_user: MockUser) ->
|
|||||||
|
|
||||||
state = hass.states.get("person.tracked_person")
|
state = hass.states.get("person.tracked_person")
|
||||||
assert state.state == "home"
|
assert state.state == "home"
|
||||||
assert state.attributes.get(ATTR_ID) == "1234"
|
assert state.attributes == expected_attributes | {
|
||||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
ATTR_LATITUDE: 32.87336,
|
||||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
ATTR_LONGITUDE: -117.22743,
|
||||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
ATTR_SOURCE: DEVICE_TRACKER,
|
||||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
}
|
||||||
assert state.attributes.get(ATTR_DEVICE_TRACKERS) == [DEVICE_TRACKER]
|
|
||||||
|
|
||||||
|
# Test home with coordinates
|
||||||
|
hass.states.async_set(
|
||||||
|
DEVICE_TRACKER,
|
||||||
|
"home",
|
||||||
|
{ATTR_LATITUDE: 10.123456, ATTR_LONGITUDE: 11.123456, ATTR_GPS_ACCURACY: 10},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("person.tracked_person")
|
||||||
|
assert state.state == "home"
|
||||||
|
assert state.attributes == expected_attributes | {
|
||||||
|
ATTR_GPS_ACCURACY: 10,
|
||||||
|
ATTR_LATITUDE: 10.123456,
|
||||||
|
ATTR_LONGITUDE: 11.123456,
|
||||||
|
ATTR_SOURCE: DEVICE_TRACKER,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test not_home without coordinates
|
||||||
|
hass.states.async_set(
|
||||||
|
DEVICE_TRACKER,
|
||||||
|
"not_home",
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("person.tracked_person")
|
||||||
|
assert state.state == "not_home"
|
||||||
|
assert state.attributes == expected_attributes | {ATTR_SOURCE: DEVICE_TRACKER}
|
||||||
|
|
||||||
|
# Test not_home with coordinates
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
DEVICE_TRACKER,
|
DEVICE_TRACKER,
|
||||||
"not_home",
|
"not_home",
|
||||||
@@ -147,13 +182,12 @@ async def test_setup_tracker(hass: HomeAssistant, hass_admin_user: MockUser) ->
|
|||||||
|
|
||||||
state = hass.states.get("person.tracked_person")
|
state = hass.states.get("person.tracked_person")
|
||||||
assert state.state == "not_home"
|
assert state.state == "not_home"
|
||||||
assert state.attributes.get(ATTR_ID) == "1234"
|
assert state.attributes == expected_attributes | {
|
||||||
assert state.attributes.get(ATTR_LATITUDE) == 10.123456
|
ATTR_GPS_ACCURACY: 10,
|
||||||
assert state.attributes.get(ATTR_LONGITUDE) == 11.123456
|
ATTR_LATITUDE: 10.123456,
|
||||||
assert state.attributes.get(ATTR_GPS_ACCURACY) == 10
|
ATTR_LONGITUDE: 11.123456,
|
||||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
ATTR_SOURCE: DEVICE_TRACKER,
|
||||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
}
|
||||||
assert state.attributes.get(ATTR_DEVICE_TRACKERS) == [DEVICE_TRACKER]
|
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_two_trackers(
|
async def test_setup_two_trackers(
|
||||||
@@ -188,8 +222,8 @@ async def test_setup_two_trackers(
|
|||||||
state = hass.states.get("person.tracked_person")
|
state = hass.states.get("person.tracked_person")
|
||||||
assert state.state == "home"
|
assert state.state == "home"
|
||||||
assert state.attributes.get(ATTR_ID) == "1234"
|
assert state.attributes.get(ATTR_ID) == "1234"
|
||||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
assert state.attributes.get(ATTR_LATITUDE) == 32.87336
|
||||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
assert state.attributes.get(ATTR_LONGITUDE) == -117.22743
|
||||||
assert state.attributes.get(ATTR_GPS_ACCURACY) is None
|
assert state.attributes.get(ATTR_GPS_ACCURACY) is None
|
||||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
||||||
assert state.attributes.get(ATTR_USER_ID) == user_id
|
assert state.attributes.get(ATTR_USER_ID) == user_id
|
||||||
@@ -453,8 +487,8 @@ async def test_load_person_storage(
|
|||||||
state = hass.states.get("person.tracked_person")
|
state = hass.states.get("person.tracked_person")
|
||||||
assert state.state == "home"
|
assert state.state == "home"
|
||||||
assert state.attributes.get(ATTR_ID) == "1234"
|
assert state.attributes.get(ATTR_ID) == "1234"
|
||||||
assert state.attributes.get(ATTR_LATITUDE) is None
|
assert state.attributes.get(ATTR_LATITUDE) == 32.87336
|
||||||
assert state.attributes.get(ATTR_LONGITUDE) is None
|
assert state.attributes.get(ATTR_LONGITUDE) == -117.22743
|
||||||
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
assert state.attributes.get(ATTR_SOURCE) == DEVICE_TRACKER
|
||||||
assert state.attributes.get(ATTR_USER_ID) == hass_admin_user.id
|
assert state.attributes.get(ATTR_USER_ID) == hass_admin_user.id
|
||||||
|
|
||||||
@@ -817,7 +851,7 @@ async def test_reload(hass: HomeAssistant, hass_admin_user: MockUser) -> None:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert len(hass.states.async_entity_ids()) == 2
|
assert len(hass.states.async_entity_ids()) == 3 # Person1, Person2, zone.home
|
||||||
|
|
||||||
state_1 = hass.states.get("person.person_1")
|
state_1 = hass.states.get("person.person_1")
|
||||||
state_2 = hass.states.get("person.person_2")
|
state_2 = hass.states.get("person.person_2")
|
||||||
@@ -847,7 +881,7 @@ async def test_reload(hass: HomeAssistant, hass_admin_user: MockUser) -> None:
|
|||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.states.async_entity_ids()) == 2
|
assert len(hass.states.async_entity_ids()) == 3 # Person1, Person2, zone.home
|
||||||
|
|
||||||
state_1 = hass.states.get("person.person_1")
|
state_1 = hass.states.get("person.person_1")
|
||||||
state_2 = hass.states.get("person.person_2")
|
state_2 = hass.states.get("person.person_2")
|
||||||
|
Reference in New Issue
Block a user