Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality to erase devices in FindMyiPhone #448

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ You can list which devices associated with your account by using the ``devices``

and you can access individual devices by either their index, or their ID:

.. code-block:: pycon
>>> api.devices[0]
<AppleDevice(iPhone 4S: Johnny Appleseed's iPhone)>
>>> api.devices['i9vbKRGIcLYqJnXMd1b257kUWnoyEBcEh6yM+IfmiMLh7BmOpALS+w==']
<AppleDevice(iPhone 4S: Johnny Appleseed's iPhone)>

>>> api.devices[0]
<AppleDevice(iPhone 4S: Johnny Appleseed's iPhone)>
Expand All @@ -156,20 +159,16 @@ Location

Returns the device's last known location. The Find My iPhone app must have been installed and initialized.

.. code-block:: pycon

>>> api.iphone.location()
{'timeStamp': 1357753796553, 'locationFinished': True, 'longitude': -0.14189, 'positionType': 'GPS', 'locationType': None, 'latitude': 51.501364, 'isOld': False, 'horizontalAccuracy': 5.0}
>>> api.iphone.location()
{u'timeStamp': 1357753796553, u'locationFinished': True, u'longitude': -0.14189, u'positionType': u'GPS', u'locationType': None, u'latitude': 51.501364, u'isOld': False, u'horizontalAccuracy': 5.0}

Status
******

The Find My iPhone response is quite bloated, so for simplicity's sake this method will return a subset of the properties.

.. code-block:: pycon

>>> api.iphone.status()
{'deviceDisplayName': 'iPhone 5', 'deviceStatus': '200', 'batteryLevel': 0.6166913, 'name': "Peter's iPhone"}
>>> api.iphone.status()
{'deviceDisplayName': u'iPhone 5', 'deviceStatus': u'200', 'batteryLevel': 0.6166913, 'name': u"Peter's iPhone"}

If you wish to request further properties, you may do so by passing in a list of property names.

Expand Down
41 changes: 31 additions & 10 deletions pyicloud/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
PyiCloudServiceNotActivatedException,
)
from pyicloud.services import (
FindMyiPhoneServiceManager,
FindMyiPhoneService,
CalendarService,
UbiquityService,
ContactsService,
Expand Down Expand Up @@ -126,7 +126,7 @@ def request(self, method, url, **kwargs): # pylint: disable=arguments-differ
api_error = PyiCloudAPIResponseException(
response.reason, response.status_code, retry=True
)
request_logger.debug(api_error)
request_logger.warning(api_error)
kwargs["retried"] = True
return self.request(method, url, **kwargs)

Expand Down Expand Up @@ -197,7 +197,7 @@ class PyiCloudService:
Usage:
from pyicloud import PyiCloudService
pyicloud = PyiCloudService('[email protected]', 'password')
pyicloud.iphone.location()
pyicloud.iphone.location
"""

AUTH_ENDPOINT = "https://idmsa.apple.com/appleauth/auth"
Expand Down Expand Up @@ -271,6 +271,7 @@ def __init__(
self.authenticate()

self._drive = None
self._find_my_iphone = None
self._files = None
self._photos = None

Expand Down Expand Up @@ -536,18 +537,38 @@ def _get_webservice_url(self, ws_key):
)
return self._webservices[ws_key]["url"]

def fmipWebAuthenticate(self, device):
data = json.dumps(
{
"dsWebAuthToken": self.session_data["session_token"]
}
)
req = self.session.post("%s/fmipWebAuthenticate" % self.SETUP_ENDPOINT, params=self.params, data=data)
return req.json()['tokens']['mmeFMIPWebEraseDeviceToken']

@property
def find_my_iphone(self):
"""Gets the 'Find My iPhone' service."""
if not self._find_my_iphone:
service_root = self._get_webservice_url("findme")
self._find_my_iphone = FindMyiPhoneService(
service_root, self.session, self.params, self.with_family
)
return self._find_my_iphone

@property
def devices(self):
"""Returns all devices."""
service_root = self._get_webservice_url("findme")
return FindMyiPhoneServiceManager(
service_root, self.session, self.params, self.with_family
)
"""Return all devices."""
service = self.find_my_iphone
service.refresh_client()
return service.devices

@property
def iphone(self):
"""Returns the iPhone."""
return self.devices[0]
"""Return the first device (usually the iPhone)."""
service = self.find_my_iphone
service.refresh_client()
return service.device(0)

@property
def account(self):
Expand Down
50 changes: 26 additions & 24 deletions pyicloud/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from click import confirm

from pyicloud import PyiCloudService
from pyicloud.base import PyiCloudService
from pyicloud.exceptions import PyiCloudFailedLoginException
from . import utils

Expand All @@ -24,8 +24,9 @@ def create_pickled_data(idevice, filename):
This allows the data to be used without resorting to screen / pipe
scrapping.
"""
with open(filename, "wb") as pickle_file:
pickle.dump(idevice.content, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
pickle_file = open(filename, "wb")
pickle.dump(idevice.content, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
pickle_file.close()


def main(args=None):
Expand Down Expand Up @@ -263,40 +264,41 @@ def main(args=None):

print(message, file=sys.stderr)

for dev in api.devices:
if not command_line.device_id or (
command_line.device_id.strip().lower() == dev.content["id"].strip().lower()
fmi_service = api.find_my_iphone
fmi_service.refresh_client()
for device in fmi_service.devices.values():
if (
not command_line.device_id
or command_line.device_id.strip().lower() == device.id.strip().lower()
):
# List device(s)
if command_line.locate:
dev.location()
fmi_service.refresh_client()

if command_line.output_to_file:
create_pickled_data(
dev,
filename=(dev.content["name"].strip().lower() + ".fmip_snapshot"),
device, filename=(device.name.strip().lower() + ".fmip_snapshot")
)

contents = dev.content
if command_line.longlist:
print("-" * 30)
print(contents["name"])
for key in contents:
print("%20s - %s" % (key, contents[key]))
print(device.name)
for attr, value in device.attrs.items():
print("%20s - %s" % (attr, value))
elif command_line.list:
print("-" * 30)
print("Name - %s" % contents["name"])
print("Display Name - %s" % contents["deviceDisplayName"])
print("Location - %s" % contents["location"])
print("Battery Level - %s" % contents["batteryLevel"])
print("Battery Status- %s" % contents["batteryStatus"])
print("Device Class - %s" % contents["deviceClass"])
print("Device Model - %s" % contents["deviceModel"])
print("Name - %s" % device.name)
print("Display Name - %s" % device.deviceDisplayName)
print("Location - %s" % device.location)
print("Battery Level - %s" % device.batteryLevel)
print("Battery Status- %s" % device.batteryStatus)
print("Device Class - %s" % device.deviceClass)
print("Device Model - %s" % device.deviceModel)

# Play a Sound on a device
if command_line.sound:
if command_line.device_id:
dev.play_sound()
device.play_sound()
else:
raise RuntimeError(
"\n\n\t\t%s %s\n\n"
Expand All @@ -309,7 +311,7 @@ def main(args=None):
# Display a Message on the device
if command_line.message:
if command_line.device_id:
dev.display_message(
device.display_message(
subject="A Message", message=command_line.message, sounds=True
)
else:
Expand All @@ -324,7 +326,7 @@ def main(args=None):
# Display a Silent Message on the device
if command_line.silentmessage:
if command_line.device_id:
dev.display_message(
device.display_message(
subject="A Silent Message",
message=command_line.silentmessage,
sounds=False,
Expand All @@ -342,7 +344,7 @@ def main(args=None):
# Enable Lost mode
if command_line.lostmode:
if command_line.device_id:
dev.lost_device(
device.lost_device(
number=command_line.lost_phone.strip(),
text=command_line.lost_message.strip(),
newpasscode=command_line.lost_password.strip(),
Expand Down
2 changes: 1 addition & 1 deletion pyicloud/services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Services."""
from pyicloud.services.calendar import CalendarService
from pyicloud.services.findmyiphone import FindMyiPhoneServiceManager
from pyicloud.services.findmyiphone import FindMyiPhoneService
from pyicloud.services.ubiquity import UbiquityService
from pyicloud.services.contacts import ContactsService
from pyicloud.services.reminders import RemindersService
Expand Down
7 changes: 5 additions & 2 deletions pyicloud/services/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,13 +316,16 @@ def __init__(self, storage_data):
self.usage = AccountStorageUsage(
storage_data.get("storageUsageInfo"), storage_data.get("quotaStatus")
)
self.usages_by_media = OrderedDict()
self.usage_by_media = OrderedDict()

for usage_media in storage_data.get("storageUsageByMedia"):
self.usages_by_media[usage_media["mediaKey"]] = AccountStorageUsageForMedia(
self.usage_by_media[usage_media["mediaKey"]] = AccountStorageUsageForMedia(
usage_media
)

def __unicode__(self):
return "{usage: %s, usages_by_media: %s}" % (self.usage, self.usages_by_media)

def __str__(self):
return f"{{usage: {self.usage}, usages_by_media: {self.usages_by_media}}}"

Expand Down
Loading