Home Assistant Unofficial Reference 2024.12.1
device_tracker.py
Go to the documentation of this file.
1 """Support for tracking for iCloud devices."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from homeassistant.components.device_tracker import TrackerEntity
8 from homeassistant.config_entries import ConfigEntry
9 from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
10 from homeassistant.helpers.device_registry import DeviceInfo
11 from homeassistant.helpers.dispatcher import async_dispatcher_connect
12 from homeassistant.helpers.entity_platform import AddEntitiesCallback
13 
14 from .account import IcloudAccount, IcloudDevice
15 from .const import (
16  DEVICE_LOCATION_HORIZONTAL_ACCURACY,
17  DEVICE_LOCATION_LATITUDE,
18  DEVICE_LOCATION_LONGITUDE,
19  DOMAIN,
20 )
21 
22 
24  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
25 ) -> None:
26  """Set up device tracker for iCloud component."""
27  account: IcloudAccount = hass.data[DOMAIN][entry.unique_id]
28  tracked = set[str]()
29 
30  @callback
31  def update_account():
32  """Update the values of the account."""
33  add_entities(account, async_add_entities, tracked)
34 
35  account.listeners.append(
36  async_dispatcher_connect(hass, account.signal_device_new, update_account)
37  )
38 
39  update_account()
40 
41 
42 @callback
43 def add_entities(account: IcloudAccount, async_add_entities, tracked):
44  """Add new tracker entities from the account."""
45  new_tracked = []
46 
47  for dev_id, device in account.devices.items():
48  if dev_id in tracked or device.location is None:
49  continue
50 
51  new_tracked.append(IcloudTrackerEntity(account, device))
52  tracked.add(dev_id)
53 
54  async_add_entities(new_tracked, True)
55 
56 
57 class IcloudTrackerEntity(TrackerEntity):
58  """Represent a tracked device."""
59 
60  _attr_has_entity_name = True
61  _attr_name = None
62 
63  def __init__(self, account: IcloudAccount, device: IcloudDevice) -> None:
64  """Set up the iCloud tracker entity."""
65  self._account_account = account
66  self._device_device = device
67  self._unsub_dispatcher_unsub_dispatcher: CALLBACK_TYPE | None = None
68  self._attr_unique_id_attr_unique_id = device.unique_id
69 
70  @property
71  def location_accuracy(self):
72  """Return the location accuracy of the device."""
73  return self._device_device.location[DEVICE_LOCATION_HORIZONTAL_ACCURACY]
74 
75  @property
76  def latitude(self):
77  """Return latitude value of the device."""
78  return self._device_device.location[DEVICE_LOCATION_LATITUDE]
79 
80  @property
81  def longitude(self):
82  """Return longitude value of the device."""
83  return self._device_device.location[DEVICE_LOCATION_LONGITUDE]
84 
85  @property
86  def battery_level(self) -> int | None:
87  """Return the battery level of the device."""
88  return self._device_device.battery_level
89 
90  @property
91  def icon(self) -> str:
92  """Return the icon."""
93  return icon_for_icloud_device(self._device_device)
94 
95  @property
96  def extra_state_attributes(self) -> dict[str, Any]:
97  """Return the device state attributes."""
98  return self._device_device.extra_state_attributes
99 
100  @property
101  def device_info(self) -> DeviceInfo:
102  """Return the device information."""
103  return DeviceInfo(
104  configuration_url="https://icloud.com/",
105  identifiers={(DOMAIN, self._device_device.unique_id)},
106  manufacturer="Apple",
107  model=self._device_device.device_model,
108  name=self._device_device.name,
109  )
110 
111  async def async_added_to_hass(self) -> None:
112  """Register state update callback."""
114  self.hass, self._account_account.signal_device_update, self.async_write_ha_state
115  )
116 
117  async def async_will_remove_from_hass(self) -> None:
118  """Clean up after entity before removal."""
119  if self._unsub_dispatcher_unsub_dispatcher:
120  self._unsub_dispatcher_unsub_dispatcher()
121 
122 
123 def icon_for_icloud_device(icloud_device: IcloudDevice) -> str:
124  """Return an icon for the device."""
125  switcher = {
126  "iPad": "mdi:tablet",
127  "iPhone": "mdi:cellphone",
128  "iPod": "mdi:ipod",
129  "iMac": "mdi:monitor",
130  "MacBookPro": "mdi:laptop",
131  }
132 
133  return switcher.get(icloud_device.device_class, "mdi:cellphone-link")
None __init__(self, IcloudAccount account, IcloudDevice device)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
def add_entities(IcloudAccount account, async_add_entities, tracked)
str icon_for_icloud_device(IcloudDevice icloud_device)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103