diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index 0dd8646b17e..46e9a121649 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -15,6 +15,7 @@ from homeassistant.components.device_tracker import ( DOMAIN as DEVICE_TRACKER_DOMAIN, SourceType, ) +from homeassistant.components.zone import ENTITY_ID_HOME from homeassistant.const import ( ATTR_EDITABLE, ATTR_GPS_ACCURACY, @@ -464,7 +465,7 @@ class Person( """Register device trackers.""" await super().async_added_to_hass() if state := await self.async_get_last_state(): - self._parse_source_state(state) + self._parse_source_state(state, state) if self.hass.is_running: # Update person now if hass is already running. @@ -514,7 +515,7 @@ class Person( @callback def _update_state(self) -> None: """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]: state = self.hass.states.get(entity_id) @@ -530,13 +531,23 @@ class Person( if 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: latest = latest_gps + coordinates = latest_gps else: latest = latest_not_home + coordinates = latest_not_home - if latest: - self._parse_source_state(latest) + if latest and coordinates: + self._parse_source_state(latest, coordinates) else: self._attr_state = None self._source = None @@ -548,15 +559,15 @@ class Person( self.async_write_ha_state() @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. This is a device tracker state or the restored person state. """ self._attr_state = state.state self._source = state.entity_id - self._latitude = state.attributes.get(ATTR_LATITUDE) - self._longitude = state.attributes.get(ATTR_LONGITUDE) + self._latitude = coordinates.attributes.get(ATTR_LATITUDE) + self._longitude = coordinates.attributes.get(ATTR_LONGITUDE) self._gps_accuracy = state.attributes.get(ATTR_GPS_ACCURACY) @callback diff --git a/homeassistant/components/person/manifest.json b/homeassistant/components/person/manifest.json index 0c1792e9277..46ccf85db4a 100644 --- a/homeassistant/components/person/manifest.json +++ b/homeassistant/components/person/manifest.json @@ -2,7 +2,7 @@ "domain": "person", "name": "Person", "codeowners": [], - "dependencies": ["image_upload", "http"], + "dependencies": ["image_upload", "http", "zone"], "documentation": "https://www.home-assistant.io/integrations/person", "integration_type": "system", "iot_class": "calculated", diff --git a/tests/components/person/test_init.py b/tests/components/person/test_init.py index c001da86adb..81b38f59a3d 100644 --- a/tests/components/person/test_init.py +++ b/tests/components/person/test_init.py @@ -14,7 +14,9 @@ from homeassistant.components.person import ( DOMAIN, ) from homeassistant.const import ( + ATTR_EDITABLE, ATTR_ENTITY_PICTURE, + ATTR_FRIENDLY_NAME, ATTR_GPS_ACCURACY, ATTR_ID, 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) + 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") 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 + # Test home without coordinates hass.states.async_set(DEVICE_TRACKER, "home") 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") assert state.state == "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_SOURCE) == DEVICE_TRACKER - assert state.attributes.get(ATTR_USER_ID) == user_id - assert state.attributes.get(ATTR_DEVICE_TRACKERS) == [DEVICE_TRACKER] + assert state.attributes == expected_attributes | { + ATTR_LATITUDE: 32.87336, + ATTR_LONGITUDE: -117.22743, + ATTR_SOURCE: 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( DEVICE_TRACKER, "not_home", @@ -147,13 +182,12 @@ async def test_setup_tracker(hass: HomeAssistant, hass_admin_user: MockUser) -> 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) == 10.123456 - assert state.attributes.get(ATTR_LONGITUDE) == 11.123456 - assert state.attributes.get(ATTR_GPS_ACCURACY) == 10 - 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] + assert state.attributes == expected_attributes | { + ATTR_GPS_ACCURACY: 10, + ATTR_LATITUDE: 10.123456, + ATTR_LONGITUDE: 11.123456, + ATTR_SOURCE: DEVICE_TRACKER, + } async def test_setup_two_trackers( @@ -188,8 +222,8 @@ 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) is None - assert state.attributes.get(ATTR_LONGITUDE) is None + assert state.attributes.get(ATTR_LATITUDE) == 32.87336 + assert state.attributes.get(ATTR_LONGITUDE) == -117.22743 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 @@ -453,8 +487,8 @@ 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) is None - assert state.attributes.get(ATTR_LONGITUDE) is None + 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 @@ -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_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() - 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_2 = hass.states.get("person.person_2")