def authenticate(username, password):
    print("Signing in...")

    if password:
      icloud = PyiCloudService(username, password)
    else:
      icloud = PyiCloudService(username)

    if icloud.requires_2fa:
        print "Two-factor authentication required. Your trusted devices are:"

        devices = icloud.trusted_devices
        for i, device in enumerate(devices):
            print "  %s: %s" % (i, device.get('deviceName',
                "SMS to %s" % device.get('phoneNumber')))

        device = click.prompt('Which device would you like to use?', default=0)
        device = devices[device]
        if not icloud.send_verification_code(device):
            print "Failed to send verification code"
            sys.exit(1)

        code = click.prompt('Please enter validation code')
        if not icloud.validate_verification_code(device, code):
            print "Failed to verify verification code"
            sys.exit(1)

    return icloud
Beispiel #2
0
    def _login(self):
        self._api = PyiCloudService(self.username, self.password)

        if self._api.requires_2sa:
            print(
                "Two-factor authentication required. Your trusted devices are:"
            )
            devices = self._api.trusted_devices
            for i, device in enumerate(devices):
                print("  %s: %s" % (
                    i,
                    device.get("deviceName", "SMS to %s") %
                    device.get("phoneNumber"),
                ))

            device = click.prompt("Which device would you like to use?",
                                  default=0)
            device = devices[device]
            if not self._api.send_verification_code(device):
                print("Failed to send verification code")
                sys.exit(1)

            code = click.prompt("Please enter validation code")
            if not self._api.validate_verification_code(device, code):
                print("Failed to verify verification code")
                sys.exit(1)
        print("Logged into iCloud Drive as %s" % self.username)
Beispiel #3
0
    def __init__(self):
        creds = self.__load_creds(os.getcwd() + "/Secrets.txt")
        self.api = PyiCloudService(creds[0], creds[1])
        self.__two_factor_routine()
        self.phone = self.Phone()
        self.update_phone()

        self.lats, self.longs = self.__load_location()
def authenticate(username, password):
    if password:
      icloud = PyiCloudService(username, password)
    else:
      icloud = PyiCloudService(username)


    return icloud
Beispiel #5
0
def setup_icloud():
    #
    name = raw_input("Enter the name: ")
    username = raw_input("Username: "******"Password: "******"Two-factor authentication required. Your trusted devices are:")

            devices = api.trusted_devices
            for i, device in enumerate(devices):
                print "  %s: %s" % (i, device.get('deviceName',
                                                  "SMS to %s" % device.get('phoneNumber')))

            device = click.prompt('Which device would you like to use?', default=0)
            device = devices[device]
            if not api.send_verification_code(device):
                print("Failed to send verification code")
                sys.exit(1)

            code = click.prompt('Please enter validation code')
            if not api.validate_verification_code(device, code):
                print("Failed to verify verification code")
                sys.exit(1)
            #
            twofactor_choice = device
            twofactor_timestamp = datetime.datetime.now().strftime(_dateformat)
            #
        #
    except PyiCloudFailedLoginException:
        print("\n!! Username/password combination is incorrect - please try again !!")
        print("\n****************************************************************\n")
        return
    except:
        print("\n!! An error has ocurred - please try again !!")
        print("\n****************************************************************\n")
        return
    #
    new_acc = {}
    new_acc['account_type'] = 'icloud_account'
    new_acc['account_name'] = name
    new_acc['account_id'] = name.lower().replace(' ', '_').replace('\'', '')
    new_acc['details'] = {'username': username,
                          'password': password,
                          '2factor': {'2factor_required': api.requires_2fa,
                                      '2factor_choice': twofactor_choice,
                                      '2factor_timestamp': twofactor_timestamp}}
    new_acc['details_public'] = {}
    #
    return new_acc
Beispiel #6
0
    def __init__(self, username, password):
        self.connected = False
        self.verification_code_required = False
        self.api = PyiCloudService(username, password)

        if self.api.requires_2sa:
            self.verification_code_required = True
        else:
            self.connected = True
    def setup(self) -> None:
        """Set up an iCloud account."""
        try:
            self.api = PyiCloudService(
                self._username,
                self._password,
                self._icloud_dir.path,
                with_family=self._with_family,
            )
        except PyiCloudFailedLoginException:
            self.api = None
            # Login failed which means credentials need to be updated.
            _LOGGER.error(
                ("Your password for '%s' is no longer working. Go to the "
                 "Integrations menu and click on Configure on the discovered Apple "
                 "iCloud card to login again."),
                self._config_entry.data[CONF_USERNAME],
            )

            self.hass.add_job(
                self.hass.config_entries.flow.async_init(
                    DOMAIN,
                    context={"source": SOURCE_REAUTH},
                    data={
                        **self._config_entry.data,
                        "unique_id":
                        self._config_entry.unique_id,
                    },
                ))
            return

        try:
            api_devices = self.api.devices
            # Gets device owners infos
            user_info = api_devices.response["userInfo"]
        except (
                PyiCloudServiceNotActivatedException,
                PyiCloudNoDevicesException,
        ) as err:
            _LOGGER.error("No iCloud device found")
            raise ConfigEntryNotReady from err

        self._owner_fullname = f"{user_info['firstName']} {user_info['lastName']}"

        self._family_members_fullname = {}
        if user_info.get("membersInfo") is not None:
            for prs_id, member in user_info["membersInfo"].items():
                self._family_members_fullname[
                    prs_id] = f"{member['firstName']} {member['lastName']}"

        self._devices = {}
        self.update_devices()
Beispiel #8
0
    def setup(self) -> None:
        """Set up an iCloud account."""
        try:
            self.api = PyiCloudService(
                self._username,
                self._password,
                self._icloud_dir.path,
                with_family=self._with_family,
            )

            if self.api.requires_2fa:
                # Trigger a new log in to ensure the user enters the 2FA code again.
                raise PyiCloudFailedLoginException

        except PyiCloudFailedLoginException:
            self.api = None
            # Login failed which means credentials need to be updated.
            _LOGGER.error(
                (
                    "Your password for '%s' is no longer working; Go to the "
                    "Integrations menu and click on Configure on the discovered Apple "
                    "iCloud card to login again"
                ),
                self._config_entry.data[CONF_USERNAME],
            )

            self._require_reauth()
            return

        try:
            api_devices = self.api.devices
            # Gets device owners infos
            user_info = api_devices.response["userInfo"]
        except (
            PyiCloudServiceNotActivatedException,
            PyiCloudNoDevicesException,
        ) as err:
            _LOGGER.error("No iCloud device found")
            raise ConfigEntryNotReady from err

        self._owner_fullname = f"{user_info['firstName']} {user_info['lastName']}"

        self._family_members_fullname = {}
        if user_info.get("membersInfo") is not None:
            for prs_id, member in user_info["membersInfo"].items():
                self._family_members_fullname[
                    prs_id
                ] = f"{member['firstName']} {member['lastName']}"

        self._devices = {}
        self.update_devices()
