Caution
After installing this update, you must delete your existing configuration and re-add the integration. This is due to major architectural changes. Location history should not be affected.
A comprehensive Home Assistant custom integration for Google's FindMy Device network, enabling real-time(ish) tracking and control of FindMy devices directly within Home Assistant!
Tip
Check out my companion Lovelace card, designed to work perfectly with this integration!
Tip
Home Assistant Core 2025.10 or newer is recommended. The functional minimum is 2025.9.1 (enforced in hacs.json and pyproject.toml), the empirically determined floor at which all bundled integration dependencies resolve (verified with script/check_ha_compatibility.py --find-minimum). The config subentry flow maturity and the async_added_to_hass behavior the tracker/service subentries depend on landed earlier, in 2025.8, and the Core-managed config subentry model itself has been available since the 2025.3 cycle. Running 2025.10 or newer is recommended for the bug fixes and stability improvements made since 2025.9.1, not because of a hard API requirement.
Our GitHub Actions pipeline now validates manifests with hassfest, runs the HACS integration checker, and executes Ruff, Codespell, Bandit, mypy --strict, and pytest -q --cov on Python 3.13 to protect code quality before merges.
For the quickest way to bootstrap Home Assistant test stubs before running pytest -q, see the Environment verification bullets in AGENTS.md.
- Clean caches: Run
make clean(or the equivalentfind … '__pycache__' -prunecommand from AGENTS.md) after test runs to avoid stale bytecode interfering with CI results. - Connectivity probe: Capture a quick HTTP/HTTPS check (for example,
python -m pip install --dry-run --no-deps pip) before longer installs so summaries document network status. - Home Assistant stubs: Run
make install-devto install Poetry dev/test dependencies (includinghomeassistantandpytest-homeassistant-custom-component) before runningpytest -q.
mypy --strict— run the full strict type-checker locally to mirror CI expectations before opening a pull request.make lint— invokeruff check . --fixacross the entire repository (auto-fixes safe issues). CI runs the same check without--fix.make test-unload— run the focused parent-unload rollback regression (tests/test_unload_subentry_cleanup.py) so you can confirm the recovery guardrails without executing the entire suite.make test-ha— execute the targeted regression smoke tests (tests/test_entity_recovery_manager.py,tests/test_homeassistant_callback_stub_helper.py) and then runpytest -q --covfor the full suite while teeing detailed output topytest_output.log. Append flags such as--maxfail=1 -k recoverywithmake test-ha PYTEST_ARGS="…"when you need custom pytest options, or override the coverage summary withmake test-ha PYTEST_COV_FLAGS="--cov-report=term"for slimmer output.make test-cov— runpytest -q --covwith coverage reporting (output teed topytest_output.log).make test-single TEST=<path>— run a single test file with optionalPYTEST_ARGS.make translation-check— check for missing translation keys across all locale files.make check-ha-compat— check dependency compatibility with Home Assistant.script/bootstrap_ssot_cached.sh— stage the Home Assistant Single Source of Truth (SSoT) wheels in.wheelhouse/ssotand install them from the local cache. PassSKIP_WHEELHOUSE_REFRESH=1to reuse the cached artifacts on subsequent bootstrap runs orPYTHON=python3.12to target an alternate interpreter. The helper also validates.wheelhouse/ssotagainstscript/ssot_wheel_manifest.txt(override withSSOT_MANIFEST=…) so repeated runs can confirm the primary wheels are cached without re-listing the full directory.python script/list_wheelhouse.py— print a grouped index of cached wheels (optionally against--manifest script/ssot_wheel_manifest.txt) before running lengthy installs so you can confirm the cache satisfies the manifest without scrolling through pip logs. Pass--allow-missingto preview the formatter when.wheelhouse/ssothas not been generated yet.
The repository uses Poetry to manage all
development and test dependencies. Run make install-dev from the project root
to install homeassistant, pytest-homeassistant-custom-component, and the
remaining dev/test packages into your Poetry-managed environment. This is the
quickest way to unblock pytest after cloning the repository or when a CI run
reports missing Home Assistant packages.
Alternatively, make test-ha runs the targeted regression smoke tests followed
by the full pytest -q --cov suite. Adjust PYTEST_ARGS/PYTEST_COV_FLAGS to
narrow the test selection.
The script/bootstrap_ssot_cached.sh helper stages heavy wheels (e.g.
homeassistant, pytest-homeassistant-custom-component) in .wheelhouse/ssot
for offline or cached installs. Delete the directory whenever you need to rebuild
the cache for a clean-room test of updated dependencies, or pass
SKIP_WHEELHOUSE_REFRESH=1 to reuse the existing cache.
The bootstrap script pulls down heavy wheels into .wheelhouse/. Package the
cache once and reuse it on future containers or machines instead of redownloading
hundreds of megabytes every regression run:
tar -czf wheelhouse-ha-cache.tgz -C .wheelhouse .Copy wheelhouse-ha-cache.tgz to the new environment, extract it at the project
root, and the next script/bootstrap_ssot_cached.sh invocation will reuse the
cached wheels immediately:
tar -xzf wheelhouse-ha-cache.tgz -C .When a dependency pin changes, delete the archive (and .wheelhouse/) or rerun
script/bootstrap_ssot_cached.sh to regenerate the cache before producing a fresh snapshot.
- Install Poetry if not already available:
pip install poetry - Install the full development toolchain (linting, typing, tests):
make install-dev(orpoetry install --with dev,test)- Minimal options-flow test stack (
homeassistant, pytest helpers, andbcryptonly):./script/install_options_flow_test_deps.sh
- Minimal options-flow test stack (
- Execute the regression suite, for example:
poetry run pytest tests/test_entity_recovery_manager.py tests/test_homeassistant_callback_stub_helper.pyor simplymake test-ha(override pytest flags withmake test-ha PYTEST_ARGS="--maxfail=1 -k callback"as needed)
make install: Install Poetry dependencies.make install-dev: Install Poetry dependencies with dev and test groups.make lint: Runruff check . --fixacross the entire repository (auto-fixes safe issues).make clean: Remove Python bytecode caches viascript/clean_pycache.pyto keep local environments tidy during development.make clean-node-modules: Remove thenode_modules/directory viascript/clean_node_modules.py.make test-ha: Run targeted Home Assistant regression smoke tests followed by the fullpytest -q --covsuite (output teed topytest_output.log).make test-unload: Execute the targeted unload regression suite (tests/test_unload_subentry_cleanup.py) to verify the parent-unload rollback path.make test-cov: Runpytest -q --covwith coverage reporting (output teed topytest_output.log).make test-single TEST=<path>: Run a single test file with optionalPYTEST_ARGS.make translation-check: Check for missing translation keys across all locale files.make check-ha-compat: Check dependency compatibility with Home Assistant viascript/check_ha_compatibility.py.make doctoc: Regenerate the AGENTS.md table of contents (requires Node.js; installs DocToc viamake bootstrap-doctoc).make bootstrap-doctoc: Install the DocToc npm dev dependency into the local cache.
- 🗺️ Real-time Device Tracking: Track Google FindMy devices with location data, sourced from the FindMy network
- ⏱️ Configurable Polling: Flexible polling intervals with rate limit protection
- 🔔 Sound Button Entity: Devices include button entity that plays a sound on supported devices
- ✅ Attribute grading system: Best location data is selected automatically based on recency, accuracy, and source of data
- 📍 Historical Map-View: Each tracker has a filterable Map-View that shows tracker movement with location data, localized into every shipped UI language
- 📋 Statistic Entity: Detailed statistics for monitoring integration performance
- #️⃣ Multi-Account Support: Add multiple Find Hub Google accounts that show up separately
- ❣️ More to come!
The manifest classifies Google Find My Device as a hub integration. Home Assistant treats the integration as a central coordinator that manages multiple connected devices, aligning documentation and compliance checks with the restored 1.7.0-3 metadata.
Note
This is a true integration! No docker containers, external systems, or scripts required (other than for initial authentication)!
- Click the button below to add this custom repository to HACS
- Install "Google Find My Device" from HACS
- Restart Home Assistant
- Add the integration through the UI
- Download this repository
- Copy the
googlefindmyfolder tocustom_components/ - Restart Home Assistant
- Add the integration through the UI
Important
Authentication is a 2-part process. One part requires use of a python script to obtain a secrets.json file, which will contain all necessary keys for authentication! This is currently the ONLY way to authenticate to the FindMy network.
- Navigate to GoogleFindMyTools repository and follow the directions on "How to use" the main.py script.
[!IMPORTANT] Run the authentication script from the same public IP address / network that your Home Assistant instance uses, and sign in with the same Google account that owns the trackers. Google ties the end-to-end encryption keys in
secrets.jsonto the account and may revoke them when requests arrive from a different IP or region. A mismatch produces a bundle that lists your devices and can ring them, but cannot decrypt any location reports — see Devices appear but no location updates. - CRITICAL STEP! Complete the ENTIRE authentication process to generate
Auth/secrets.json
Warning
While going through the process in main.py to authenticate, you MUST go through 2 login processes! After the first login is successful, your available devices will be listed. You must complete the next step to display location data for one of your devices. You will then login again. After you complete this step, you should see valid location data for your device, followed by several errors that are not important. ONLY at this point are you ready to move on to the next step!
- Copy the entire contents of the secrets.json file.
- Specifically, open the file in a text editor, select all, and copy.
Important
The encryption key (shared_key) is only retrieved during the second login, which happens when you actively select a device to locate. If you stop after the device list appears (skipping that step), the resulting secrets.json has no shared_key and Home Assistant will reject the import with a keys_missing error, because locations cannot be decrypted without it.
As an alternative to the external GoogleFindMyTools, this repository ships a bundled copy of the same CLI that fetches both keys automatically in a single run, without requiring you to select a device manually, so it is the more robust way to generate a complete secrets.json. To use it for the initial login you must run it from a flat folder: copy the contents of custom_components/googlefindmy/ into a fresh, empty directory (so that main.py, Auth/, NovaApi/, etc. sit directly at its top level) and run python main.py from there. The flat layout is required because the script auto-detects its location: when it is run in place at custom_components/googlefindmy/main.py it operates in Home Assistant mode and only lists devices from an existing secrets.json, so it will not open the Chrome login that creates the bundle.
- Add the integration to your Home Assistant install.
- In Home Assistant, paste the copied text from secrets.json when prompted.
- After completing authentication and adding devices, RESTART Home Assistant!
Note
Recently, some have had issues with the script from the repository above. If you follow all the steps in Leon's repository and are unable to get through the main.py sequence due to errors, please try using my modification of the script BACKUP:GoogleFindMyTools
- Secrets watcher (automatic pickup): Home Assistant watches for a fresh
secrets.jsonand, when one appears, opens the config flow with the email and tokens pre-filled, so you can confirm the entry without pasting anything manually. Out of the box it watches both the integration'sAuth/secrets.jsonand the login container'sdocker-login/data/secrets.json, so the container hand-off needs no configuration at all; the integration options only add further paths for layouts that differ from these defaults. Only one file is ever written: the watcher observes one or more paths, it never keeps a second copy. If several watched files happen to exist at once, the newest one wins (by modification time, with a content-hash tiebreak). After a successful import Home Assistant deletes the imported bundle and any watched copy that belongs to the same Google account — mirroring the existingAuth/cleanup — so no redundant secret lingers on disk. The cleanup is also content-aware: if the login container wrote fresher credentials of that same account while you were still confirming the flow, only the copies carrying the imported content are removed and the newer bundle is kept, so the watcher picks it up on its next scan instead of losing it. A watched file for a different account is likewise kept and logged rather than being silently discarded. An aborted or failed flow deletes nothing, so you can simply retry. - Update flows for existing entries: When the watcher detects refreshed credentials for an account that is already configured, the integration pushes a
discovery_updateflow. Accepting it reauthenticates the existing entry and keeps all devices and options intact. - Cloud discovery channel: Cloud-triggered discovery continues to operate in parallel, using the same deduplication logic as the secrets watcher. Regardless of source, duplicate flows are suppressed using Home Assistant's
DiscoveryKeymechanism.
- Home Assistant supports connecting multiple Google accounts, but only one config entry per email address stays active. When duplicate entries share the same Google account, the integration automatically disables and unloads the non-authoritative entries to prevent device duplication and token conflicts.
- The disabled entries remain visible in Settings → Devices & Services with an integration-managed disabled state so you can review or remove them manually. Reactivating a disabled duplicate requires removing the authoritative entry first or supplying credentials for a different Google account.
- The login container publishes two ports with different purposes, and they are configured separately: the one-click token endpoint (7901, read by Home Assistant, pinned to loopback in the compose overlay as a security boundary and deliberately not configurable) and the noVNC viewer (7900, opened by your browser,
GFMY_NOVNC_BIND/GFMY_NOVNC_URL_HOST, or simply./login.sh --ip <address>). Home Assistant cannot guess which address your browser can reach, so the config flow only renders a clickable noVNC link for a non-loopback IP. Details, defaults and the network-mode requirement:custom_components/googlefindmy/docker-login/README.md. - If two
secrets.jsonfiles for different accounts are ever present in the watched paths at the same time, only the newer file is imported; the older account's file is kept (not deleted) and logged, so it is discovered and offered on the next scan rather than being silently discarded. Write one bundle at a time; the login container always writes a single complete bundle, so this only matters if you place files manually.
Third-party consumers should anchor on the google_device_id state attribute when associating Find My trackers with external data sources (for example, Bermuda BLE Trilogy listeners). MAC addresses rotate for privacy and are intentionally omitted from state; google_device_id is the stable, registry-aligned identifier that will not change across reboots. See docs/Ephemeral_Identifier_Resolver_API.md for usage guidance and templating examples.
The integration ships a bidirectional bridge to the jleinenbach/bermuda Bermuda BLE Trilateration fork. Two capabilities are available:
- EID Resolver API (always on). Bermuda detects FMDN advertisements from your trackers locally and asks GoogleFindMy to map the ephemeral identifier to your
google_device_id. The two integrations then share one Home Assistant device while keeping their owndevice_trackerentities, so live coordinates from your own BLE scanners and cloud coordinates from the Find Hub network coexist on the same tag. - FMDN Finder uploads (experimental, currently blocked). When enabled via the
FEATURE_FMDN_FINDER_ENABLEDfeature flag incustom_components/googlefindmy/const.py, the integration wires up a Bermuda listener that prepares an end-to-end encrypted Finder report for every stable area change. The actual upload to Google's Find Hub network is hard-disabled incustom_components/googlefindmy/fmdn_finder/google_uploader.py(FMDN_UPLOAD_ENABLED = False) because the endpoint requires DroidGuard attestation that Home Assistant cannot produce. The flag is therefore a developer-facing opt-in for the listener pipeline only. See docs/BERMUDA_INTEGRATION.md and docs/FMDN_UPLOAD_LIMITATION.md for details.
Setup steps, troubleshooting, the device-matching contract (congealment via HA device_id, never via MAC or entity name), and the FMDN throttling rules are documented in docs/BERMUDA_INTEGRATION.md.
Accessible via the ⚙️ cogwheel button on the main Google Find My Device Integration page.
Tip
The options table is a mirror of OPTION_KEYS and DEFAULT_* in custom_components/googlefindmy/const.py, which is the single source of truth for option order and defaults.
| Option | Default | Units | Description |
|---|---|---|---|
ignored_devices |
none | - | Devices hidden from tracking. Use Manage ignored devices to restore them. |
location_poll_interval |
300 | seconds | How often the integration runs a poll cycle for all devices. |
device_poll_delay |
5 | seconds | How much time to wait between polling devices during a poll cycle. |
min_poll_interval |
60 | seconds | Hard lower bound between poll cycles and the manual locate cooldown. |
allow_history_fallback |
false | toggle | Falls back to Recorder history when no live device tracker state is available. |
enable_stats_entities |
true | toggle | Exposes the "Google Find My Integration" statistics entity (polling status, counters, etc.). |
google_home_filter_enabled |
true | toggle | Enables or disables Google Home device location filtering. |
google_home_filter_keywords |
nest,google,home,mini,hub,display,chromecast,speaker | text input | Comma-separated keywords used to filter out location data from Google Home devices. |
map_view_token_expiration |
false | toggle | Enables expiration of generated API tokens used in Map View history queries. |
semantic_locations |
none | - | User-defined semantic location zones (managed via a dedicated options flow step). |
delete_caches_on_remove |
true | toggle | Removes stored authentication caches when the integration is deleted. |
contributor_mode |
in_all_areas | selection | Chooses whether Google shares aggregated network-only data (high_traffic) or participates in full crowdsourced reporting (in_all_areas). |
stale_threshold |
3900 | seconds | After this many seconds (default: 65 minutes) without a location update, the tracker state becomes unknown. Use the "Last Location" entity to always see the last known position. |
show_location_age |
true | toggle | Adds a location_age attribute (in seconds, rounded to 60s) to each tracker entity. Excluded from Recorder history to keep DB size predictable. |
The Google Home filter helps prevent noisy location updates from speakers and displays that frequently report "Home":
- Defaults: The filter starts enabled with keywords
nest,google,home,mini,hub,display,chromecast, andspeaker. - Detection: Any detection whose semantic name contains one of these keywords is treated as a Google/Nest/Chromecast device.
- Substitution: When a Google Home detection is away from Home, the integration substitutes the
zone.homelatitude/longitude (and radius when available) so Home Assistant resolves the tracker tohomeinstead of the semantic label. - Debounce window: Consecutive "home" or Google Home detections for the same device within 15 minutes are suppressed to reduce spam.
- Tuning: Adjust
google_home_filter_enabledandgoogle_home_filter_keywordsfrom the integration's options flow to refine matching or disable substitution. The keywords field accepts comma-separated values or a list; changes update both the detection logic and the config flow copy.
Home Assistant's config-entry subentries let the integration organize devices and helper entities into feature groups. The coordinator deterministically provisions two subentries—SERVICE_SUBENTRY_KEY and TRACKER_SUBENTRY_KEY—and recreates them after reloads or restarts so entity grouping stays stable across updates. Both subentries persist alongside the config entry, storing options, visible_device_ids, and diagnostics based on their constant identifiers.
Home Assistant 2025.11+ handles subentry platform scheduling automatically. The parent async_setup_entry forwards the platform list once (no config_subentry_id allowed). Each platform then iterates the subentry coordinators on entry.runtime_data and calls async_add_entities(..., config_subentry_id=<subentry_id>) so devices and entities attach to the correct child entry. This pattern prevents orphaned tracker devices and avoids the silent failure caused by manual per-subentry forwarding.
- Parent–child enforcement: Each child is a
ConfigEntrywhoseparent_entry_idlinks it to the owning parent. Device Registry entries attach to the parent or a specific child—never both—and subentryunique_idvalues only need to be unique within the parent scope. - Lifecycle guardrail: Leave
async_setup(hass, config)for domain-level helpers only. Instance work lives inasync_setup_entry, which receives the populated entry and iteratesentry.subentriesso the parent and every child load without triggeringhomeassistant.config_entries.UnknownEntryduring startup or reloads.
The service hub subentry, identified by SERVICE_SUBENTRY_KEY, represents the account-level hub device for the integration.
- Home Assistant localizes the hub device name in the UI using
SERVICE_DEVICE_TRANSLATION_KEYinstead of a hard-coded string, so translations stay synchronized with the codebase. - The hub publishes only integration-scope diagnostics (polling status, authentication health, statistics counters) and intentionally surfaces zero tracker devices via
visible_device_ids. It is the logical parent for trackers, not a list of them. - All diagnostic entities exposed here point to a shared service device in Home Assistant's device registry. Each entity still exports a stable unique ID and provides
DeviceInfo, which Home Assistant uses to group the diagnostics under the service hub in the UI.1 - This shared hub device is what users see as the central integration device in the UI, reflecting Home Assistant's hub-style integration guidance.1
The tracker subentry, keyed by TRACKER_SUBENTRY_KEY, represents the phones, tablets, and tags imported from Google Find My Device.
- Each tracker entry backs per-device entities such as
device_tracker, “last seen” timestamp sensors, and control buttons for actions like ring / play sound / locate. - Trackers register as individual device entries in the Home Assistant device registry with their own unique IDs and
DeviceInfo. They remain standalone devices—Home Assistant automatically associates them with the correct config-entry subentry without manualvia_deviceorvia_device_idpointers.1 - Trackers never appear in the service hub’s
visible_device_idslist and are never assigned to the service hub subentry; they stay within the tracker subentry so repairs and options target the correct devices.
Config flows communicate state transitions through abort reasons, which power the toast notifications and translation strings surfaced in Home Assistant dialogs. Subentry-related flows use the following reason keys:
| Reason key | Where it appears | Meaning |
|---|---|---|
invalid_subentry |
Reconfigure handlers, options steps, and repairs forms | The requested feature group could not be resolved or was removed during the flow. |
repairs_no_subentries |
Repairs entry point and move action | No feature groups exist, so the repairs workflow cannot continue. |
repair_no_devices |
Repairs → Move devices | A move operation was attempted without selecting any devices. |
subentry_move_success |
Repairs → Move devices | The selected devices were re-assigned successfully; the flow exits with a success toast. |
subentry_delete_invalid |
Repairs → Delete subentry | There are too few removable feature groups to continue. |
subentry_remove_failed |
Repairs → Delete subentry | Removing the requested feature group failed unexpectedly. |
subentry_delete_success |
Repairs → Delete subentry | A feature group was deleted (after optional device reassignment). |
reconfigure_successful |
Credentials refresh flow | The integration applied new credentials and refreshed the chosen feature group. |
The strings.json and translation files under custom_components/googlefindmy/translations/ provide localized messages for each key so UI notifications remain consistent.
The integration provides a couple of Home Assistant Actions for use with automations. Note that Device ID is different than Entity ID. Device ID is a long, alpha-numeric value that can be obtained from the Device info pages.
| Action | Attribute | Description |
|---|---|---|
| googlefindmy.locate_device | Device ID (required) | Request fresh location data for a specific device. |
| googlefindmy.play_sound | Device ID (required) | Play a sound on a specific device for location assistance. Devices must be capable of playing a sound. Most devices should be compatible. |
| googlefindmy.stop_sound | Device ID (required) | Stop the active sound on the selected device. |
| googlefindmy.locate_external | Device ID (required), Device Name (optional) | Trigger the locate flow via the external helper while optionally labeling logs with a human-readable device name. |
| googlefindmy.refresh_device_urls | - | Refreshes all device Map View URLs. Useful if you are having problems with accessing Map View pages. |
| googlefindmy.rebuild_device_registry | - | Maintenance: rebuilds device registry links for Google Find My hubs and removes tracker devices incorrectly tied to the parent entry. |
| googlefindmy.rebuild_registry | Config Entry ID(s) (optional) | Reload integration config entries; without a payload the first configured entry reloads, or target specific IDs by passing one or many entry_id values. |
- Device coverage: Phones, tablets, Wear OS devices, earbuds, and compatible Bluetooth trackers surfaced in the Google Find My Device network. Any device that appears in the official Google Find My interface is eligible to be imported.
- Entities created: Each tracked device exposes a
device_trackerentity for live location, a binary sensor for connection state, a "last seen" timestamp sensor, and a Plus Code (Open Location Code) sensor. Optional helper entities (statistics, sound trigger button) are added depending on options and device capabilities, and a BLE battery sensor is added when the device is matched to a local Bermuda BLE tracker that reports battery. - Action support: Sound playback is available on hardware that exposes the native "Play sound" action within Google's ecosystem. The integration hides the button on devices that do not advertise support, aligning with Home Assistant action documentation.
- Snapshot merge semantics: Push updates from the FCM listener are merged into the coordinator snapshot rather than replacing it. Devices that were not part of a push event keep their previous metadata, which prevents transient gaps in the device tracker registry between full poll cycles. The subentry-device index is rebuilt from the merged snapshot.
- Coordinator-driven updates: Location and metadata are refreshed through Home Assistant's
DataUpdateCoordinatorwith a default 300-second polling interval. Staggered per-device delays keep API calls within Google's rate limits. - Manual refresh: Call the
googlefindmy.locate_deviceaction to request fresh data outside the scheduled polling cycle. The integration debounces requests to avoid repeated queries that would exceed the appropriate polling guidance. - Repair flows: When authentication expires or Google invalidates API tokens, the integration raises a Home Assistant repair issue that guides you through reauthentication without removing the config entry.
Only older FMDN trackers exercise a pure-Python elliptic-curve path
(SECP160r1) when computing rotating EIDs. Modern trackers use P-256, which is
already C-backed (cryptography), so they are unaffected. For the legacy path,
python-ecdsa automatically uses gmpy2 (preferred) or gmpy for its modular
arithmetic if either is importable, with no configuration. If neither is
present, it falls back to pure Python.
This is purely optional and the effect is small. After the polling/refresh optimizations in #1140 the legacy path runs rarely, and the measured speedup is modest: roughly ~1.15x on x86_64 for this small curve (not the "~3x" the library advertises for large operands). On weaker ARM CPUs it may be somewhat higher, but this is not quantified.
You can verify which backend would be used (and which accelerator versions are
installed) from the integration's diagnostics download (crypto.ecdsa_acceleration
and crypto.gmpy2_version) or from a one-time DEBUG log line at startup. Note
that this reports import availability and installed version, not guaranteed
runtime acceleration: a broken gmpy2 install would still appear installed
while python-ecdsa silently falls back to pure Python.
| Platform | gmpy2 wheel? | Effect on legacy path |
|---|---|---|
| x86_64 / aarch64 (glibc) | yes | small speedup (~1.15x x86_64), used automatically |
| aarch64 (musl) | yes (>= 2.3.1) | small speedup, used automatically |
| armv6 / armv7 (32-bit) | no | not installable, no effect |
On 32-bit ARM (armv6/armv7) there is no gmpy2 wheel and a source build
fails, so it cannot be used there. Because the effect is already small after the
#1140 optimizations and only touches legacy trackers, installing gmpy2 is a
minor, optional tweak rather than a recommended step.
The Map View is a self-rendered HTML page served by the integration, so it does
not receive Home Assistant's frontend translations (those cover only entity,
config and service strings). Its labels live in a dedicated catalog,
custom_components/googlefindmy/map_i18n.py,
and are resolved server-side from hass.config.language with an English
fallback. Plus Code stays untranslated on purpose (it is Google's brand name).
To add or adjust a language, edit the MAP_LABELS dict in that module: add a
locale entry with the same key set as en (a unit test enforces that every
locale carries every key with a non-empty value). This catalog is intentionally
separate from strings.json / translations/, so make translation-check,
translation_key_check.py and translation_placeholder_check.py are unaffected
by map-label changes and there is no hassfest schema risk.
- Historical data availability: Map View history is generated locally and depends on the Recorder integration retaining statistics; pruning recorder data will remove historical traces.
- Offline devices: Google only reports the last known location for powered-off or offline hardware. Devices may appear as
unavailableuntil they reconnect to the Find My network. - Authentication tooling: Generating
Auth/secrets.jsoncurrently relies on the external GoogleFindMyTools scripts. Future upstream changes to Google's login flow may require updated tooling before the integration can connect again. - Multiple households: Home Assistant imports all trackers from the authenticated Google account. Fine-grained sharing to limit visibility per household member is not yet available and should be handled via entity permissions.
- Disable or delete related automations, dashboards, and notification flows that reference
googlefindmyentities to prevent "entity not found" errors after removal. - Open Settings → Devices & Services → Integrations → Google Find My Device.
- Use the ⋮ menu → Delete action to remove the config entry. Home Assistant will unload entities and purge the stored token cache.
- If you installed through HACS, remove the integration from HACS to stop future updates. For manual installs, delete
custom_components/googlefindmy/from your Home Assistant configuration directory. - Restart Home Assistant to clear any cached services. If you encounter lingering repairs, resolve them through the Home Assistant Repairs dashboard.
- Trigger a sound alert on misplaced earbuds via the
googlefindmy.play_soundaction when a BLE beacon indicates they are nearby. - Build an automation that notifies you when a tracker enters or leaves a geofenced zone based on the
device_trackerentity state. - Monitor integration health by surfacing the statistics entity in dashboards to verify polling intervals and API latency.
- Combine the Map View history with companion dashboards to visualize multi-day movement patterns for shared family devices.
- Check if devices have moved recently (Find My devices may not update GPS when stationary)
- Check battery levels (low battery may disable GPS reporting)
When you run the standalone helper scripts (get_oauth_token.py,
Auth/auth_flow.py, KeyBackup/shared_key_flow.py) from the command line,
undetected_chromedriver downloads a driver for your installed Chrome version.
If the Chrome-for-Testing stable channel has moved ahead of the Chrome build
offered to your desktop (for example the driver targets Chrome 150 while only
149 is installed), startup can abort with only supports Chrome version 150.
The integration auto-detects the installed version and passes it through, so this usually resolves itself. If detection fails, or you need to pin a specific version, use the layered override (priority: CLI flag > environment variable > auto-detection):
| Override | CLI flag | Environment variable |
|---|---|---|
| Chrome binary path | --chrome-path /path/to/chrome |
GOOGLEFINDMY_CHROME_PATH |
| Chrome major version | --chrome-version 149 |
GOOGLEFINDMY_CHROME_VERSION |
# Pin the major version on the command line
python custom_components/googlefindmy/get_oauth_token.py --chrome-version 149
# Or via environment variables. These also cover the Home Assistant runtime
# path, which has no command line of its own.
export GOOGLEFINDMY_CHROME_VERSION=149
export GOOGLEFINDMY_CHROME_PATH=/usr/bin/google-chromeRun any of the scripts with --help to list the available options.
Location data is fetched outbound from Home Assistant to Google (FCM push plus Nova/SPOT polling); it does not depend on your Home Assistant internal or external URL configuration. If updates stop after upgrading the integration:
- Reload the integration (Settings → Devices & Services → Google Find My Device → ⋮ → Reload) or restart Home Assistant. This re-establishes the FCM connection and refreshes tokens, which resolves most post-upgrade stalls.
- If updates are still missing, review the logs for authentication errors and re-authenticate if prompted (see Authentication Expires Repeatedly).
Note on the internal/external URL: Your Home Assistant internal/external URL only affects the clickable Map View links on each device page, not location reception. Changing it can appear to "fix" updates because it triggers a reload or restart, but it is the restart that restores tracking, not the URL value. A configuration such as internal
http://ip-address:8123plus externalhttps://your-domain:8123is perfectly valid.
- Extended timeout allows up to 60 seconds for device response
- Check firewall settings for Firebase Cloud Messaging
- Review FCM debug logs for connection details
- Ensure port 5228 is forwarded if you run this behind reverse-proxy, inside KVM or any other virtual environment not directly exposed.
- Google may revoke tokens when API requests originate from a different IP address or geographic region than where the token was originally created.
- Common scenario:
secrets.jsongenerated on a laptop at home, but Home Assistant runs on a cloud VPS or a server in another country. - Fix: Run the authentication script on the same network (same public IP) where your Home Assistant instance is located, then re-import the credentials.
The integration respects Google's rate limits by:
- Sequential device polling (one device at a time)
- Configurable delays between requests
- Minimum poll interval enforcement
- Home Assistant shows this error when the config flow fails to register. Double-check that
custom_components/googlefindmy/manifest.jsonsets"domain": "googlefindmy"and"config_flow": true. - Inspect
custom_components/googlefindmy/config_flow.pyto ensure theConfigFlowclass inherits fromconfig_entries.ConfigFlowand declares the domain viaclass ConfigFlow(..., domain=DOMAIN)(ordomain = DOMAIN). - Enable targeted debug logging while reproducing the issue to confirm the handler lifecycle:
You can apply the same levels temporarily via Settings → System → Logs → Configure or by calling the
logger: default: info logs: homeassistant.config_entries: debug homeassistant.data_entry_flow: debug homeassistant.loader: debug homeassistant.setup: debug custom_components.googlefindmy: debug
logger.set_levelservice. - Review the Home Assistant logs for the integration's import-time entry (
ConfigFlow import OK; class=ConfigFlow, class.domain=googlefindmy, const.DOMAIN=googlefindmy, class_id=...) followed by the registry verification messages to ensure the handler is present inHANDLERS. - Run
pytest tests/test_config_flow_basic.py -qto exercise the smoke tests that validate the handler registration and user-step initialization before retrying the flow. - Automatic retry with exponential backoff
Corporate proxies that intercept HTTPS often replace the default certificate
authority chain, which breaks tools such as pip-audit. Use
python script/bootstrap_truststore.py to merge your organization's CA bundle
with the upstream certifi trust store
and (optionally) generate a pip.conf that points at an internal PyPI mirror.
- Collect your proxy or internal PKI certificate in PEM format and save it as
company-ca.pemin the repository root. - Run
python script/bootstrap_truststore.py --ca-file company-ca.pem --emit-exports. The helper creates.truststore/ca-bundle.pemand prints the environment overrides required by bothpipandpip-audit. - Export the recommended variables in the shell that will run security checks:
export REQUESTS_CA_BUNDLE="$(pwd)/.truststore/ca-bundle.pem" export PIP_CERT="$(pwd)/.truststore/ca-bundle.pem"
- (Optional) Provide an internal package index while generating the trust
store, for example:
python script/bootstrap_truststore.py \ --ca-file company-ca.pem \ --pip-config .truststore/pip.conf \ --index-url https://pypi.internal.example/simple \ --emit-exports export PIP_CONFIG_FILE="$(pwd)/.truststore/pip.conf" - Invoke
pip-auditusing the normal repository instructions. The tool now trusts the injected certificates and can reach either the public index or your internal mirror without disabling TLS verification.
The generated artifacts remain in .truststore/ so developers can refresh
them whenever certificates rotate without committing secrets to version
control. The helper always creates this directory in the repository root, and
the .gitignore entry ensures the resulting bundle, optional pip.conf, and
any exported environment snippets never land in commits. It is safe to delete
the folder between runs; a subsequent invocation of
script/bootstrap_truststore.py recreates it with the latest certificates and
configuration.
- When Google's Nova endpoint returns 401, the integration now clears both the entry-scoped and global ADM token cache entries before refreshing. This ensures a brand-new token is minted and stored automatically, without requiring you to restart Home Assistant or re-run the configuration flow.
- The regeneration also refreshes the associated metadata so subsequent requests resume with the updated token immediately.
- All location data uses Google's end-to-end encryption
- Authentication tokens are securely cached
- No location data is transmitted to third parties
- Local processing of all GPS coordinates
Contributions are welcome and encouraged!
To contribute, please:
- Fork the repository
- Create a feature branch
- Install the development dependencies with
make install-dev(orpoetry install --with dev,test) - Install the development hooks with
pre-commit installand ensurepre-commit run --all-filespasses before submitting changes. If the CLI entry points are unavailable, use thepython -mfallbacks from the module invocation primer to run the same commands reliably. - Run
python script/local_verify.pyto execute the requiredruff format --checkandpytest -qcommands together (or invokepython script/precommit_hooks/ruff_format.py --check ...andpytest -qmanually if you need custom arguments). - When running pytest (either through the helper script or directly) fix any failures and address every
DeprecationWarningyou encounter—rerun withPYTHONWARNINGS=error::DeprecationWarning pytest -qif you need help spotting new warnings. - Test thoroughly with your Find My devices
- Submit a pull request with detailed description
For quick sanity checks during development, run the lint and type checks after installing dev dependencies:
make install-dev
poetry run ruff check .
poetry run mypy --strict- Update the version in both
custom_components/googlefindmy/manifest.jsonandcustom_components/googlefindmy/const.py(INTEGRATION_VERSION) at the same time so the manifest metadata and runtime constants remain in sync. - Run the full verification suite (
ruff format --check, targeted pytest modules, andpytest -q) before tagging a release to confirm the version bump did not introduce regressions.
Manifest validation (hassfest) now runs exclusively through the
hassfest-auto-fix workflow. Every
push to main and every pull request automatically executes the
home-assistant/actions/hassfest
GitHub Action, which rewrites manifests when needed and re-runs the validator to
confirm the fixes.
When you need to inspect or download the results locally:
- Open the relevant workflow run from the PR or commit.
- Expand the Run hassfest (may rewrite manifest) step to review the console output, or download the generated artifact directly from the workflow UI.
- If you need a fresh validation pass, trigger the workflow manually from the Run workflow button in the Actions tab or by re-running the job on the PR.
Several modules still expose lightweight CLI entry points (for example the device
listing helper and the standalone "Play/Stop Sound" examples). These scripts now
require you to target a specific Home Assistant config entry whenever more than
one token cache is available. Set the environment variable
GOOGLEFINDMY_ENTRY_ID to the desired config entry ID before running the CLI, or
pass a cache= override when instantiating the legacy FcmReceiver shim. If you
omit the entry ID while multiple caches are active the CLI will abort with a
message listing the available IDs so you can pick the right account.
- Böttger, L. (2024). GoogleFindMyTools [Computer software]. https://github.com/leonboe1/GoogleFindMyTools
- Firebase Cloud Messaging integration. https://github.com/home-assistant/mobile-apps-fcm-push
- @txitxo0 for his amazing work on the MQTT based tool that I used to help kickstart this project!
- Open Location Code (Plus Code) encoder, (c) Google (Apache-2.0), vendored from
google/open-location-code,
commit
dcff1534f70a0d7244d0d1c357c20f0aa28ab355; modified: encode-only path. Seecustom_components/googlefindmy/vendor/openlocationcode/LICENSE.
- @DominicWindisch
- @suka97
- @jleinenbach
This integration is not affiliated with Google. Use at your own risk and in compliance with Google's Terms of Service. The developers are not responsible for any misuse or issues arising from the use of this integration.