Propagate the in_zones attribute from device trackers in person entities (#167192)

This commit is contained in:
Erik Montnemery
2026-04-02 14:09:57 +02:00
committed by GitHub
parent 9f41e3341f
commit d78c05ab62
2 changed files with 26 additions and 4 deletions

View File

@@ -11,6 +11,7 @@ import voluptuous as vol
from homeassistant.auth import EVENT_USER_REMOVED
from homeassistant.components import persistent_notification, websocket_api
from homeassistant.components.device_tracker import (
ATTR_IN_ZONES,
ATTR_SOURCE_TYPE,
DOMAIN as DEVICE_TRACKER_DOMAIN,
SourceType,
@@ -435,6 +436,7 @@ class Person(
self._unsub_track_device: Callable[[], None] | None = None
self._attr_state: str | None = None
self.device_trackers: list[str] = []
self._in_zones: list[str] = []
self._attr_unique_id = config[CONF_ID]
self._set_attrs_from_config()
@@ -552,6 +554,7 @@ class Person(
self._latitude = None
self._longitude = None
self._gps_accuracy = None
self._in_zones = []
self._update_extra_state_attributes()
self.async_write_ha_state()
@@ -567,6 +570,7 @@ class Person(
self._latitude = coordinates.attributes.get(ATTR_LATITUDE)
self._longitude = coordinates.attributes.get(ATTR_LONGITUDE)
self._gps_accuracy = coordinates.attributes.get(ATTR_GPS_ACCURACY)
self._in_zones = coordinates.attributes.get(ATTR_IN_ZONES, [])
@callback
def _update_extra_state_attributes(self) -> None:
@@ -575,6 +579,7 @@ class Person(
ATTR_EDITABLE: self.editable,
ATTR_ID: self.unique_id,
ATTR_DEVICE_TRACKERS: self.device_trackers,
ATTR_IN_ZONES: self._in_zones,
}
if self._latitude is not None:

View File

@@ -6,7 +6,11 @@ from unittest.mock import patch
import pytest
from homeassistant.components import person
from homeassistant.components.device_tracker import ATTR_SOURCE_TYPE, SourceType
from homeassistant.components.device_tracker import (
ATTR_IN_ZONES,
ATTR_SOURCE_TYPE,
SourceType,
)
from homeassistant.components.person import (
ATTR_DEVICE_TRACKERS,
ATTR_SOURCE,
@@ -119,6 +123,7 @@ async def test_setup_tracker(hass: HomeAssistant, hass_admin_user: MockUser) ->
ATTR_EDITABLE: False,
ATTR_FRIENDLY_NAME: "tracked person",
ATTR_ID: "1234",
ATTR_IN_ZONES: [],
ATTR_USER_ID: user_id,
}
@@ -223,10 +228,17 @@ async def test_setup_two_trackers(
# 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.
hass.states.async_set(
DEVICE_TRACKER,
"home",
{ATTR_SOURCE_TYPE: SourceType.ROUTER, ATTR_GPS_ACCURACY: 99},
{
ATTR_SOURCE_TYPE: SourceType.ROUTER,
ATTR_GPS_ACCURACY: 99,
ATTR_IN_ZONES: ["zone.fake"],
},
)
await hass.async_block_till_done()
@@ -235,9 +247,10 @@ async def test_setup_two_trackers(
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 comes from the coordinates source (home zone), not from
# the state source (router tracker which reported gps_accuracy=99).
# 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) == [
@@ -252,6 +265,7 @@ async def test_setup_two_trackers(
ATTR_LATITUDE: 12.123456,
ATTR_LONGITUDE: 13.123456,
ATTR_GPS_ACCURACY: 12,
ATTR_IN_ZONES: ["zone.work"],
ATTR_SOURCE_TYPE: SourceType.GPS,
},
)
@@ -267,6 +281,7 @@ async def test_setup_two_trackers(
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) == [
@@ -346,6 +361,7 @@ async def test_setup_router_ble_trackers(
ATTR_LATITUDE: 12.123456,
ATTR_LONGITUDE: 13.123456,
ATTR_GPS_ACCURACY: 12,
ATTR_IN_ZONES: ["zone.office"],
ATTR_SOURCE_TYPE: SourceType.BLUETOOTH_LE,
},
)
@@ -358,6 +374,7 @@ async def test_setup_router_ble_trackers(
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) == [