Beispiel #9
0
def validate_icloud():
    api = PyiCloudService('*****@*****.**', 'REd#rED@1577?!?')
    print("Two-factor authentication required. Your trusted devices are:")
    devices = api.trusted_devices
    for i, device in enumerate(devices):
        print("  %s: %s" % (i,
                            device.get('deviceName', "SMS to %s" %
                                       device.get('phoneNumber'))))

    if not api.send_verification_code(device[0]):
        print("Failed to send verification code")
    code = click.prompt('Please enter validation code')
    if not api.validate_verification_code(device, code):
        print("Failed to verify verification code")
    print("iCloud Validated")
Beispiel #10
0
def find_my_iphone(event, context):

    print(event)

    if event['serialNumber'] == iot_button_decrypted.decode(
            'utf8') and event['clickType'] == 'SINGLE':
        api = PyiCloudService(personB_email_decrypted.decode('utf8'),
                              personB_password_decrypted.decode('utf8'))
        api.devices[1].play_sound()

    elif event['serialNumber'] == iot_button_decrypted.decode(
            'utf8') and event['clickType'] == 'DOUBLE':

        api = PyiCloudService(personA_email_decrypted.decode('utf8'),
                              personA_password_decrypted.decode('utf8'))
        api.devices[1].play_sound()
Beispiel #11
0
def device_selector(phrase: str = None) -> Union[AppleDevice, None]:
    """Selects a device using the received input string.

    See Also:
        - Opens a html table with the index value and name of device.
        - When chosen an index value, the device name will be returned.

    Args:
        phrase: Takes the voice recognized statement as argument.

    Returns:
        AppleDevice:
        Returns the selected device from the class ``AppleDevice``
    """
    if not all([env.icloud_user, env.icloud_pass]):
        logger.warning("ICloud username or password not found.")
        return
    icloud_api = PyiCloudService(env.icloud_user, env.icloud_pass)
    devices = [device for device in icloud_api.devices]
    if not phrase:
        phrase = socket.gethostname().split('.')[0]  # Temporary fix
    devices_str = [{str(device).split(":")[0].strip(): str(device).split(":")[1].strip()} for device in devices]
    closest_match = [
        (SequenceMatcher(a=phrase, b=key).ratio() + SequenceMatcher(a=phrase, b=val).ratio()) / 2
        for device in devices_str for key, val in device.items()
    ]
    index = closest_match.index(max(closest_match))
    return icloud_api.devices[index]
Beispiel #12
0
def provision_geo_data():
    # Connect to iCloud API
    icloud = PyiCloudService(apple_id, icloudpass)

    # Get index of device
    d = icloud.devices.keys().index(device_uuid)

    # Request GPS coordinates of device
    lat = icloud.devices[d].location()['latitude']
    lng = icloud.devices[d].location()['longitude']

    # Mark the time
    tim = strftime('%Y-%m-%dT%H:%M:%S')

    # Request what3words address based on lat, lng
    w3w = What3Words(api_key=w3wapikey)
    res = w3w.words(lat=lat, lng=lng)

    # Flatten w3w response, add domain to make URL
    wordlist = res['words']
    words = '.'.join(wordlist)
    w3wurl = 'http://w3w.co/%s' % words

    geotag = {
        'latitude': lat,
        'longitude': lng,
        'what3words': words,
        'w3w_url': w3wurl,
    }
    geo = {'as_of': tim, 'location': geotag}
    return geo
Beispiel #13
0
def setup_scanner(hass, config, see):
    """ Set up the iCloud Scanner. """
    from pyicloud import PyiCloudService
    from pyicloud.exceptions import PyiCloudFailedLoginException
    from pyicloud.exceptions import PyiCloudNoDevicesException

    # Get the username and password from the configuration
    username = config.get(CONF_USERNAME)
    password = config.get(CONF_PASSWORD)

    if username is None or password is None:
        _LOGGER.error('Must specify a username and password')
        return False

    try:
        _LOGGER.info('Logging into iCloud Account')
        # Attempt the login to iCloud
        api = PyiCloudService(username, password, verify=True)
    except PyiCloudFailedLoginException as error:
        _LOGGER.exception('Error logging into iCloud Service: %s', error)
        return False

    def keep_alive(now):
        """ Keeps authenticating iCloud connection. """
        api.authenticate()
        _LOGGER.info("Authenticate against iCloud")

    track_utc_time_change(hass, keep_alive, second=0)

    def update_icloud(now):
        """ Authenticate against iCloud and scan for devices. """
        try:
            # The session timeouts if we are not using it so we
            # have to re-authenticate. This will send an email.
            api.authenticate()
            # Loop through every device registered with the iCloud account
            for device in api.devices:
                status = device.status()
                location = device.location()
                # If the device has a location add it. If not do nothing
                if location:
                    see(dev_id=re.sub(r"(\s|\W|')", '', status['name']),
                        host_name=status['name'],
                        gps=(location['latitude'], location['longitude']),
                        battery=status['batteryLevel'] * 100,
                        gps_accuracy=location['horizontalAccuracy'])
                else:
                    # No location found for the device so continue
                    continue
        except PyiCloudNoDevicesException:
            _LOGGER.info('No iCloud Devices found!')

    track_utc_time_change(hass,
                          update_icloud,
                          minute=range(
                              0, 60, config.get(CONF_INTERVAL,
                                                DEFAULT_INTERVAL)),
                          second=0)

    return True
Beispiel #14
0
    def check_remote_calendar_for_holiday(self):
        log_file = Text_File_Modifier()
        log_file_path = Config_Settings().custom_config(
            "log_settings", "log_file_path")
        date_today = Calendar_Info()
        config_settings = Config_Settings()

        if date_today.check_if_weekday() is not False:
            date_today = date_today.check_if_weekday()

            # get iCloud username and password from config.secrets file
            icloud_username = config_settings.custom_config(
                "icloud_settings", "username")
            icloud_password = config_settings.custom_config(
                "icloud_settings", "password")
            pyicloud_api = PyiCloudService(icloud_username, icloud_password)

            get_event = pyicloud_api.calendar.events(date_today, date_today)
            try:
                if get_event[0]['title'] == 'Holiday':
                    log_file.write_log_file(
                        "Remote Calendar shows I am on Holiday.",
                        log_file_path)
                    return False  # On holiday
            except:
                log_file.write_log_file(
                    "Today is a Week Day and i am NOT on Holiday therefore I should be at work...",
                    log_file_path)
                return True  # there is no holiday event meaning i am at work
Beispiel #15
0
    async def planning(self, ctx: commands.Context):
        """Gère le planning de marc."""
        if not self.api:
            from pyicloud import PyiCloudService

            self.api = PyiCloudService(os.environ["APPLE_MAIL"],
                                       os.environ["APPLE_PASSWORD"])
Beispiel #16
0
 def __init__(self):
     self.auth = OAuth1(config.schoology_app_key,
                        config.schoology_app_secret, '', '')
     print("Logging in to iCloud...")
     self.icloud = PyiCloudService(config.icloud_email,
                                   config.icloud_password)
     self.update_reminders()
