Adapt existing module architecture to allow for multiple apple devices; adding unicode/string displays; adding attribute and key convenience accessors.

This commit is contained in:
Adam Coddington 2013-05-18 19:14:27 -07:00
parent ff230c6f42
commit 8b5e907896
4 changed files with 134 additions and 38 deletions

View file

@ -5,7 +5,7 @@ import json
import requests import requests
from exceptions import PyiCloudFailedLoginException from exceptions import PyiCloudFailedLoginException
from services import FindMyiPhoneService, CalendarService from services import FindMyiPhoneServiceManager, CalendarService
class PyiCloudService(object): class PyiCloudService(object):
@ -88,11 +88,29 @@ class PyiCloudService(object):
self.webservices = self.discovery['webservices'] self.webservices = self.discovery['webservices']
@property @property
def iphone(self): def devices(self):
""" Return all devices."""
service_root = self.webservices['findme']['url'] service_root = self.webservices['findme']['url']
return FindMyiPhoneService(service_root, self.session, self.params) return FindMyiPhoneServiceManager(
service_root,
self.session,
self.params
)
@property
def iphone(self):
return self.devices[0]
@property @property
def calendar(self): def calendar(self):
service_root = self.webservices['calendar']['url'] service_root = self.webservices['calendar']['url']
return CalendarService(service_root, self.session, self.params) return CalendarService(service_root, self.session, self.params)
def __unicode__(self):
return u'iCloud API: %s' % self.user.get('apple_id')
def __str__(self):
return unicode(self).encode('ascii', 'ignore')
def __repr__(self):
return '<%s>' % str(self)

View file

@ -1,2 +1,2 @@
from calendar import CalendarService from calendar import CalendarService
from findmyiphone import FindMyiPhoneService from findmyiphone import FindMyiPhoneServiceManager

View file

