Coverage for icloudpy/services/account.py: 93%
217 statements
« prev ^ index » next coverage.py v7.6.10, created at 2024-12-30 19:31 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2024-12-30 19:31 +0000
1"""Account service."""
2from collections import OrderedDict
4from six import PY2
6from icloudpy.utils import underscore_to_camelcase
9class AccountService:
10 """The 'Account' iCloud service."""
12 def __init__(self, service_root, session, params):
13 self.session = session
14 self.params = params
15 self._service_root = service_root
17 self._devices = []
18 self._family = []
19 self._storage = None
21 self._acc_endpoint = f"{self._service_root}/setup/web"
22 self._acc_devices_url = f"{self._acc_endpoint}/device/getDevices"
23 self._acc_family_details_url = f"{self._acc_endpoint}/family/getFamilyDetails"
24 self._acc_family_member_photo_url = (
25 f"{self._acc_endpoint}/family/getMemberPhoto"
26 )
28 self._acc_storage_url = "https://setup.icloud.com/setup/ws/1/storageUsageInfo"
30 @property
31 def devices(self):
32 """Returns current paired devices."""
33 if not self._devices:
34 req = self.session.get(self._acc_devices_url, params=self.params)
35 response = req.json()
37 for device_info in response["devices"]:
38 self._devices.append(AccountDevice(device_info))
40 return self._devices
42 @property
43 def family(self):
44 """Returns family members."""
45 if not self._family:
46 req = self.session.get(self._acc_family_details_url, params=self.params)
47 response = req.json()
49 for member_info in response["familyMembers"]:
50 self._family.append(
51 FamilyMember(
52 member_info,
53 self.session,
54 self.params,
55 self._acc_family_member_photo_url,
56 ),
57 )
59 return self._family
61 @property
62 def storage(self):
63 """Returns storage infos."""
64 if not self._storage:
65 req = self.session.get(self._acc_storage_url, params=self.params)
66 response = req.json()
68 self._storage = AccountStorage(response)
70 return self._storage
72 def __unicode__(self):
73 storage_available = self.storage.usage.available_storage_in_bytes
74 return f"{{devices: {len(self.devices)}, family: {len(self.family)}, storage: {storage_available} bytes free}}"
76 def __str__(self):
77 as_unicode = self.__unicode__()
78 if PY2:
79 return as_unicode.encode("utf-8", "ignore")
80 return as_unicode
82 def __repr__(self):
83 return f"<{type(self).__name__}: {str(self)}>"
86class AccountDevice(dict):
87 """Account device."""
89 def __getattr__(self, key):
90 return self[underscore_to_camelcase(key)]
92 def __unicode__(self):
93 return f"{{model: {self.model_display_name}, name: {self.name}}}"
95 def __str__(self):
96 as_unicode = self.__unicode__()
97 if PY2:
98 return as_unicode.encode("utf-8", "ignore")
99 return as_unicode
101 def __repr__(self):
102 return f"<{type(self).__name__}: {str(self)}>"
105class FamilyMember:
106 """A family member."""
108 def __init__(self, member_info, session, params, acc_family_member_photo_url):
109 self._attrs = member_info
110 self._session = session
111 self._params = params
112 self._acc_family_member_photo_url = acc_family_member_photo_url
114 @property
115 def last_name(self):
116 """Gets the last name."""
117 return self._attrs.get("lastName")
119 @property
120 def dsid(self):
121 """Gets the dsid."""
122 return self._attrs.get("dsid")
124 @property
125 def original_invitation_email(self):
126 """Gets the original invitation."""
127 return self._attrs.get("originalInvitationEmail")
129 @property
130 def full_name(self):
131 """Gets the full name."""
132 return self._attrs.get("fullName")
134 @property
135 def age_classification(self):
136 """Gets the age classification."""
137 return self._attrs.get("ageClassification")
139 @property
140 def apple_id_for_purchases(self):
141 """Gets the apple id for purchases."""
142 return self._attrs.get("appleIdForPurchases")
144 @property
145 def apple_id(self):
146 """Gets the apple id."""
147 return self._attrs.get("appleId")
149 @property
150 def family_id(self):
151 """Gets the family id."""
152 return self._attrs.get("familyId")
154 @property
155 def first_name(self):
156 """Gets the first name."""
157 return self._attrs.get("firstName")
159 @property
160 def has_parental_privileges(self):
161 """Has parental privileges."""
162 return self._attrs.get("hasParentalPrivileges")
164 @property
165 def has_screen_time_enabled(self):
166 """Has screen time enabled."""
167 return self._attrs.get("hasScreenTimeEnabled")
169 @property
170 def has_ask_to_buy_enabled(self):
171 """Has to ask for buying."""
172 return self._attrs.get("hasAskToBuyEnabled")
174 @property
175 def has_share_purchases_enabled(self):
176 """Has share purshases."""
177 return self._attrs.get("hasSharePurchasesEnabled")
179 @property
180 def share_my_location_enabled_family_members(self):
181 """Has share my location with family."""
182 return self._attrs.get("shareMyLocationEnabledFamilyMembers")
184 @property
185 def has_share_my_location_enabled(self):
186 """Has share my location."""
187 return self._attrs.get("hasShareMyLocationEnabled")
189 @property
190 def dsid_for_purchases(self):
191 """Gets the dsid for purchases."""
192 return self._attrs.get("dsidForPurchases")
194 def get_photo(self):
195 """Returns the photo."""
196 params_photo = dict(self._params)
197 params_photo.update({"memberId": self.dsid})
198 return self._session.get(
199 self._acc_family_member_photo_url, params=params_photo, stream=True,
200 )
202 def __getitem__(self, key):
203 if self._attrs.get(key):
204 return self._attrs[key]
205 return getattr(self, key)
207 def __unicode__(self):
208 return (
209 f"{{name: {self.full_name}, age_classification: {self.age_classification}}}"
210 )
212 def __str__(self):
213 as_unicode = self.__unicode__()
214 if PY2:
215 return as_unicode.encode("utf-8", "ignore")
216 return as_unicode
218 def __repr__(self):
219 return f"<{type(self).__name__}: {str(self)}>"
222class AccountStorageUsageForMedia:
223 """Storage used for a specific media type into the account."""
225 def __init__(self, usage_data):
226 self.usage_data = usage_data
228 @property
229 def key(self):
230 """Gets the key."""
231 return self.usage_data["mediaKey"]
233 @property
234 def label(self):
235 """Gets the label."""
236 return self.usage_data["displayLabel"]
238 @property
239 def color(self):
240 """Gets the HEX color."""
241 return self.usage_data["displayColor"]
243 @property
244 def usage_in_bytes(self):
245 """Gets the usage in bytes."""
246 return self.usage_data["usageInBytes"]
248 def __unicode__(self):
249 return f"{{key: {self.key}, usage: {self.usage_in_bytes} bytes}}"
251 def __str__(self):
252 as_unicode = self.__unicode__()
253 if PY2:
254 return as_unicode.encode("utf-8", "ignore")
255 return as_unicode
257 def __repr__(self):
258 return f"<{type(self).__name__}: {str(self)}>"
261class AccountStorageUsage:
262 """Storage used for a specific media type into the account."""
264 def __init__(self, usage_data, quota_data):
265 self.usage_data = usage_data
266 self.quota_data = quota_data
268 @property
269 def comp_storage_in_bytes(self):
270 """Gets the comp storage in bytes."""
271 return self.usage_data["compStorageInBytes"]
273 @property
274 def used_storage_in_bytes(self):
275 """Gets the used storage in bytes."""
276 return self.usage_data["usedStorageInBytes"]
278 @property
279 def used_storage_in_percent(self):
280 """Gets the used storage in percent."""
281 return round(self.used_storage_in_bytes * 100 / self.total_storage_in_bytes, 2)
283 @property
284 def available_storage_in_bytes(self):
285 """Gets the available storage in bytes."""
286 return self.total_storage_in_bytes - self.used_storage_in_bytes
288 @property
289 def available_storage_in_percent(self):
290 """Gets the available storage in percent."""
291 return round(
292 self.available_storage_in_bytes * 100 / self.total_storage_in_bytes, 2,
293 )
295 @property
296 def total_storage_in_bytes(self):
297 """Gets the total storage in bytes."""
298 return self.usage_data["totalStorageInBytes"]
300 @property
301 def commerce_storage_in_bytes(self):
302 """Gets the commerce storage in bytes."""
303 return self.usage_data["commerceStorageInBytes"]
305 @property
306 def quota_over(self):
307 """Gets the over quota."""
308 return self.quota_data["overQuota"]
310 @property
311 def quota_tier_max(self):
312 """Gets the max tier quota."""
313 return self.quota_data["haveMaxQuotaTier"]
315 @property
316 def quota_almost_full(self):
317 """Gets the almost full quota."""
318 return self.quota_data["almost-full"]
320 @property
321 def quota_paid(self):
322 """Gets the paid quota."""
323 return self.quota_data["paidQuota"]
325 def __unicode__(self):
326 return f"{self.used_storage_in_percent}% used of {self.total_storage_in_bytes} bytes"
328 def __str__(self):
329 as_unicode = self.__unicode__()
330 if PY2:
331 return as_unicode.encode("utf-8", "ignore")
332 return as_unicode
334 def __repr__(self):
335 return f"<{type(self).__name__}: {str(self)}>"
338class AccountStorage:
339 """Storage of the account."""
341 def __init__(self, storage_data):
342 self.usage = AccountStorageUsage(
343 storage_data.get("storageUsageInfo"), storage_data.get("quotaStatus"),
344 )
345 self.usages_by_media = OrderedDict()
347 for usage_media in storage_data.get("storageUsageByMedia"):
348 self.usages_by_media[usage_media["mediaKey"]] = AccountStorageUsageForMedia(
349 usage_media,
350 )
352 def __unicode__(self):
353 return f"{{usage: {self.usage}, usages_by_media: {self.usages_by_media}}}"
355 def __str__(self):
356 as_unicode = self.__unicode__()
357 if PY2:
358 return as_unicode.encode("utf-8", "ignore")
359 return as_unicode
361 def __repr__(self):
362 return f"<{type(self).__name__}: {str(self)}>"