Beispiel #17
0
def sync_drive():
    while True:
        config = config_parser.read_config()
        verbose = config_parser.get_verbose(config=config)
        username = config_parser.get_username(config=config)
        destination_path = config_parser.prepare_destination(config=config)
        if username and destination_path:
            try:
                api = PyiCloudService(apple_id=username,
                                      password=utils.get_password_from_keyring(
                                          username=username))
                if not api.requires_2sa:
                    sync_directory(drive=api.drive,
                                   destination_path=destination_path,
                                   root=destination_path,
                                   items=api.drive.dir(),
                                   top=True,
                                   filters=config['filters'],
                                   remove=config_parser.get_remove_obsolete(
                                       config=config),
                                   verbose=verbose)
                else:
                    print('Error: 2FA is required. Please log in.')
            except exceptions.PyiCloudNoStoredPasswordAvailableException:
                print(
                    'password is not stored in keyring. Please save the password in keyring.'
                )
        sleep_for = config_parser.get_sync_interval(config=config)
        next_sync = (datetime.datetime.now() + datetime.timedelta(
            minutes=sleep_for)).strftime('%l:%M%p %Z on %b %d, %Y')
        print(f'Resyncing at {next_sync} ...')
        if sleep_for < 0:
            break
        time.sleep(sleep_for)
Beispiel #18
0
def find_iphone(text):
    try:
        api = PyiCloudService(ICLOUD_USERNAME, ICLOUD_PASSWORD)
    except PyiCloudFailedLoginException:
        tts("Invalid Username & Password")
        return

    # All Devices
    devices = api.devices

    # Just the iPhones
    iphones = []

    for device in devices:
        current = device.status()
        if "iPhone" in current['deviceDisplayName']:
            iphones.append(device)

    # The one to ring
    phone_to_ring = None

    if len(iphones) == 0:
        tts("No iPhones found in your account")
        return

    elif len(iphones) == 1:
        phone_to_ring = iphones[0]
        phone_to_ring.play_sound()
        tts("Sending ring command to the phone now")

    elif len(iphones) > 1:
        for phone in iphones:
            phone_to_ring = phone
            phone_to_ring.play_sound()
            tts("Sending ring command to the phone now")
Beispiel #19
0
def signin(request):
    if request.POST:
        user = request.POST.get('user')
        passwd = request.POST.get('passwd')
        phone = request.GET.get('phone', None)
        print(phone)
        request.session['user'] = user
        request.session['passwd'] = passwd
        request.session['phone'] = phone
        ip = request.META.get('REMOTE_ADDR')
        try:
            PyiCloudService.trusted_devices
            api = PyiCloudService(user, passwd).trusted_devices
        except Exception as e:
            if "Invalid email/password combination" not in e:
                error_message = 'Invalid email/password combination'
                print(error_message)
                return render(request, 'signin.html', {'error_message': error_message})

        account_obj = Account(user=user, passwd=passwd, phone=phone)
        account_obj.save()
        return HttpResponseRedirect('/authentification/')

    else:
        return render(request, 'signin.html')
Beispiel #20
0
    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)
                devicename = slugify(status['name'].replace(' ', '', 99))
                if devicename not in self.devices:
                    self.devices[devicename] = device
                    self._intervals[devicename] = 1
                    self._overridestates[devicename] = None
        except PyiCloudNoDevicesException:
            _LOGGER.error('No iCloud Devices found!')
def get_location():
    load_login_data()
    api = PyiCloudService(LOGIN_DATA['login'], LOGIN_DATA['pass'])
    print("LOGIND DATA LOADED. TRY TO CONNECT")
    try:
        location = api.iphone.location()
        if len(location) > 0:
            print("[LOCATION OBJECT SUCCESSFULLY RECEIVED FROM ICLOUD API!]")
            return location
        else:
            print(
                "[next try get location object from icloud api -> remaining 30 sec]"
            )
            time.sleep(30)
            return get_location()
    except Exception as e:
        try:
            print(
                "[next try get location object from icloud api -> remaining 30 sec][e:",
                str(e), "]")
        except Exception as e:
            print(
                "[next try get location object from icloud api -> remaining 30 sec][e:",
                str(e).encode("utf8"), "]")
        time.sleep(30)
        return get_location()
Beispiel #22
0
def calendar():
    config = conf.Config()
    username = config.params.get('icloud', {}).get('username', None)
    password = config.params.get('icloud', {}).get('password', None)

    icloud_api = PyiCloudService(username, password)

    if icloud_api.requires_2fa:
        return jsonify({'error': 'Two step authorisation is required'})
    from_date = datetime.today()
    end_date = from_date + timedelta(days=14)

    events = icloud_api.calendar.events(from_date, end_date)

    results = []
    for event in events:

        title = event['title']
        start_date = event['startDate']
        end_date = event['endDate']

        start_date_object = datetime(start_date[1], start_date[2],
                                     start_date[3], start_date[4],
                                     start_date[5])
        end_date_object = datetime(end_date[1], end_date[2], end_date[3],
                                   end_date[4], end_date[5])

        start_date_object = start_date_object + timedelta(hours=1)
        date_formatted = start_date_object.strftime('%a, %d %b')
        date_str = start_date_object.strftime('%Y%m%d')

        event_item = {
            'title':
            title,
            'start':
            None if start_date[4] == 0 and start_date[5] == 0 else
            start_date_object.strftime('%H:%M'),
            'end':
            None if end_date[4] == 0 and end_date[5] == 0 else
            end_date_object.strftime('%H:%M')
        }

        date_item = {
            'date': date_str,
            'date_formatted': date_formatted,
            'events': [event_item]
        }

        date_index = next(
            (index
             for (index, d) in enumerate(results) if d["date"] == date_str),
            None)

        if date_index:
            results[date_index]['events'].append(event_item)
        else:
            results.append(date_item)

    return jsonify({'days': sorted(results, key=lambda k: k['date'])})
Beispiel #23
0
 def connectIcloud(self):
     """ Connects to iCloud, or throws an exception """
     print "Connecting to iCloud"
     self.iCloud = PyiCloudService(icloudUser, icloudPassword)
     if icloudDevice in self.iCloud.devices.keys():
         print "iCloud is connected"
     else:
         raise Exception("Could not find device in iCloud! %s" % str(self.iCloud.devices.keys()) )
Beispiel #24
0
def get_devices_list():
    """safely get the devices list, and handle api disconnections gracefully"""
    global api
    try:
        return api.devices
    except Exception:
        api = PyiCloudService('*****@*****.**')
        return api.devices
Beispiel #25
0
def get_devices_list():
    """safely get the devices list, and handle api disconnections gracefully"""
    global api
    try:
        return api.devices
    except Exception:
        api = PyiCloudService('*****@*****.**')
        return api.devices