@ -3,11 +3,14 @@ import json
from pyicloud.exceptions import PyiCloudNoDevicesException from pyicloud.exceptions import PyiCloudNoDevicesException
class FindMyiPhoneService(object): class FindMyiPhoneServiceManager(object):
""" """ The 'Find my iPhone' iCloud service
The 'Find my iPhone' iCloud service, connects to iCloud and returns
phone data including the near-realtime latitude and longitude. This connects to iCloud and return phone data including the near-realtime
latitude and longitude.
""" """
def __init__(self, service_root, session, params): def __init__(self, service_root, session, params):
self.session = session self.session = session
self.params = params self.params = params
@ -17,59 +20,106 @@ class FindMyiPhoneService(object):
self._fmip_sound_url = '%s/playSound' % self._fmip_endpoint self._fmip_sound_url = '%s/playSound' % self._fmip_endpoint
self._fmip_lost_url = '%s/lostDevice' % self._fmip_endpoint self._fmip_lost_url = '%s/lostDevice' % self._fmip_endpoint
self._devices = {}
self.refresh_client()
def refresh_client(self): def refresh_client(self):
""" """ Refreshes the FindMyiPhoneService endpoint,
Refreshes the FindMyiPhoneService endpoint,
ensuring that the location data is up-to-date. This ensures that the location data is up-to-date.
""" """
host = self._service_root.split('//')[1].split(':')[0] host = self._service_root.split('//')[1].split(':')[0]
self.session.headers.update({'host': host}) self.session.headers.update({'host': host})
req = self.session.post(self._fmip_refresh_url, params=self.params) req = self.session.post(self._fmip_refresh_url, params=self.params)
self.response = req.json() self.response = req.json()
if self.response['content']:
# TODO: Support multiple devices. for device_info in self.response['content']:
self.content = self.response['content'][0] device_id = device_info['id']
else: if not device_id in self._devices:
message = 'You do not have any active devices.' self._devices[device_id] = AppleDevice(
device_info,
self.session,
self.params,
manager=self,
sound_url=self._fmip_sound_url,
lost_url=self._fmip_lost_url
)
else:
self._devices[device_id].update(device_info)
if not self._devices:
raise PyiCloudNoDevicesException(message) raise PyiCloudNoDevicesException(message)
self.user_info = self.response['userInfo']
def __getitem__(self, key):
if isinstance(key, int):
key = self.keys()[key]
return self._devices[key]
def __getattr__(self, attr):
return getattr(self._devices, attr)
def __unicode__(self):
return unicode(self._devices)
def __str__(self):
return unicode(self).encode('ascii', 'ignore')
def __repr__(self):
return str(self)
class AppleDevice(object):
def __init__(self, content, session, params, manager,
sound_url=None, lost_url=None):
self.content = content
self.manager = manager
self.session = session
self.params = params
self.sound_url = sound_url
self.lost_url = lost_url
def update(self, data):
self.content = data
def location(self): def location(self):
self.refresh_client() self.manager.refresh_client()
return self.content['location'] return self.content['location']
def status(self, additional=[]): def status(self, additional=[]):
""" Returns status information for device.
This returns only a subset of possible properties.
""" """
The FindMyiPhoneService response is quite bloated, this method self.manager.refresh_client()
will return a subset of the more useful properties.
"""
self.refresh_client()
fields = ['batteryLevel', 'deviceDisplayName', 'deviceStatus', 'name'] fields = ['batteryLevel', 'deviceDisplayName', 'deviceStatus', 'name']
fields += additional fields += additional
properties = {} properties = {}
for field in fields: for field in fields:
properties[field] = self.content.get(field, 'Unknown') properties[field] = self.content.get(field)
return properties return properties
def play_sound(self, subject='Find My iPhone Alert'): def play_sound(self, subject='Find My iPhone Alert'):
""" """ Send a request to the device to play a sound.
Send a request to the device to play a sound, it's possible to
pass a custom message by changing the `subject`.
"""
self.refresh_client()
data = json.dumps({'device': self.content['id'], 'subject': subject})
self.session.post(self._fmip_sound_url, params=self.params, data=data)
def lost_device(self, number, text=None): It's possible to pass a custom message by changing the `subject`.
""" """
Send a request to the device to trigger 'lost mode'. The data = json.dumps({'device': self.content['id'], 'subject': subject})
device will show the message in `text`, and if a number has self.session.post(
self.sound_url,
params=self.params,
data=data
)
def lost_device(self, number,
text='This iPhone has been lost. Please call me.'):
""" Send a request to the device to trigger 'lost mode'.
The device will show the message in `text`, and if a number has
been passed, then the person holding the device can call been passed, then the person holding the device can call
the number without entering the passcode. the number without entering the passcode.
""" """
self.refresh_client()
if not text:
text = 'This iPhone has been lost. Please call me.'
data = json.dumps({ data = json.dumps({
'text': text, 'text': text,
'userText': True, 'userText': True,
@ -78,4 +128,32 @@ class FindMyiPhoneService(object):
'trackingEnabled': True, 'trackingEnabled': True,
'device': self.content['id'], 'device': self.content['id'],
}) })
self.session.post(self._fmip_lost_url, params=self.params, data=data) self.session.post(
self.lost_url,
params=self.params,
data=data
)
@property
def data(self):
return self.content
def __getitem__(self, key):
return self.content[key]
def __getattr__(self, attr):
return getattr(self.content, attr)
def __unicode__(self):
display_name = self['deviceDisplayName']
name = self['name']
return u'%s: %s' % (
display_name,
name,
)
def __str__(self):
return unicode(self).encode('ascii', 'ignore')
def __repr__(self):
return '<AppleDevice(%s)>' % str(self)

View file

@ -7,7 +7,7 @@ with open('requirements.txt') as f:
setup( setup(
name='pyicloud', name='pyicloud',
version='0.1', version='0.2',
url='https://github.com/picklepete/pyicloud', url='https://github.com/picklepete/pyicloud',
description=( description=(
'PyiCloud is a module which allows pythonistas to ' 'PyiCloud is a module which allows pythonistas to '