Coverage for icloudpy/services/account.py: 95%
198 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-28 23:44 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-28 23:44 +0000
1"""Account service."""
3from collections import OrderedDict
5from icloudpy.utils import underscore_to_camelcase
8class AccountService:
9 """The 'Account' iCloud service."""
11 def __init__(self, service_root, session, params):
12 self.session = session
13 self.params = params
14 self._service_root = service_root
16 self._devices = []
17 self._family = []
18 self._storage = None
20 self._acc_endpoint = f"{self._service_root}/setup/web"
21 self._acc_devices_url = f"{self._acc_endpoint}/device/getDevices"
22 self._acc_family_details_url = f"{self._acc_endpoint}/family/getFamilyDetails"
23 self._acc_family_member_photo_url = f"{self._acc_endpoint}/family/getMemberPhoto"
25 self._acc_storage_url = "https://setup.icloud.com/setup/ws/1/storageUsageInfo"
27 @property
28 def devices(self):
29 """Returns current paired devices."""
30 if not self._devices:
31 req = self.session.get(self._acc_devices_url, params=self.params)
32 response = req.json()
34 for device_info in response["devices"]:
35 self._devices.append(AccountDevice(device_info))
37 return self._devices
39 @property
40 def family(self):
41 """Returns family members."""
42 if not self._family:
43 req = self.session.get(self._acc_family_details_url, params=self.params)
44 response = req.json()
46 for member_info in response["familyMembers"]:
47 self._family.append(
48 FamilyMember(
49 member_info,
50 self.session,
51 self.params,
52 self._acc_family_member_photo_url,
53 ),
54 )
56 return self._family
58 @property
59 def storage(self):
60 """Returns storage infos."""
61 if not self._storage:
62 req = self.session.get(self._acc_storage_url, params=self.params)
63 response = req.json()
65 self._storage = AccountStorage(response)
67 return self._storage
69 def __unicode__(self):
70 storage_available = self.storage.usage.available_storage_in_bytes
71 return f"{{devices: {len(self.devices)}, family: {len(self.family)}, storage: {storage_available} bytes free}}"
73 def __str__(self):
74 return self.__unicode__()
76 def __repr__(self):
77 return f"<{type(self).__name__}: {str(self)}>"
80class AccountDevice(dict):
81 """Account device."""
83 def __getattr__(self, key):
84 return self[underscore_to_camelcase(key)]
86 def __unicode__(self):
87 return f"{{model: {self.model_display_name}, name: {self.name}}}"
89 def __str__(self):
90 return self.__unicode__()
92 def __repr__(self):
93 return f"<{type(self).__name__}: {str(self)}>"
96class FamilyMember:
97 """A family member."""
99 def __init__(self, member_info, session, params, acc_family_member_photo_url):
100 self._attrs = member_info
101 self._session = session
102 self._params = params
103 self._acc_family_member_photo_url = acc_family_member_photo_url
105 @property
106 def last_name(self):
107 """Gets the last name."""
108 return self._attrs.get("lastName")
110 @property
111 def dsid(self):
112 """Gets the dsid."""
113 return self._attrs.get("dsid")
115 @property
116 def original_invitation_email(self):
117 """Gets the original invitation."""
118 return self._attrs.get("originalInvitationEmail")
120 @property
121 def full_name(self):
122 """Gets the full name."""
123 return self._attrs.get("fullName")
125 @property
126 def age_classification(self):
127 """Gets the age classification."""
128 return self._attrs.get("ageClassification")
130 @property
131 def apple_id_for_purchases(self):
132 """Gets the apple id for purchases."""
133 return self._attrs.get("appleIdForPurchases")
135 @property
136 def apple_id(self):
137 """Gets the apple id."""
138 return self._attrs.get("appleId")
140 @property
141 def family_id(self):
142 """Gets the family id."""
143 return self._attrs.get("familyId")
145 @property
146 def first_name(self):
147 """Gets the first name."""
148 return self._attrs.get("firstName")
150 @property
151 def has_parental_privileges(self):
152 """Has parental privileges."""
153 return self._attrs.get("hasParentalPrivileges")
155 @property
156 def has_screen_time_enabled(self):
157 """Has screen time enabled."""
158 return self._attrs.get("hasScreenTimeEnabled")
160 @property
161 def has_ask_to_buy_enabled(self):
162 """Has to ask for buying."""
163 return self._attrs.get("hasAskToBuyEnabled")
165 @property
166 def has_share_purchases_enabled(self):
167 """Has share purshases."""
168 return self._attrs.get("hasSharePurchasesEnabled")
170 @property
171 def share_my_location_enabled_family_members(self):
172 """Has share my location with family."""
173 return self._attrs.get("shareMyLocationEnabledFamilyMembers")
175 @property
176 def has_share_my_location_enabled(self):
177 """Has share my location."""
178 return self._attrs.get("hasShareMyLocationEnabled")
180 @property
181 def dsid_for_purchases(self):
182 """Gets the dsid for purchases."""
183 return self._attrs.get("dsidForPurchases")
185 def get_photo(self):
186 """Returns the photo."""
187 params_photo = dict(self._params)
188 params_photo.update({"memberId": self.dsid})
189 return self._session.get(
190 self._acc_family_member_photo_url,
191 params=params_photo,
192 stream=True,
193 )
195 def __getitem__(self, key):
196 if self._attrs.get(key):
197 return self._attrs[key]
198 return getattr(self, key)
200 def __unicode__(self):
201 return f"{{name: {self.full_name}, age_classification: {self.age_classification}}}"
203 def __str__(self):
204 return self.__unicode__()
206 def __repr__(self):
207 return f"<{type(self).__name__}: {str(self)}>"
210class AccountStorageUsageForMedia:
211 """Storage used for a specific media type into the account."""
213 def __init__(self, usage_data):
214 self.usage_data = usage_data
216 @property
217 def key(self):
218 """Gets the key."""
219 return self.usage_data["mediaKey"]
221 @property
222 def label(self):
223 """Gets the label."""
224 return self.usage_data["displayLabel"]
226 @property
227 def color(self):
228 """Gets the HEX color."""
229 return self.usage_data["displayColor"]
231 @property
232 def usage_in_bytes(self):
233 """Gets the usage in bytes."""
234 return self.usage_data["usageInBytes"]
236 def __unicode__(self):
237 return f"{{key: {self.key}, usage: {self.usage_in_bytes} bytes}}"
239 def __str__(self):
240 return self.__unicode__()
242 def __repr__(self):
243 return f"<{type(self).__name__}: {str(self)}>"
246class AccountStorageUsage:
247 """Storage used for a specific media type into the account."""
249 def __init__(self, usage_data, quota_data):
250 self.usage_data = usage_data
251 self.quota_data = quota_data
253 @property
254 def comp_storage_in_bytes(self):
255 """Gets the comp storage in bytes."""
256 return self.usage_data["compStorageInBytes"]
258 @property
259 def used_storage_in_bytes(self):
260 """Gets the used storage in bytes."""
261 return self.usage_data["usedStorageInBytes"]
263 @property
264 def used_storage_in_percent(self):
265 """Gets the used storage in percent."""
266 return round(self.used_storage_in_bytes * 100 / self.total_storage_in_bytes, 2)
268 @property
269 def available_storage_in_bytes(self):
270 """Gets the available storage in bytes."""
271 return self.total_storage_in_bytes - self.used_storage_in_bytes
273 @property
274 def available_storage_in_percent(self):
275 """Gets the available storage in percent."""
276 return round(
277 self.available_storage_in_bytes * 100 / self.total_storage_in_bytes,
278 2,
279 )
281 @property
282 def total_storage_in_bytes(self):
283 """Gets the total storage in bytes."""
284 return self.usage_data["totalStorageInBytes"]
286 @property
287 def commerce_storage_in_bytes(self):
288 """Gets the commerce storage in bytes."""
289 return self.usage_data["commerceStorageInBytes"]
291 @property
292 def quota_over(self):
293 """Gets the over quota."""
294 return self.quota_data["overQuota"]
296 @property
297 def quota_tier_max(self):
298 """Gets the max tier quota."""
299 return self.quota_data["haveMaxQuotaTier"]
301 @property
302 def quota_almost_full(self):
303 """Gets the almost full quota."""
304 return self.quota_data["almost-full"]
306 @property
307 def quota_paid(self):
308 """Gets the paid quota."""
309 return self.quota_data["paidQuota"]
311 def __unicode__(self):
312 return f"{self.used_storage_in_percent}% used of {self.total_storage_in_bytes} bytes"
314 def __str__(self):
315 return self.__unicode__()
317 def __repr__(self):
318 return f"<{type(self).__name__}: {str(self)}>"
321class AccountStorage:
322 """Storage of the account."""
324 def __init__(self, storage_data):
325 self.usage = AccountStorageUsage(
326 storage_data.get("storageUsageInfo"),
327 storage_data.get("quotaStatus"),
328 )
329 self.usages_by_media = OrderedDict()
331 for usage_media in storage_data.get("storageUsageByMedia"):
332 self.usages_by_media[usage_media["mediaKey"]] = AccountStorageUsageForMedia(
333 usage_media,
334 )
336 def __unicode__(self):
337 return f"{{usage: {self.usage}, usages_by_media: {self.usages_by_media}}}"
339 def __str__(self):
340 return self.__unicode__()
342 def __repr__(self):
343 return f"<{type(self).__name__}: {str(self)}>"