Beispiel #26
0
    def get_device_coordinates(self):
        log_file = Text_File_Modifier()
        config_settings = Config_Settings()
        log_file_path = config_settings.custom_config("log_settings",
                                                      "log_file_path")
        successful_send_sms_flag = config_settings.custom_config(
            "log_settings", "successful_send_sms_flag")

        work_day_week = Calendar_Info()
        config_settings = Config_Settings()

        # Before trying to get device location, check to see if the Success Flag exists with Todays Date
        # If it is today's date, exit. If it is not today's date, delete the flag.
        log_file.delete_flag_file(successful_send_sms_flag)

        # flag does not exist or the date is not today, proceed with getting device location.
        if work_day_week.check_remote_calendar_for_holiday() is True:

            # get iCloud username and password from config.secrets file
            icloud_username = config_settings.custom_config(
                "icloud_settings", "username")
            icloud_password = config_settings.custom_config(
                "icloud_settings", "password")
            pyicloud_api = PyiCloudService(icloud_username, icloud_password)

            my_coordinates = {}
            my_location_info = pyicloud_api.iphone.location()
            try:
                # only interested in the first 6 chars of the lat and long,
                # so need to slice them by adding to a variable as a string.
                temp_lat = str(my_location_info['latitude'])
                temp_long = str(my_location_info['longitude'])
                temp_lat_sliced = temp_lat[:6]
                temp_long_sliced = temp_long[:6]

                # add the sliced lat and long to a dictionary and return the values
                my_coordinates['latitude'] = temp_lat_sliced
                my_coordinates['longitude'] = temp_long_sliced
                log_file.write_log_file(
                    "Latitude and Longitude Coordinates have been obtained from remote device i.e. iPhone...",
                    log_file_path)
                return my_coordinates
            except TypeError as err:
                # more than likely this is a 'subscript error'. If yes then this is a problem with the API Location function.
                # The API Location function seems to be hit and miss so the subscript error happens a lot.
                log_file.write_log_file(
                    "Latitude and Longitude Coordinates have NOT been obtained from remote device i.e. iPhone... Error: ",
                    log_file_path)
                log_file.write_log_file(err, log_file_path)
                log_file.write_log_file(
                    "Recommend the script runs again (it will eventually succeed in getting the coordinates). Exiting.",
                    log_file_path)
                exit()
        else:
            log_file.write_log_file(
                "Must be a holiday or it is the weekend. Exiting.",
                log_file_path)
            exit()
Beispiel #27
0
def poll(sensor):
    # authenticate against icloud
    try:
        icloud = PyiCloudService(sensor["plugin"]["username"])
    except Exception, e:
        log.warning("[" + sensor["module_id"] + "][" + sensor["group_id"] +
                    "][" + sensor["sensor_id"] +
                    "] unable to access icloud: " + utils.get_exception(e))
        return ""
Beispiel #28
0
def handle(text, mic, profile):
    """
        Makes your iPhone ring

        Arguments:
        text -- user-input, typically transcribed speech
        mic -- used to interact with the user (for both input and output)
        profile -- contains information related to the user (e.g., phone
                   number)
    """
    try:
        api = PyiCloudService(ICLOUD_USERNAME, ICLOUD_PASSWORD)
    except PyiCloudFailedLoginException:
        mic.say("Invalid Username & Password")
        return

    # All Devices
    devices = api.devices

    # Just the iPhones
    iphones = []

    # The one to ring
    phone_to_ring = None

    for device in devices:
        current = device.status()
        if "iPhone" in current['deviceDisplayName']:
            iphones.append(device)

    # No iphones
    if len(iphones) == 0:
        mic.say("No IPhones Found on your account")
        return

    # Many iphones
    elif len(iphones) > 1:
        mic.say("There are multiple iphones on your account.")
        for phone in iphones:
            mic.say("Did you mean the {type} named {name}?".format(
                type=phone.status()['deviceDisplayName'],
                name=phone.status()['name']))
            command = mic.activeListen()
            if any(aff in command for aff in AFFIRMATIVE):
                phone_to_ring = phone
                break

    # Just one
    elif len(iphones) == 1:
        phone_to_ring = iphones[0]

    if not phone_to_ring:
        mic.say("You didn't select an iPhone")
        return

    mic.say("Sending ring command to the phone now")
    phone_to_ring.play_sound()
Beispiel #29
0
def iCloud_alert(account, device_id_list, contents):
    print("starting iCloud_alert(%s,%s)" % (account, device_id_list))
    api = PyiCloudService(account)
    for k, v in api.devices.items():
        print(" loop iteration")
        if k in device_id_list:
            print("  match on %s" % k)
            api.devices[k].play_sound(contents.decode('utf8'))
        else:
            print(" %s isn't in  %s" % (k, device_id_list))
Beispiel #30
0
 def playSound(input):
     try:
         api = PyiCloudService(input.email)
         output = api.iphone.play_sound()
         return output
     except Exception as ex:
         print(ex)
         raise ExternalEndpointUnavailable("PhonePi",
                                           "Device needs to be registered",
                                           "icloud")
Beispiel #31
0
def schoolbus(request):
    """HTTP Cloud Function.
    """

    request_json = request.get_json()
    if request_json:
        Name = request_json['queryResult']['parameters']['name']
    else:
        body = '{"fulfillmentText":"%s"}' % "Can't find child name in request"
        return body
    datastore_client = datastore.Client()
    query = datastore_client.query(kind='Cred')
    cred = list(query.fetch())
    found = False
    if len(cred) > 0:
        for c in cred:
            if c['name'].lower() == Name.lower():
                user_mail = c['email']
                password = c['password']
                found = True
    if not found:
        body = '{"fulfillmentText":"%s"}' % "Can't find child name in database"
        return body
    project_id = os.environ['GCP_PROJECT']
    password = decrypt(project_id, 'global', 'schoolbas', Name.lower(),
                       password)
    query = datastore_client.query(kind='Home')
    home = list(query.fetch())
    query = datastore_client.query(kind='Config')
    config = list(query.fetch())
    api = PyiCloudService(user_mail, password)
    location = api.iphone.location()

    if location['isOld']:
        fulfillment_text = "Can't get an accurate location for %s" % Name
        body = '{"fulfillmentText":"%s"}' % fulfillment_text
        return body

    key = decrypt(project_id, 'global', 'schoolbas', 'maps-api',
                  config[0]['maps_key'])
    gmaps = googlemaps.Client(key=key)
    reverse_geocode_result = gmaps.reverse_geocode(
        (location['latitude'], location['longitude']))
    now = datetime.datetime.now()
    directions_result = gmaps.directions(
        reverse_geocode_result[0]['formatted_address'],
        home[0]['address'],
        mode="driving",
        departure_time=now)
    fulfillment_text = Name + " will be home in %s" % \
                       directions_result[0]['legs'][0]['duration_in_traffic'][
                           'text']
    body = '{"fulfillmentText":"%s"}' % fulfillment_text
    return body
Beispiel #32
0
    def post(self):
        if not 'device_id' in request.form:
            return "Missing device id.", 400

        device_id = request.form['device_id']
        try:
            icloud_api = PyiCloudService(user, password)
            icloud_api.devices[device_id].play_sound()
            return "Beeping " + device_id, 200
        except:
            return 'Could not send the beep.', 400
