Skip to content

Detect and discard stale iCloud locations - #181391

Open
TeroPihlaja wants to merge 1 commit into
home-assistant:devfrom
TeroPihlaja:icloud/stale-location-detection
Open

Detect and discard stale iCloud locations#181391
TeroPihlaja wants to merge 1 commit into
home-assistant:devfrom
TeroPihlaja:icloud/stale-location-detection

Conversation

@TeroPihlaja

@TeroPihlaja TeroPihlaja commented Sep 5, 2026

Copy link
Copy Markdown

Proposed change

iCloud keeps serving the last known fix for a device it cannot reach, flagged with isOld. The integration stored those fixes like any other, so a device that had left home stayed pinned to its last location indefinitely and presence automations kept treating it as home.

A fix is now discarded only once iCloud has marked it cached and it is older than the longest gap between two fetches, by which point a fresher one should already have superseded it.

Both signals are needed because neither is sufficient alone:

  • isOld says a cached fix was served, but not why. A device that was briefly unreachable looks exactly like one that has genuinely moved away, so acting on the flag alone would drop locations that are still good.
  • The age alone is no better: a device sitting still keeps reporting the same old timestamp while iCloud still considers that fix current, and discarding those would throw away the only location such a device ever reports.

test_recent_cached_location_kept and test_old_uncached_location_kept cover those two cases, and both pass with or without the change — they exist to pin down that neither signal on its own discards a location. test_stale_location_cleared covers the case being fixed and fails on unmodified dev.

The threshold is taken from the configured maximum interval rather than the current fetch interval, because _fetch_interval is recomputed after the device loop runs and drops to 15 seconds while devices are pending — a threshold that short would discard every cached fix there is.

Once a fix has been discarded the tracker has no coordinates and reports unknown, rather than continuing to claim the device is where it used to be.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:
  • Link to developer documentation pull request:
  • Link to frontend pull request:

Supersedes #169004, which was closed by the stale bot. Related and independent: #181389 and #181390. That PR had review feedback from a maintainer, and both points are addressed here:

  • "Why do we use both is old and check a stale interval?" — answered above and in the _is_stale() docstring, with the two tests above as the executable form of the argument.
  • "Don't patch internals like the dispatcher" / "instead have HA setup the integration and read the state machine" — the tests are rebuilt on that basis. They set the integration up through hass.config_entries.async_setup, advance time to let the account poll, and assert on hass.states.get(...). Nothing patches the dispatcher, and the only mock is PyiCloudService itself.

This needs the bugfix label for the required-labels check to pass — I cannot add it myself.

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies a diff between library versions and ideally a link to the changelog/release notes is added to the PR description.

To help with the load of incoming pull requests:

@home-assistant

home-assistant Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Hey there @Quentame, @nzapponi, mind taking a look at this pull request as it has been labeled with an integration (icloud) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of icloud can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant mark-draft Mark the pull request as draft.
  • @home-assistant ready-for-review Remove the draft status from the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign icloud Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant update-branch Update the pull request branch with the base branch.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Mandatory PR-template sections are missing, and the new code contains avoidable maintenance overhead.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Detects stale cached iCloud locations so trackers report unknown instead of outdated coordinates.

Changes:

  • Adds stale-location detection using isOld, timestamp age, and polling interval.
  • Handles unavailable tracker coordinates safely.
  • Adds integration-level coverage for stale, recent, and uncached locations.
File summaries
File Description
account.py Detects and clears stale locations.
const.py Adds location metadata constants.
device_tracker.py Supports missing coordinates.
conftest.py Adds mutable iCloud device fixtures.
test_device_tracker.py Tests location retention and clearing.
Review details

Suppressed comments (1)

homeassistant/components/icloud/account.py:461

  • Condense this docstring to the non-obvious threshold choice; the multi-paragraph explanation duplicates the PR description and tests, making a simple predicate harder to maintain.
        """Return whether a location fix is too old to be trusted.

        Both signals are needed, because neither is sufficient alone:

        `isOld` says iCloud served a cached fix rather than a fresh one, but
  • Files reviewed: 5/5 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread homeassistant/components/icloud/account.py Outdated
Comment thread tests/components/icloud/conftest.py Outdated
Comment thread tests/components/icloud/test_device_tracker.py
iCloud keeps serving the last known fix for a device it cannot reach,
flagged with isOld. The integration stored those fixes like any other,
so a device that had left home stayed pinned to its last location
indefinitely and presence automations kept treating it as home.

Discard a fix only once iCloud has marked it cached and it is older than
the longest gap between two fetches, by which point a fresher one should
already have superseded it. Neither signal works alone: isOld does not
say why a fix was cached, so a briefly unreachable device looks like one
that has moved away, and a device sitting still keeps reporting the same
old timestamp while iCloud still considers that fix current. The tests
cover both of those cases alongside the stale one.

Once a fix has been discarded the tracker has no coordinates and reports
'unknown', rather than continuing to claim the device is where it used
to be.
Copilot AI review requested due to automatic review settings September 5, 2026 16:45
@TeroPihlaja
TeroPihlaja force-pushed the icloud/stale-location-detection branch from e180e7e to 815f35a Compare September 5, 2026 16:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The stale-location behavior is focused, correctly propagated to entity state, and adequately covered by integration-level tests.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants