Coverage for icloudpy/services/account.py: 95%
198 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-02 05:49 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-02 05:49 +0000
1"""Account service."""
2from collections import OrderedDict
4from icloudpy.utils import underscore_to_camelcase
7class AccountService:
8 """The 'Account' iCloud service."""
10 def __init__(self, service_root, session, params):
11 self.session = session
12 self.params = params
13 self._service_root = service_root
15 self._devices = []
16 self._family = []
17 self._storage = None
19 self._acc_endpoint = f"{self._service_root}/setup/web"
20 self._acc_devices_url = f"{self._acc_endpoint}/device/getDevices"
21 self._acc_family_details_url = f"{self._acc_endpoint}/family/getFamilyDetails"
22 self._acc_family_member_photo_url = (
23 f"{self._acc_endpoint}/family/getMemberPhoto"
24 )
26 self._acc_storage_url = "https://setup.icloud.com/setup/ws/1/storageUsageInfo"
28 @property
29 def devices(self):
30 """Returns current paired devices."""
31 if not self._devices:
32 req = self.session.get(self._acc_devices_url, params=self.params)
33 response = req.json()
35 for device_info in response["devices"]:
36 self._devices.append(AccountDevice(device_info))
38 return self._devices
40 @property
41 def family(self):
42 """Returns family members."""
43 if not self._family:
44 req = self.session.get(self._acc_family_details_url, params=self.params)
45 response = req.json()
47 for member_info in response["familyMembers"]:
48 self._family.append(
49 FamilyMember(
50 member_info,
51 self.session,
52 self.params,
53 self._acc_family_member_photo_url,
54 ),
55 )
57 return self._family
59 @property
60 def storage(self):
61 """Returns storage infos."""
62 if not self._storage:
63 req = self.session.get(self._acc_storage_url, params=self.params)
64 response = req.json()
66 self._storage = AccountStorage(response)
68 return self._storage
70 def __unicode__(self):
71 storage_available = self.storage.usage.available_storage_in_bytes
72 return f"{{devices: {len(self.devices)}, family: {len(self.family)}, storage: {storage_available} bytes free}}"
74 def __str__(self):
75 return self.__unicode__()
77 def __repr__(self):
78 return f"<{type(self).__name__}: {str(self)}>"
81class AccountDevice(dict):
82 """Account device."""
84 def __getattr__(self, key):
85 return self[underscore_to_camelcase(key)]
87 def __unicode__(self):
88 return f"{{model: {self.model_display_name}, name: {self.name}}}"
90 def __str__(self):
91 return self.__unicode__()
93 def __repr__(self):
94 return f"<{type(self).__name__}: {str(self)}>"
97class FamilyMember:
98 """A family member."""
100 def __init__(self, member_info, session, params, acc_family_member_photo_url):
101 self._attrs = member_info
102 self._session = session
103 self._params = params
104 self._acc_family_member_photo_url = acc_family_member_photo_url
106 @property
107 def last_name(self):
108 """Gets the last name."""
109 return self._attrs.get("lastName")
111 @property
112 def dsid(self):
113 """Gets the dsid."""
114 return self._attrs.get("dsid")
116 @property
117 def original_invitation_email(self):
118 """Gets the original invitation."""
119 return self._attrs.get("originalInvitationEmail")
121 @property
122 def full_name(self):
123 """Gets the full name."""
124 return self._attrs.get("fullName")
126 @property
127 def age_classification(self):
128 """Gets the age classification."""
129 return self._attrs.get("ageClassification")
131 @property
132 def apple_id_for_purchases(self):
133 """Gets the apple id for purchases."""
134 return self._attrs.get("appleIdForPurchases")
136 @property
137 def apple_id(self):
138 """Gets the apple id."""
139 return self._attrs.get("appleId")
141 @property
142 def family_id(self):
143 """Gets the family id."""
144 return self._attrs.get("familyId")
146 @property
147 def first_name(self):
148 """Gets the first name."""
149 return self._attrs.get("firstName")
151 @property
152 def has_parental_privileges(self):
153 """Has parental privileges."""
154 return self._attrs.get("hasParentalPrivileges")
156 @property
157 def has_screen_time_enabled(self):
158 """Has screen time enabled."""
159 return self._attrs.get("hasScreenTimeEnabled")
161 @property
162 def has_ask_to_buy_enabled(self):
163 """Has to ask for buying."""
164 return self._attrs.get("hasAskToBuyEnabled")
166 @property
167 def has_share_purchases_enabled(self):
168 """Has share purshases."""
169 return self._attrs.get("hasSharePurchasesEnabled")
171 @property
172 def share_my_location_enabled_family_members(self):
173 """Has share my location with family."""
174 return self._attrs.get("shareMyLocationEnabledFamilyMembers")
176 @property
177 def has_share_my_location_enabled(self):
178 """Has share my location."""
179 return self._attrs.get("hasShareMyLocationEnabled")
181 @property
182 def dsid_for_purchases(self):
183 """Gets the dsid for purchases."""
184 return self._attrs.get("dsidForPurchases")
186 def get_photo(self):
187 """Returns the photo."""
188 params_photo = dict(self._params)
189 params_photo.update({"memberId": self.dsid})
190 return self._session.get(
191 self._acc_family_member_photo_url, params=params_photo, stream=True,
192 )
194 def __getitem__(self, key):
195 if self._attrs.get(key):
196 return self._attrs[key]
197 return getattr(self, key)
199 def __unicode__(self):
200 return (
201 f"{{name: {self.full_name}, age_classification: {self.age_classification}}}"
202 )
204 def __str__(self):
205 return self.__unicode__()
207 def __repr__(self):
208 return f"<{type(self).__name__}: {str(self)}>"
211class AccountStorageUsageForMedia:
212 """Storage used for a specific media type into the account."""
214 def __init__(self, usage_data):
215 self.usage_data = usage_data
217 @property
218 def key(self):
219 """Gets the key."""
220 return self.usage_data["mediaKey"]
222 @property
223 def label(self):
224 """Gets the label."""
225 return self.usage_data["displayLabel"]
227 @property
228 def color(self):
229 """Gets the HEX color."""
230 return self.usage_data["displayColor"]
232 @property
233 def usage_in_bytes(self):
234 """Gets the usage in bytes."""
235 return self.usage_data["usageInBytes"]
237 def __unicode__(self):
238 return f"{{key: {self.key}, usage: {self.usage_in_bytes} bytes}}"
240 def __str__(self):
241 return self.__unicode__()
243 def __repr__(self):
244 return f"<{type(self).__name__}: {str(self)}>"
247class AccountStorageUsage:
248 """Storage used for a specific media type into the account."""
250 def __init__(self, usage_data, quota_data):
251 self.usage_data = usage_data
252 self.quota_data = quota_data
254 @property
255 def comp_storage_in_bytes(self):
256 """Gets the comp storage in bytes."""
257 return self.usage_data["compStorageInBytes"]
259 @property
260 def used_storage_in_bytes(self):
261 """Gets the used storage in bytes."""
262 return self.usage_data["usedStorageInBytes"]
264 @property
265 def used_storage_in_percent(self):
266 """Gets the used storage in percent."""
267 return round(self.used_storage_in_bytes * 100 / self.total_storage_in_bytes, 2)
269 @property
270 def available_storage_in_bytes(self):
271 """Gets the available storage in bytes."""
272 return self.total_storage_in_bytes - self.used_storage_in_bytes
274 @property
275 def available_storage_in_percent(self):
276 """Gets the available storage in percent."""
277 return round(
278 self.available_storage_in_bytes * 100 / self.total_storage_in_bytes, 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"), storage_data.get("quotaStatus"),
327 )
328 self.usages_by_media = OrderedDict()
330 for usage_media in storage_data.get("storageUsageByMedia"):
331 self.usages_by_media[usage_media["mediaKey"]] = AccountStorageUsageForMedia(
332 usage_media,
333 )
335 def __unicode__(self):
336 return f"{{usage: {self.usage}, usages_by_media: {self.usages_by_media}}}"
338 def __str__(self):
339 return self.__unicode__()
341 def __repr__(self):
342 return f"<{type(self).__name__}: {str(self)}>"