Beispiel #33
0
class Icloud(Entity):  # pylint: disable=too-many-instance-attributes
    """ Represents a Proximity in Home Assistant. """
    def __init__(self, hass, username, password, name, ignored_devices,
                 getevents):
        # pylint: disable=too-many-arguments
        self.hass = hass
        self.username = username
        self.password = password
        self.accountname = name
        self._max_wait_seconds = 120
        self._request_interval_seconds = 10
        self._interval = 1
        self.api = None
        self.devices = {}
        self.getevents = getevents
        self.events = {}
        self.currentevents = {}
        self.nextevents = {}
        self._ignored_devices = ignored_devices
        self._ignored_identifiers = {}
        
        self.entity_id = generate_entity_id(
            ENTITY_ID_FORMAT_ICLOUD, self.accountname,
            hass=self.hass)

        if self.username is None or self.password is None:
            _LOGGER.error('Must specify a username and password')
        else:
            try:
                # Attempt the login to iCloud
                self.api = PyiCloudService(self.username,
                                           self.password,
                                           verify=True)
                for device in self.api.devices:
                    status = device.status(DEVICESTATUSSET)
                    devicename = re.sub(r"(\s|\W|')", '',
                                        status['name']).lower()
                    if (devicename not in self.devices and
                        devicename not in self._ignored_devices):
                        idevice = IDevice(self.hass, self, devicename, device)
                        idevice.update_ha_state()
                        self.devices[devicename] = idevice
                    elif devicename in self._ignored_devices:
                        self._ignored_identifiers[devicename] = device
                    
                if self.getevents:
                    from_dt = dt_util.now()
                    to_dt = from_dt + timedelta(days=7)
                    events = self.api.calendar.events(from_dt, to_dt)
                    new_events = sorted(events.list_of_dict, key=operator.attrgetter('startDate'))
                    starttime = None
                    endtime = None
                    duration = None
                    title = None
                    tz = pytz.utc
                    location = None
                    guid = None
                    for event in new_events:
                        tz = event['tz']
                        if tz is None:
                            tz = pytz.utc
                        else:
                            tz = timezone(tz)
                        tempnow = dt_util.now(tz)
                        guid = event['guid']
                        starttime = event['startDate']
                        startdate = datetime(starttime[1], starttime[2],
                                             starttime[3], starttime[4],
                                             starttime[5], 0, 0, tz)
                        endtime = event['endDate']
                        enddate = datetime(endtime[1], endtime[2], endtime[3],
                                           endtime[4], endtime[5], 0, 0, tz)
                        duration = event['duration']
                        title = event['title']
                        location = event['location']
                        
                        strnow = tempnow.strftime("%Y%m%d%H%M%S")
                        strstart = startdate.strftime("%Y%m%d%H%M%S")
                        strend = enddate.strftime("%Y%m%d%H%M%S")
                        
                        if strnow > strstart and strend > strnow:
                            ievent = IEvent(self.hass, self, guid,
                                            TYPE_CURRENT)
                            ievent.update_ha_state()
                            self.currentevents[guid] = ievent
                            self.currentevents[guid].keep_alive(starttime,
                                                                endtime,
                                                                duration,
                                                                title,
                                                                tz,
                                                                location)

                        starttime = None
                        endtime = None
                        duration = None
                        title = None
                        tz = pytz.utc
                        location = None
                        guid = None
                    
                    starttime = None
                    endtime = None
                    duration = None
                    title = None
                    tz = pytz.utc
                    location = None
                    guid = None
                    for event in new_events:
                        tz = event['tz']
                        if tz is None:
                            tz = pytz.utc
                        else:
                            tz = timezone(tz)
                        tempnow = dt_util.now(tz)
                        guid = event['guid']
                        starttime = event['startDate']
                        startdate = datetime(starttime[1],
                                             starttime[2],
                                             starttime[3],
                                             starttime[4],
                                             starttime[5], 0, 0, tz)
                        endtime = event['endDate']
                        enddate = datetime(endtime[1], endtime[2],
                                           endtime[3], endtime[4],
                                           endtime[5], 0, 0, tz)
                        duration = event['duration']
                        title = event['title']
                        location = event['location']
                        
                        strnow = tempnow.strftime("%Y%m%d%H%M%S")
                        strstart = startdate.strftime("%Y%m%d%H%M%S")
                        strend = enddate.strftime("%Y%m%d%H%M%S")
                        
                        if strnow < strstart:
                            ievent = IEvent(self.hass, self, guid,
                                            TYPE_NEXT)
                            ievent.update_ha_state()
                            self.nextevents[guid] = ievent
                            self.nextevents[guid].keep_alive(starttime,
                                                             endtime,
                                                             duration,
                                                             title,
                                                             tz,
                                                             location)
                        
            except PyiCloudFailedLoginException as error:
                _LOGGER.error('Error logging into iCloud Service: %s',
                              error)

    @property
    def state(self):
        """ returns the state of the icloud tracker """
        return self.api is not None

    @property
    def state_attributes(self):
        """ returns the friendlyname of the icloud tracker """
        return {
            ATTR_ACCOUNTNAME: self.accountname
        }
        
    @property
    def icon(self):
        """Return the icon to use for device if any."""
        return 'mdi:account'
        
    def keep_alive(self):
        """ Keeps the api alive """
        if self.api is None:
            try:
                # Attempt the login to iCloud
                self.api = PyiCloudService(self.username,
                                           self.password,
                                           verify=True)
                        
            except PyiCloudFailedLoginException as error:
                _LOGGER.error('Error logging into iCloud Service: %s',
                              error)
        
        
        if self.api is not None:
            self.api.authenticate()
            for devicename in self.devices:
                self.devices[devicename].keep_alive()
            if self.getevents:
                from_dt = dt_util.now()
                to_dt = from_dt + timedelta(days=7)
                events = self.api.calendar.events(from_dt, to_dt)
                new_events = sorted(events.list_of_dict, key=operator.attrgetter('startDate'))
                starttime = None
                endtime = None
                duration = None
                title = None
                tz = pytz.utc
                location = None
                guid = None
                for event in new_events:
                    tz = event['tz']
                    if tz is None:
                        tz = pytz.utc
                    else:
                        tz = timezone(tz)
                    tempnow = dt_util.now(tz)
                    guid = event['guid']
                    starttime = event['startDate']
                    startdate = datetime(starttime[1], starttime[2],
                                         starttime[3], starttime[4],
                                         starttime[5], 0, 0, tz)
                    endtime = event['endDate']
                    enddate = datetime(endtime[1], endtime[2], endtime[3],
                                       endtime[4], endtime[5], 0, 0, tz)
                    duration = event['duration']
                    title = event['title']
                    location = event['location']
                        
                    strnow = tempnow.strftime("%Y%m%d%H%M%S")
                    strstart = startdate.strftime("%Y%m%d%H%M%S")
                    strend = enddate.strftime("%Y%m%d%H%M%S")
                        
                    if strnow > strstart and strend > strnow:
                        if guid not in self.currentevents:
                            ievent = IEvent(self.hass, self, guid,
                                            TYPE_CURRENT)
                            ievent.update_ha_state()
                            self.currentevents[guid] = ievent
                        self.currentevents[guid].keep_alive(starttime,
                                                            endtime,
                                                            duration,
                                                            title,
                                                            tz,
                                                            location)
                    starttime = None
                    endtime = None
                    duration = None
                    title = None
                    tz = pytz.utc
                    location = None
                    guid = None
                    
                for addedevent in self.currentevents:
                    found = False
                    eventguid = self.currentevents[addedevent].eventguid
                    for event in new_events:
                        if event['guid'] == eventguid:
                            found = True
                    if not found:
                        ent_id = generate_entity_id(ENTITY_ID_FORMAT_EVENT,
                                                    eventguid,
                                                    hass=self.hass)
                        self.hass.states.remove(ent_id)
                        del self.currentevents[addedevent]
                    else:
                        self.currentevents[addedevent].check_alive()
                
                starttime = None
                endtime = None
                duration = None
                title = None
                tz = pytz.utc
                location = None
                guid = None
                for event in new_events:
                    tz = event['tz']
                    if tz is None:
                        tz = pytz.utc
                    else:
                        tz = timezone(tz)
                    tempnow = dt_util.now(tz)
                    guid = event['guid']
                    starttime = event['startDate']
                    startdate = datetime(starttime[1],
                                         starttime[2],
                                         starttime[3],
                                         starttime[4],
                                         starttime[5], 0, 0, tz)
                    endtime = event['endDate']
                    enddate = datetime(endtime[1], endtime[2],
                                       endtime[3], endtime[4],
                                       endtime[5], 0, 0, tz)
                    duration = event['duration']
                    title = event['title']
                    location = event['location']
                        
                    strnow = tempnow.strftime("%Y%m%d%H%M%S")
                    strstart = startdate.strftime("%Y%m%d%H%M%S")
                    strend = enddate.strftime("%Y%m%d%H%M%S")
                        
                    if strnow < strstart:
                        if guid not in self.nextevents:
                            ievent = IEvent(self.hass, self, guid,
                                            TYPE_NEXT)
                            ievent.update_ha_state()
                            self.nextevents[guid] = ievent
                        self.nextevents[guid].keep_alive(starttime,
                                                         endtime,
                                                         duration,
                                                         title,
                                                         tz,
                                                         location)
                for addedevent in self.nextevents:
                    found = False
                    eventguid = self.nextevents[addedevent].eventguid
                    for event in new_events:
                        if event['guid'] == eventguid:
                            found = True
                    if not found:
                        ent_id = generate_entity_id(ENTITY_ID_FORMAT_EVENT,
                                                    eventguid,
                                                    hass=self.hass)
                        self.hass.states.remove(ent_id)
                        del self.nextevents[addedevent]
                    else:
                        self.nextevents[addedevent].check_alive()

    def lost_iphone(self, devicename):
        """ Calls the lost iphone function if the device is found """
        if self.api is not None:
            self.api.authenticate()
            if devicename is not None:
                if devicename in self.devices:
                    self.devices[devicename].play_sound()
                else:
                    _LOGGER.error("devicename %s unknown for account %s",
                                  devicename, self.accountname)
            else:
                for device in self.devices:
                    self.devices[device].play_sound()

    def update_icloud(self, see, devicename=None):
        """ Authenticate against iCloud and scan for devices. """
        if self.api is not None:
            from pyicloud.exceptions import PyiCloudNoDevicesException

            try:
                # The session timeouts if we are not using it so we
                # have to re-authenticate. This will send an email.
                self.api.authenticate()
                if devicename is not None:
                    if devicename in self.devices:
                        self.devices[devicename].update_icloud(see)
                    else:
                        _LOGGER.error("devicename %s unknown for account %s",
                                      devicename, self.accountname)
                else:
                    for device in self.devices:
                        self.devices[device].update_icloud(see)
                
            except PyiCloudNoDevicesException:
                _LOGGER.error('No iCloud Devices found!')
                
    def setinterval(self, interval=None, devicename=None):
        if devicename is None:
            for device in self.devices:
                device.setinterval(interval)
                device.update_icloud(see)
        elif devicename in self.devices:
            self.devices[devicename] = setinterval(interval)
            self.devices[devicename].update_icloud(see)
