text: """Platform that supports scanning iCloud."""
  import logging
  import random
  import os
  
  import voluptuous as vol
  
  from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
  from homeassistant.components.device_tracker import PLATFORM_SCHEMA
  from homeassistant.components.device_tracker.const import (
      DOMAIN,
      ATTR_ATTRIBUTES,
      ENTITY_ID_FORMAT,
  )
  from homeassistant.components.device_tracker.legacy import DeviceScanner
  from homeassistant.components.zone import async_active_zone
  from homeassistant.helpers.event import track_utc_time_change
  import homeassistant.helpers.config_validation as cv
  from homeassistant.util import slugify
  import homeassistant.util.dt as dt_util
  from homeassistant.util.location import distance
  from homeassistant.util.async_ import run_callback_threadsafe
  
  _LOGGER = logging.getLogger(__name__)
  
  CONF_ACCOUNTNAME = "account_name"
  CONF_MAX_INTERVAL = "max_interval"
  CONF_GPS_ACCURACY_THRESHOLD = "gps_accuracy_threshold"
  
  # entity attributes
  ATTR_ACCOUNTNAME = "account_name"
  ATTR_INTERVAL = "interval"
  ATTR_DEVICENAME = "device_name"
  ATTR_BATTERY = "battery"
  ATTR_DISTANCE = "distance"
  ATTR_DEVICESTATUS = "device_status"
  ATTR_LOWPOWERMODE = "low_power_mode"
  ATTR_BATTERYSTATUS = "battery_status"
  
  ICLOUDTRACKERS = {}
  
  _CONFIGURING = {}
  
  DEVICESTATUSSET = [
      "features",
      "maxMsgChar",
      "darkWake",
      "fmlyShare",
      "deviceStatus",
      "remoteLock",
      "activationLocked",
      "deviceClass",
      "id",
      "deviceModel",
      "rawDeviceModel",
      "passcodeLength",
      "canWipeAfterLock",
      "trackingInfo",
      "location",
      "msg",
      "batteryLevel",
      "remoteWipe",
      "thisDevice",
      "snd",
      "prsId",
      "wipeInProgress",
      "lowPowerMode",
      "lostModeEnabled",
      "isLocating",
      "lostModeCapable",
      "mesg",
      "name",
      "batteryStatus",
      "lockedTimestamp",
      "lostTimestamp",
      "locationCapable",
      "deviceDisplayName",
      "lostDevice",
      "deviceColor",
      "wipedTimestamp",
      "modelDisplayName",
      "locationEnabled",
      "isMac",
      "locFoundEnabled",
  ]
  
  DEVICESTATUSCODES = {
      "200": "online",
      "201": "offline",
      "203": "pending",
      "204": "unregistered",
  }
  
  SERVICE_SCHEMA = vol.Schema(
      {
          vol.Optional(ATTR_ACCOUNTNAME): vol.All(cv.ensure_list, [cv.slugify]),
          vol.Optional(ATTR_DEVICENAME): cv.slugify,
          vol.Optional(ATTR_INTERVAL): cv.positive_int,
      }
  )
  
  PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
      {
          vol.Required(CONF_USERNAME): cv.string,
          vol.Required(CONF_PASSWORD): cv.string,
          vol.Optional(ATTR_ACCOUNTNAME): cv.slugify,
          vol.Optional(CONF_MAX_INTERVAL, default=30): cv.positive_int,
          vol.Optional(CONF_GPS_ACCURACY_THRESHOLD, default=1000): cv.positive_int,
      }
  )
  
  
  def setup_scanner(hass, config: dict, see, discovery_info=None):
      """Set up the iCloud Scanner."""
      username = config.get(CONF_USERNAME)
      password = config.get(CONF_PASSWORD)
      account = config.get(CONF_ACCOUNTNAME, slugify(username.partition("@")[0]))
      max_interval = config.get(CONF_MAX_INTERVAL)
      gps_accuracy_threshold = config.get(CONF_GPS_ACCURACY_THRESHOLD)
  
      icloudaccount = Icloud(
          hass, username, password, account, max_interval, gps_accuracy_threshold, see
      )
  
      if icloudaccount.api is not None:
          ICLOUDTRACKERS[account] = icloudaccount
  
      else:
          _LOGGER.error("No ICLOUDTRACKERS added")
          return False
  
      def lost_iphone(call):
          """Call the lost iPhone function if the device is found."""
          accounts = call.data.get(ATTR_ACCOUNTNAME, ICLOUDTRACKERS)
          devicename = call.data.get(ATTR_DEVICENAME)
          for account in accounts:
              if account in ICLOUDTRACKERS:
                  ICLOUDTRACKERS[account].lost_iphone(devicename)
  
      hass.services.register(
          DOMAIN, "icloud_lost_iphone", lost_iphone, schema=SERVICE_SCHEMA
      )
  
      def update_icloud(call):
          """Call the update function of an iCloud account."""
          accounts = call.data.get(ATTR_ACCOUNTNAME, ICLOUDTRACKERS)
          devicename = call.data.get(ATTR_DEVICENAME)
          for account in accounts:
              if account in ICLOUDTRACKERS:
                  ICLOUDTRACKERS[account].update_icloud(devicename)
  
      hass.services.register(
          DOMAIN, "icloud_update", update_icloud, schema=SERVICE_SCHEMA
      )
  
      def reset_account_icloud(call):
          """Reset an iCloud account."""
          accounts = call.data.get(ATTR_ACCOUNTNAME, ICLOUDTRACKERS)
          for account in accounts:
              if account in ICLOUDTRACKERS:
                  ICLOUDTRACKERS[account].reset_account_icloud()
  
      hass.services.register(
          DOMAIN, "icloud_reset_account", reset_account_icloud, schema=SERVICE_SCHEMA
      )
  
      def setinterval(call):
          """Call the update function of an iCloud account."""
          accounts = call.data.get(ATTR_ACCOUNTNAME, ICLOUDTRACKERS)
          interval = call.data.get(ATTR_INTERVAL)
          devicename = call.data.get(ATTR_DEVICENAME)
          for account in accounts:
              if account in ICLOUDTRACKERS:
                  ICLOUDTRACKERS[account].setinterval(interval, devicename)
  
      hass.services.register(
          DOMAIN, "icloud_set_interval", setinterval, schema=SERVICE_SCHEMA
      )
  
      # Tells the bootstrapper that the component was successfully initialized
      return True
  
  
  class Icloud(DeviceScanner):
      """Representation of an iCloud account."""
  
      def __init__(
          self, hass, username, password, name, max_interval, gps_accuracy_threshold, see
      ):
          """Initialize an iCloud account."""
          self.hass = hass
          self.username = username
          self.password = password
          self.api = None
          self.accountname = name
          self.devices = {}
          self.seen_devices = {}
          self._overridestates = {}
          self._intervals = {}
          self._max_interval = max_interval
          self._gps_accuracy_threshold = gps_accuracy_threshold
          self.see = see
  
          self._trusted_device = None
          self._verification_code = None
  
          self._attrs = {}
          self._attrs[ATTR_ACCOUNTNAME] = name
  
          self.reset_account_icloud()
  
          randomseconds = random.randint(10, 59)
          track_utc_time_change(self.hass, self.keep_alive, second=randomseconds)
  
      def reset_account_icloud(self):
          """Reset an iCloud account."""
          from pyicloud import PyiCloudService
          from pyicloud.exceptions import (
              PyiCloudFailedLoginException,
              PyiCloudNoDevicesException,
          )
  
          icloud_dir = self.hass.config.path("icloud")
          if not os.path.exists(icloud_dir):
              os.makedirs(icloud_dir)
  
          try:
              self.api = PyiCloudService(
                  self.username, self.password, cookie_directory=icloud_dir, verify=True
              )
          except PyiCloudFailedLoginException as error:
              self.api = None
              _LOGGER.error("Error logging into iCloud Service: %s", error)
              return
  
          try:
              self.devices = {}
              self._overridestates = {}
              self._intervals = {}
              for device in self.api.devices:
                  status = device.status(DEVICESTATUSSET)
                  _LOGGER.debug("Device Status is %s", status)
                  devicename = slugify(status["name"].replace(" ", "", 99))
                  _LOGGER.info("Adding icloud device: %s", devicename)
                  if devicename in self.devices:
                      _LOGGER.error("Multiple devices with name: %s", devicename)
                      continue
                  self.devices[devicename] = device
                  self._intervals[devicename] = 1
                  self._overridestates[devicename] = None
          except PyiCloudNoDevicesException:
              _LOGGER.error("No iCloud Devices found!")
  
      def icloud_trusted_device_callback(self, callback_data):
          """Handle chosen trusted devices."""
          self._trusted_device = int(callback_data.get("trusted_device"))
          self._trusted_device = self.api.trusted_devices[self._trusted_device]
  
          if not self.api.send_verification_code(self._trusted_device):
              _LOGGER.error("Failed to send verification code")
              self._trusted_device = None
              return
  
          if self.accountname in _CONFIGURING:
              request_id = _CONFIGURING.pop(self.accountname)
              configurator = self.hass.components.configurator
              configurator.request_done(request_id)
  
          # Trigger the next step immediately
          self.icloud_need_verification_code()
  
      def icloud_need_trusted_device(self):
          """We need a trusted device."""
          configurator = self.hass.components.configurator
          if self.accountname in _CONFIGURING:
              return
  
          devicesstring = ""
          devices = self.api.trusted_devices
          for i, device in enumerate(devices):
              devicename = device.get(
                  "deviceName", "SMS to %s" % device.get("phoneNumber")
              )
              devicesstring += f"{i}: {devicename};"
  
          _CONFIGURING[self.accountname] = configurator.request_config(
              f"iCloud {self.accountname}",
              self.icloud_trusted_device_callback,
              description=(
                  "Please choose your trusted device by entering"
                  " the index from this list: " + devicesstring
              ),
              entity_picture="/static/images/config_icloud.png",
              submit_caption="Confirm",
              fields=[{"id": "trusted_device", "name": "Trusted Device"}],
          )
  
      def icloud_verification_callback(self, callback_data):
          """Handle the chosen trusted device."""
          from pyicloud.exceptions import PyiCloudException
  
          self._verification_code = callback_data.get("code")
  
          try:
              if not self.api.validate_verification_code(
                  self._trusted_device, self._verification_code
              ):
                  raise PyiCloudException("Unknown failure")
          except PyiCloudException as error:
              # Reset to the initial 2FA state to allow the user to retry
              _LOGGER.error("Failed to verify verification code: %s", error)
              self._trusted_device = None
              self._verification_code = None
  
              # Trigger the next step immediately
              self.icloud_need_trusted_device()
  
          if self.accountname in _CONFIGURING:
              request_id = _CONFIGURING.pop(self.accountname)
              configurator = self.hass.components.configurator
              configurator.request_done(request_id)
  
      def icloud_need_verification_code(self):
          """Return the verification code."""
          configurator = self.hass.components.configurator
          if self.accountname in _CONFIGURING:
              return
  
          _CONFIGURING[self.accountname] = configurator.request_config(
              f"iCloud {self.accountname}",
              self.icloud_verification_callback,
              description=("Please enter the validation code:"),
              entity_picture="/static/images/config_icloud.png",
              submit_caption="Confirm",
              fields=[{"id": "code", "name": "code"}],
          )
  
      def keep_alive(self, now):
          """Keep the API alive."""
          if self.api is None:
              self.reset_account_icloud()
  
          if self.api is None:
              return
  
          if self.api.requires_2fa:
              from pyicloud.exceptions import PyiCloudException
  
              try:
                  if self._trusted_device is None:
                      self.icloud_need_trusted_device()
                      return
  
                  if self._verification_code is None:
                      self.icloud_need_verification_code()
                      return
  
                  self.api.authenticate()
                  if self.api.requires_2fa:
                      raise Exception("Unknown failure")
  
                  self._trusted_device = None
                  self._verification_code = None
              except PyiCloudException as error:
                  _LOGGER.error("Error setting up 2FA: %s", error)
          else:
              self.api.authenticate()
  
          currentminutes = dt_util.now().hour * 60 + dt_util.now().minute
          try:
              for devicename in self.devices:
                  interval = self._intervals.get(devicename, 1)
                  if (currentminutes % interval == 0) or (
                      interval > 10 and currentminutes % interval in [2, 4]
                  ):
                      self.update_device(devicename)
          except ValueError:
              _LOGGER.debug("iCloud API returned an error")
  
      def determine_interval(self, devicename, latitude, longitude, battery):
          """Calculate new interval."""
          currentzone = run_callback_threadsafe(
              self.hass.loop, async_active_zone, self.hass, latitude, longitude
          ).result()
  
          if (
              currentzone is not None
              and currentzone == self._overridestates.get(devicename)
          ) or (currentzone is None and self._overridestates.get(devicename) == "away"):
              return
  
          zones = (
              self.hass.states.get(entity_id)
              for entity_id in sorted(self.hass.states.entity_ids("zone"))
          )
  
          distances = []
          for zone_state in zones:
              zone_state_lat = zone_state.attributes["latitude"]
              zone_state_long = zone_state.attributes["longitude"]
              zone_distance = distance(
                  latitude, longitude, zone_state_lat, zone_state_long
              )
              distances.append(round(zone_distance / 1000, 1))
  
          if distances:
              mindistance = min(distances)
          else:
              mindistance = None
  
          self._overridestates[devicename] = None
  
          if currentzone is not None:
              self._intervals[devicename] = self._max_interval
              return
  
          if mindistance is None:
              return
  
          # Calculate out how long it would take for the device to drive to the
          # nearest zone at 120 km/h:
          interval = round(mindistance / 2, 0)
  
          # Never poll more than once per minute
          interval = max(interval, 1)
  
          if interval > 180:
              # Three hour drive?  This is far enough that they might be flying
              interval = 30
  
          if battery is not None and battery <= 33 and mindistance > 3:
              # Low battery - let's check half as often
              interval = interval * 2
  
          self._intervals[devicename] = interval
  
      def update_device(self, devicename):
          """Update the device_tracker entity."""
          from pyicloud.exceptions import PyiCloudNoDevicesException
  
          # An entity will not be created by see() when track=false in
          # 'known_devices.yaml', but we need to see() it at least once
          entity = self.hass.states.get(ENTITY_ID_FORMAT.format(devicename))
          if entity is None and devicename in self.seen_devices:
              return
          attrs = {}
          kwargs = {}
  
          if self.api is None:
              return
  
          try:
              for device in self.api.devices:
                  if str(device) != str(self.devices[devicename]):
                      continue
  
                  status = device.status(DEVICESTATUSSET)
                  _LOGGER.debug("Device Status is %s", status)
                  dev_id = status["name"].replace(" ", "", 99)
                  dev_id = slugify(dev_id)
                  attrs[ATTR_DEVICESTATUS] = DEVICESTATUSCODES.get(
                      status["deviceStatus"], "error"
                  )
                  attrs[ATTR_LOWPOWERMODE] = status["lowPowerMode"]
                  attrs[ATTR_BATTERYSTATUS] = status["batteryStatus"]
                  attrs[ATTR_ACCOUNTNAME] = self.accountname
                  status = device.status(DEVICESTATUSSET)
                  battery = status.get("batteryLevel", 0) * 100
                  location = status["location"]
                  if location and location["horizontalAccuracy"]:
                      horizontal_accuracy = int(location["horizontalAccuracy"])
                      if horizontal_accuracy < self._gps_accuracy_threshold:
                          self.determine_interval(
                              devicename,
                              location["latitude"],
                              location["longitude"],
                              battery,
                          )
                          interval = self._intervals.get(devicename, 1)
                          attrs[ATTR_INTERVAL] = interval
                          accuracy = location["horizontalAccuracy"]
                          kwargs["dev_id"] = dev_id
                          kwargs["host_name"] = status["name"]
                          kwargs["gps"] = (location["latitude"], location["longitude"])
                          kwargs["battery"] = battery
                          kwargs["gps_accuracy"] = accuracy
                          kwargs[ATTR_ATTRIBUTES] = attrs
                          self.see(**kwargs)
                          self.seen_devices[devicename] = True
          except PyiCloudNoDevicesException:
              _LOGGER.error("No iCloud Devices found")
  
      def lost_iphone(self, devicename):
          """Call the lost iPhone function if the device is found."""
          if self.api is None:
              return
  
          self.api.authenticate()
          for device in self.api.devices:
              if str(device) == str(self.devices[devicename]):
                  _LOGGER.info("Playing Lost iPhone sound for %s", devicename)
                  device.play_sound()
  
      def update_icloud(self, devicename=None):
          """Request device information from iCloud and update device_tracker."""
          from pyicloud.exceptions import PyiCloudNoDevicesException
  
          if self.api is None:
              return
  
          try:
              if devicename is not None:
                  if devicename in self.devices:
                      self.update_device(devicename)
                  else:
                      _LOGGER.error(
                          "devicename %s unknown for account %s",
                          devicename,
                          self._attrs[ATTR_ACCOUNTNAME],
                      )
              else:
                  for device in self.devices:
                      self.update_device(device)
          except PyiCloudNoDevicesException:
              _LOGGER.error("No iCloud Devices found")
  
      def setinterval(self, interval=None, devicename=None):
          """Set the interval of the given devices."""
          devs = [devicename] if devicename else self.devices
          for device in devs:
              devid = f"{DOMAIN}.{device}"
              devicestate = self.hass.states.get(devid)
              if interval is not None:
                  if devicestate is not None:
                      self._overridestates[device] = run_callback_threadsafe(
                          self.hass.loop,
                          async_active_zone,
                          self.hass,
                          float(devicestate.attributes.get("latitude", 0)),
                          float(devicestate.attributes.get("longitude", 0)),
                      ).result()
                      if self._overridestates[device] is None:
                          self._overridestates[device] = "away"
                  self._intervals[device] = interval
              else:
                  self._overridestates[device] = None
              self.update_device(device)
  