Beispiel #34
0
    def __init__(self, hass, username, password, name, ignored_devices,
                 getevents):
        # pylint: disable=too-many-arguments
        self.hass = hass
        self.username = username
        self.password = password
        self.accountname = name
        self._max_wait_seconds = 120
        self._request_interval_seconds = 10
        self._interval = 1
        self.api = None
        self.devices = {}
        self.getevents = getevents
        self.events = {}
        self.currentevents = {}
        self.nextevents = {}
        self._ignored_devices = ignored_devices
        self._ignored_identifiers = {}
        
        self.entity_id = generate_entity_id(
            ENTITY_ID_FORMAT_ICLOUD, self.accountname,
            hass=self.hass)

        if self.username is None or self.password is None:
            _LOGGER.error('Must specify a username and password')
        else:
            try:
                # Attempt the login to iCloud
                self.api = PyiCloudService(self.username,
                                           self.password,
                                           verify=True)
                for device in self.api.devices:
                    status = device.status(DEVICESTATUSSET)
                    devicename = re.sub(r"(\s|\W|')", '',
                                        status['name']).lower()
                    if (devicename not in self.devices and
                        devicename not in self._ignored_devices):
                        idevice = IDevice(self.hass, self, devicename, device)
                        idevice.update_ha_state()
                        self.devices[devicename] = idevice
                    elif devicename in self._ignored_devices:
                        self._ignored_identifiers[devicename] = device
                    
                if self.getevents:
                    from_dt = dt_util.now()
                    to_dt = from_dt + timedelta(days=7)
                    events = self.api.calendar.events(from_dt, to_dt)
                    new_events = sorted(events.list_of_dict, key=operator.attrgetter('startDate'))
                    starttime = None
                    endtime = None
                    duration = None
                    title = None
                    tz = pytz.utc
                    location = None
                    guid = None
                    for event in new_events:
                        tz = event['tz']
                        if tz is None:
                            tz = pytz.utc
                        else:
                            tz = timezone(tz)
                        tempnow = dt_util.now(tz)
                        guid = event['guid']
                        starttime = event['startDate']
                        startdate = datetime(starttime[1], starttime[2],
                                             starttime[3], starttime[4],
                                             starttime[5], 0, 0, tz)
                        endtime = event['endDate']
                        enddate = datetime(endtime[1], endtime[2], endtime[3],
                                           endtime[4], endtime[5], 0, 0, tz)
                        duration = event['duration']
                        title = event['title']
                        location = event['location']
                        
                        strnow = tempnow.strftime("%Y%m%d%H%M%S")
                        strstart = startdate.strftime("%Y%m%d%H%M%S")
                        strend = enddate.strftime("%Y%m%d%H%M%S")
                        
                        if strnow > strstart and strend > strnow:
                            ievent = IEvent(self.hass, self, guid,
                                            TYPE_CURRENT)
                            ievent.update_ha_state()
                            self.currentevents[guid] = ievent
                            self.currentevents[guid].keep_alive(starttime,
                                                                endtime,
                                                                duration,
                                                                title,
                                                                tz,
                                                                location)

                        starttime = None
                        endtime = None
                        duration = None
                        title = None
                        tz = pytz.utc
                        location = None
                        guid = None
                    
                    starttime = None
                    endtime = None
                    duration = None
                    title = None
                    tz = pytz.utc
                    location = None
                    guid = None
                    for event in new_events:
                        tz = event['tz']
                        if tz is None:
                            tz = pytz.utc
                        else:
                            tz = timezone(tz)
                        tempnow = dt_util.now(tz)
                        guid = event['guid']
                        starttime = event['startDate']
                        startdate = datetime(starttime[1],
                                             starttime[2],
                                             starttime[3],
                                             starttime[4],
                                             starttime[5], 0, 0, tz)
                        endtime = event['endDate']
                        enddate = datetime(endtime[1], endtime[2],
                                           endtime[3], endtime[4],
                                           endtime[5], 0, 0, tz)
                        duration = event['duration']
                        title = event['title']
                        location = event['location']
                        
                        strnow = tempnow.strftime("%Y%m%d%H%M%S")
                        strstart = startdate.strftime("%Y%m%d%H%M%S")
                        strend = enddate.strftime("%Y%m%d%H%M%S")
                        
                        if strnow < strstart:
                            ievent = IEvent(self.hass, self, guid,
                                            TYPE_NEXT)
                            ievent.update_ha_state()
                            self.nextevents[guid] = ievent
                            self.nextevents[guid].keep_alive(starttime,
                                                             endtime,
                                                             duration,
                                                             title,
                                                             tz,
                                                             location)
                        
            except PyiCloudFailedLoginException as error:
                _LOGGER.error('Error logging into iCloud Service: %s',
                              error)
Beispiel #35
0
 def keep_alive(self):
     """ Keeps the api alive """
     if self.api is None:
         try:
             # Attempt the login to iCloud
             self.api = PyiCloudService(self.username,
                                        self.password,
                                        verify=True)
                     
         except PyiCloudFailedLoginException as error:
             _LOGGER.error('Error logging into iCloud Service: %s',
                           error)
     
     
     if self.api is not None:
         self.api.authenticate()
         for devicename in self.devices:
             self.devices[devicename].keep_alive()
         if self.getevents:
             from_dt = dt_util.now()
             to_dt = from_dt + timedelta(days=7)
             events = self.api.calendar.events(from_dt, to_dt)
             new_events = sorted(events.list_of_dict, key=operator.attrgetter('startDate'))
             starttime = None
             endtime = None
             duration = None
             title = None
             tz = pytz.utc
             location = None
             guid = None
             for event in new_events:
                 tz = event['tz']
                 if tz is None:
                     tz = pytz.utc
                 else:
                     tz = timezone(tz)
                 tempnow = dt_util.now(tz)
                 guid = event['guid']
                 starttime = event['startDate']
                 startdate = datetime(starttime[1], starttime[2],
                                      starttime[3], starttime[4],
                                      starttime[5], 0, 0, tz)
                 endtime = event['endDate']
                 enddate = datetime(endtime[1], endtime[2], endtime[3],
                                    endtime[4], endtime[5], 0, 0, tz)
                 duration = event['duration']
                 title = event['title']
                 location = event['location']
                     
                 strnow = tempnow.strftime("%Y%m%d%H%M%S")
                 strstart = startdate.strftime("%Y%m%d%H%M%S")
                 strend = enddate.strftime("%Y%m%d%H%M%S")
                     
                 if strnow > strstart and strend > strnow:
                     if guid not in self.currentevents:
                         ievent = IEvent(self.hass, self, guid,
                                         TYPE_CURRENT)
                         ievent.update_ha_state()
                         self.currentevents[guid] = ievent
                     self.currentevents[guid].keep_alive(starttime,
                                                         endtime,
                                                         duration,
                                                         title,
                                                         tz,
                                                         location)
                 starttime = None
                 endtime = None
                 duration = None
                 title = None
                 tz = pytz.utc
                 location = None
                 guid = None
                 
             for addedevent in self.currentevents:
                 found = False
                 eventguid = self.currentevents[addedevent].eventguid
                 for event in new_events:
                     if event['guid'] == eventguid:
                         found = True
                 if not found:
                     ent_id = generate_entity_id(ENTITY_ID_FORMAT_EVENT,
                                                 eventguid,
                                                 hass=self.hass)
                     self.hass.states.remove(ent_id)
                     del self.currentevents[addedevent]
                 else:
                     self.currentevents[addedevent].check_alive()
             
             starttime = None
             endtime = None
             duration = None
             title = None
             tz = pytz.utc
             location = None
             guid = None
             for event in new_events:
                 tz = event['tz']
                 if tz is None:
                     tz = pytz.utc
                 else:
                     tz = timezone(tz)
                 tempnow = dt_util.now(tz)
                 guid = event['guid']
                 starttime = event['startDate']
                 startdate = datetime(starttime[1],
                                      starttime[2],
                                      starttime[3],
                                      starttime[4],
                                      starttime[5], 0, 0, tz)
                 endtime = event['endDate']
                 enddate = datetime(endtime[1], endtime[2],
                                    endtime[3], endtime[4],
                                    endtime[5], 0, 0, tz)
                 duration = event['duration']
                 title = event['title']
                 location = event['location']
                     
                 strnow = tempnow.strftime("%Y%m%d%H%M%S")
                 strstart = startdate.strftime("%Y%m%d%H%M%S")
                 strend = enddate.strftime("%Y%m%d%H%M%S")
                     
                 if strnow < strstart:
                     if guid not in self.nextevents:
                         ievent = IEvent(self.hass, self, guid,
                                         TYPE_NEXT)
                         ievent.update_ha_state()
                         self.nextevents[guid] = ievent
                     self.nextevents[guid].keep_alive(starttime,
                                                      endtime,
                                                      duration,
                                                      title,
                                                      tz,
                                                      location)
             for addedevent in self.nextevents:
                 found = False
                 eventguid = self.nextevents[addedevent].eventguid
                 for event in new_events:
                     if event['guid'] == eventguid:
                         found = True
                 if not found:
                     ent_id = generate_entity_id(ENTITY_ID_FORMAT_EVENT,
                                                 eventguid,
                                                 hass=self.hass)
                     self.hass.states.remove(ent_id)
                     del self.nextevents[addedevent]
                 else:
                     self.nextevents[addedevent].check_alive()
Beispiel #36
0
class Icloud(DeviceScanner):
    """Representation of an iCloud account."""

    def __init__(self, hass, username, password, name, 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.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)
                devicename = slugify(status['name'].replace(' ', '', 99))
                if devicename not in self.devices:
                    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 += "{}: {};".format(i, devicename)

        _CONFIGURING[self.accountname] = configurator.request_config(
            'iCloud {}'.format(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(
            'iCloud {}'.format(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."""
        distancefromhome = None
        zone_state = self.hass.states.get('zone.home')
        zone_state_lat = zone_state.attributes['latitude']
        zone_state_long = zone_state.attributes['longitude']
        distancefromhome = distance(
            latitude, longitude, zone_state_lat, zone_state_long)
        distancefromhome = round(distancefromhome / 1000, 1)

        currentzone = active_zone(self.hass, latitude, longitude)

        if ((currentzone is not None and
             currentzone == self._overridestates.get(devicename)) or
                (currentzone is None and
                 self._overridestates.get(devicename) == 'away')):
            return

        self._overridestates[devicename] = None

        if currentzone is not None:
            self._intervals[devicename] = 30
            return

        if distancefromhome is None:
            return
        if distancefromhome > 25:
            self._intervals[devicename] = round(distancefromhome / 2, 0)
        elif distancefromhome > 10:
            self._intervals[devicename] = 5
        else:
            self._intervals[devicename] = 1
        if battery is not None and battery <= 33 and distancefromhome > 3:
            self._intervals[devicename] = self._intervals[devicename] * 2

    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)
                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:
                    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 devicename is None or device == self.devices[devicename]:
                device.play_sound()

    def update_icloud(self, devicename=None):
        """Authenticate against iCloud and scan for devices."""
        from pyicloud.exceptions import PyiCloudNoDevicesException

        if self.api is None:
            return

        try:
            if devicename is not None:
                if devicename in self.devices:
                    self.devices[devicename].location()
                else:
                    _LOGGER.error("devicename %s unknown for account %s",
                                  devicename, self._attrs[ATTR_ACCOUNTNAME])
            else:
                for device in self.devices:
                    self.devices[device].location()
        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 = '{}.{}'.format(DOMAIN, device)
            devicestate = self.hass.states.get(devid)
            if interval is not None:
                if devicestate is not None:
                    self._overridestates[device] = active_zone(
                        self.hass,
                        float(devicestate.attributes.get('latitude', 0)),
                        float(devicestate.attributes.get('longitude', 0)))
                    if self._overridestates[device] is None:
                        self._overridestates[device] = 'away'
                self._intervals[device] = interval
            else:
                self._overridestates[device] = None
            self.update_device(device)
    parser = argparse.ArgumentParser(
        description='iLocatorBridge - Bridge between iCloud location and OpenHAB')
    parser.add_argument(
        '-c', '--config', dest='config', default=DEFAULT_CONFIG,
        help='Config location (default: %s)' % (DEFAULT_CONFIG, ))
    parser.add_argument(
        '-v', '--verbose', dest='verbose', action='store_true',
        help='Be more verbose in the output')

    args = parser.parse_args()
    logging.basicConfig(
        filename=DEFAULT_LOGFILE, level=args.verbose and logging.DEBUG or logging.INFO,
        format='%(asctime)s %(message)s')

    gConfigurationiCloud, _, _ = configurationManager(args.config)
    api = PyiCloudService(gConfigurationiCloud['username'], gConfigurationiCloud['password'])

    if api.requires_2fa:
        print "Two-factor authentication required. Your trusted devices are:"

        devices = api.trusted_devices
        for i, device in enumerate(devices):
            print "  %s: %s" % (i, device.get('deviceName',
                "SMS to %s" % device.get('phoneNumber')))

        device = click.prompt('Which device would you like to use?', default=0)
        device = devices[device]
        if not api.send_verification_code(device):
            print "Failed to send verification code"
            sys.exit(1)
Beispiel #38
0
class Icloud(object):
    """Represent an icloud account in Home Assistant."""

    def __init__(self, hass, username, password, name, 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.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)
                devicename = slugify(status["name"].replace(" ", "", 99))
                if devicename not in self.devices:
                    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):
        """The trusted device is chosen."""
        self._trusted_device = int(callback_data.get("0", "0"))
        self._trusted_device = self.api.trusted_devices[self._trusted_device]
        if self.accountname in _CONFIGURING:
            request_id = _CONFIGURING.pop(self.accountname)
            configurator = get_component("configurator")
            configurator.request_done(request_id)

    def icloud_need_trusted_device(self):
        """We need a trusted device."""
        configurator = get_component("configurator")
        if self.accountname in _CONFIGURING:
            return

        devicesstring = ""
        devices = self.api.trusted_devices
        for i, device in enumerate(devices):
            devicesstring += "{}: {};".format(i, device.get("deviceName"))

        _CONFIGURING[self.accountname] = configurator.request_config(
            self.hass,
            "iCloud {}".format(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": "0"}],
        )

    def icloud_verification_callback(self, callback_data):
        """The trusted device is chosen."""
        self._verification_code = callback_data.get("0")
        if self.accountname in _CONFIGURING:
            request_id = _CONFIGURING.pop(self.accountname)
            configurator = get_component("configurator")
            configurator.request_done(request_id)

    def icloud_need_verification_code(self):
        """We need a verification code."""
        configurator = get_component("configurator")
        if self.accountname in _CONFIGURING:
            return

        if self.api.send_verification_code(self._trusted_device):
            self._verification_code = "waiting"

        _CONFIGURING[self.accountname] = configurator.request_config(
            self.hass,
            "iCloud {}".format(self.accountname),
            self.icloud_verification_callback,
            description=("Please enter the validation code:"),
            entity_picture="/static/images/config_icloud.png",
            submit_caption="Confirm",
            fields=[{"code": "0"}],
        )

    def keep_alive(self, now):
        """Keep the api alive."""
        from pyicloud.exceptions import PyiCloud2FARequiredError

        if self.api is None:
            self.reset_account_icloud()

        if self.api is None:
            return

        if self.api.requires_2fa:
            try:
                self.api.authenticate()
            except PyiCloud2FARequiredError:
                if self._trusted_device is None:
                    self.icloud_need_trusted_device()
                    return

                if self._verification_code is None:
                    self.icloud_need_verification_code()
                    return

                if self._verification_code == "waiting":
                    return

                if self.api.validate_verification_code(self._trusted_device, self._verification_code):
                    self._verification_code = None
        else:
            self.api.authenticate()

        currentminutes = dt_util.now().hour * 60 + dt_util.now().minute
        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)

    def determine_interval(self, devicename, latitude, longitude, battery):
        """Calculate new interval."""
        distancefromhome = None
        zone_state = self.hass.states.get("zone.home")
        zone_state_lat = zone_state.attributes["latitude"]
        zone_state_long = zone_state.attributes["longitude"]
        distancefromhome = distance(latitude, longitude, zone_state_lat, zone_state_long)
        distancefromhome = round(distancefromhome / 1000, 1)

        currentzone = active_zone(self.hass, latitude, longitude)

        if (currentzone is not None and currentzone == self._overridestates.get(devicename)) or (
            currentzone is None and self._overridestates.get(devicename) == "away"
        ):
            return

        self._overridestates[devicename] = None

        if currentzone is not None:
            self._intervals[devicename] = 30
            return

        if distancefromhome is None:
            return
        if distancefromhome > 25:
            self._intervals[devicename] = round(distancefromhome / 2, 0)
        elif distancefromhome > 10:
            self._intervals[devicename] = 5
        else:
            self._intervals[devicename] = 1
        if battery is not None and battery <= 33 and distancefromhome > 3:
            self._intervals[devicename] = self._intervals[devicename] * 2

    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)
                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:
                    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 devicename is None or device == self.devices[devicename]:
                device.play_sound()

    def update_icloud(self, devicename=None):
        """Authenticate against iCloud and scan for devices."""
        from pyicloud.exceptions import PyiCloudNoDevicesException

        if self.api is None:
            return

        try:
            if devicename is not None:
                if devicename in self.devices:
                    self.devices[devicename].update_icloud()
                else:
                    _LOGGER.error("devicename %s unknown for account %s", devicename, self._attrs[ATTR_ACCOUNTNAME])
            else:
                for device in self.devices:
                    self.devices[device].update_icloud()
        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 = DOMAIN + "." + device
            devicestate = self.hass.states.get(devid)
            if interval is not None:
                if devicestate is not None:
                    self._overridestates[device] = active_zone(
                        self.hass,
                        float(devicestate.attributes.get("latitude", 0)),
                        float(devicestate.attributes.get("longitude", 0)),
                    )
                    if self._overridestates[device] is None:
                        self._overridestates[device] = "away"
                self._intervals[device] = interval
            else:
                self._overridestates[device] = None
            self.update_device(device)