pypetkitapi 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pypetkitapi/client.py +55 -12
- pypetkitapi/const.py +3 -3
- pypetkitapi/exceptions.py +25 -0
- {pypetkitapi-0.1.0.dist-info → pypetkitapi-0.1.2.dist-info}/METADATA +1 -1
- {pypetkitapi-0.1.0.dist-info → pypetkitapi-0.1.2.dist-info}/RECORD +7 -7
- {pypetkitapi-0.1.0.dist-info → pypetkitapi-0.1.2.dist-info}/LICENSE +0 -0
- {pypetkitapi-0.1.0.dist-info → pypetkitapi-0.1.2.dist-info}/WHEEL +0 -0
pypetkitapi/client.py
CHANGED
@@ -22,7 +22,14 @@ from pypetkitapi.const import (
|
|
22
22
|
PetkitURL,
|
23
23
|
)
|
24
24
|
from pypetkitapi.containers import AccountData, Device, RegionInfo, SessionInfo
|
25
|
-
from pypetkitapi.exceptions import
|
25
|
+
from pypetkitapi.exceptions import (
|
26
|
+
PetkitAuthenticationError,
|
27
|
+
PetkitInvalidHTTPResponseCodeError,
|
28
|
+
PetkitInvalidResponseFormat,
|
29
|
+
PetkitRegionalServerNotFoundError,
|
30
|
+
PetkitTimeoutError,
|
31
|
+
PypetkitError,
|
32
|
+
)
|
26
33
|
from pypetkitapi.feeder_container import Feeder
|
27
34
|
from pypetkitapi.litter_container import Litter
|
28
35
|
from pypetkitapi.water_fountain_container import WaterFountain
|
@@ -49,12 +56,13 @@ class PetKitClient:
|
|
49
56
|
"""Initialize the PetKit Client."""
|
50
57
|
self.username = username
|
51
58
|
self.password = password
|
52
|
-
self.region = region
|
59
|
+
self.region = region.lower()
|
53
60
|
self.timezone = timezone
|
54
61
|
|
55
62
|
async def _generate_header(self) -> dict[str, str]:
|
56
63
|
"""Create header for interaction with devices."""
|
57
64
|
session_id = self._session.id if self._session is not None else ""
|
65
|
+
|
58
66
|
return {
|
59
67
|
"Accept": Header.ACCEPT.value,
|
60
68
|
"Accept-Language": Header.ACCEPT_LANG,
|
@@ -92,7 +100,11 @@ class PetKitClient:
|
|
92
100
|
_LOGGER.debug("Finding region server for region: %s", self.region)
|
93
101
|
|
94
102
|
regional_server = next(
|
95
|
-
(
|
103
|
+
(
|
104
|
+
server
|
105
|
+
for server in self._servers_list
|
106
|
+
if server.name.lower() == self.region
|
107
|
+
),
|
96
108
|
None,
|
97
109
|
)
|
98
110
|
|
@@ -102,7 +114,7 @@ class PetKitClient:
|
|
102
114
|
)
|
103
115
|
self._base_url = regional_server.gateway
|
104
116
|
return
|
105
|
-
|
117
|
+
raise PetkitRegionalServerNotFoundError(self.region)
|
106
118
|
|
107
119
|
async def request_login_code(self) -> bool:
|
108
120
|
"""Request a login code to be sent to the user's email."""
|
@@ -334,14 +346,7 @@ class PrepReq:
|
|
334
346
|
data=data,
|
335
347
|
headers=_headers,
|
336
348
|
) as resp:
|
337
|
-
|
338
|
-
if ERR_KEY in response:
|
339
|
-
error_msg = response[ERR_KEY].get("msg", "Unknown error")
|
340
|
-
raise PypetkitError(f"Request failed: {error_msg}")
|
341
|
-
if RES_KEY in response:
|
342
|
-
_LOGGER.debug("Request response: %s", response)
|
343
|
-
return response[RES_KEY]
|
344
|
-
raise PypetkitError("Unexpected response format")
|
349
|
+
return await self._handle_response(resp, _url)
|
345
350
|
except ContentTypeError:
|
346
351
|
"""If we get an error, lets log everything for debugging."""
|
347
352
|
try:
|
@@ -353,3 +358,41 @@ class PrepReq:
|
|
353
358
|
_LOGGER.info("Resp raw: %s", resp_raw)
|
354
359
|
# Still raise the err so that it's clear it failed.
|
355
360
|
raise
|
361
|
+
except TimeoutError:
|
362
|
+
raise PetkitTimeoutError("The request timed out") from None
|
363
|
+
|
364
|
+
@staticmethod
|
365
|
+
async def _handle_response(response: aiohttp.ClientResponse, url: str) -> dict:
|
366
|
+
"""Handle the response from the PetKit API."""
|
367
|
+
|
368
|
+
try:
|
369
|
+
response.raise_for_status()
|
370
|
+
except aiohttp.ClientResponseError as e:
|
371
|
+
raise PetkitInvalidHTTPResponseCodeError(
|
372
|
+
f"Request failed with status code {e.status}"
|
373
|
+
) from e
|
374
|
+
|
375
|
+
try:
|
376
|
+
response_json = await response.json()
|
377
|
+
except ContentTypeError:
|
378
|
+
raise PetkitInvalidResponseFormat(
|
379
|
+
"Response is not in JSON format"
|
380
|
+
) from None
|
381
|
+
|
382
|
+
if ERR_KEY in response_json:
|
383
|
+
error_msg = response_json[ERR_KEY].get("msg", "Unknown error")
|
384
|
+
if any(
|
385
|
+
endpoint in url
|
386
|
+
for endpoint in [
|
387
|
+
PetkitEndpoint.LOGIN,
|
388
|
+
PetkitEndpoint.GET_LOGIN_CODE,
|
389
|
+
PetkitEndpoint.REFRESH_SESSION,
|
390
|
+
]
|
391
|
+
):
|
392
|
+
raise PetkitAuthenticationError(f"Login failed: {error_msg}")
|
393
|
+
raise PypetkitError(f"Request failed: {error_msg}")
|
394
|
+
|
395
|
+
if RES_KEY in response_json:
|
396
|
+
return response_json[RES_KEY]
|
397
|
+
|
398
|
+
raise PypetkitError("Unexpected response format")
|
pypetkitapi/const.py
CHANGED
@@ -57,7 +57,7 @@ class Header(StrEnum):
|
|
57
57
|
AGENT = "okhttp/3.12.11"
|
58
58
|
CLIENT = f"{Client.PLATFORM_TYPE}({Client.OS_VERSION};{Client.MODEL_NAME})"
|
59
59
|
TIMEZONE = "1.0"
|
60
|
-
TIMEZONE_ID = "Europe/Paris"
|
60
|
+
TIMEZONE_ID = "Europe/Paris" # TODO: Make this dynamic, check if this really matters (record hours?)
|
61
61
|
LOCALE = "en-US"
|
62
62
|
IMG_VERSION = "1.0"
|
63
63
|
HOUR = "24"
|
@@ -69,8 +69,8 @@ CLIENT_NFO = {
|
|
69
69
|
"osVersion": Client.OS_VERSION.value,
|
70
70
|
"platform": Client.PLATFORM_TYPE.value,
|
71
71
|
"source": Client.SOURCE.value,
|
72
|
-
"timezone": Header.TIMEZONE.value,
|
73
|
-
"timezoneId": Header.TIMEZONE_ID.value,
|
72
|
+
"timezone": Header.TIMEZONE.value, # TODO: Make this dynamic
|
73
|
+
"timezoneId": Header.TIMEZONE_ID.value, # TODO: Make this dynamic
|
74
74
|
"version": Header.API_VERSION.value,
|
75
75
|
}
|
76
76
|
|
pypetkitapi/exceptions.py
CHANGED
@@ -13,3 +13,28 @@ class PetkitTimeoutError(PypetkitError):
|
|
13
13
|
|
14
14
|
class PetkitConnectionError(PypetkitError):
|
15
15
|
"""Class for PyPetkit connection exceptions."""
|
16
|
+
|
17
|
+
|
18
|
+
class PetkitRegionalServerNotFoundError(PypetkitError):
|
19
|
+
"""Exception raised when the specified region server is not found."""
|
20
|
+
|
21
|
+
def __init__(self, region: str):
|
22
|
+
"""Initialize the exception."""
|
23
|
+
self.region = region
|
24
|
+
self.message = (
|
25
|
+
f"Region you provided: '{region}' was not found in the server list. "
|
26
|
+
f"Are you sure you provided the correct region?"
|
27
|
+
)
|
28
|
+
super().__init__(self.message)
|
29
|
+
|
30
|
+
|
31
|
+
class PetkitInvalidHTTPResponseCodeError(PypetkitError):
|
32
|
+
"""Class for PyPetkit invalid HTTP Response exceptions."""
|
33
|
+
|
34
|
+
|
35
|
+
class PetkitInvalidResponseFormat(PypetkitError):
|
36
|
+
"""Class for PyPetkit invalid Response Format exceptions."""
|
37
|
+
|
38
|
+
|
39
|
+
class PetkitAuthenticationError(PypetkitError):
|
40
|
+
"""Class for PyPetkit authentication exceptions."""
|
@@ -1,13 +1,13 @@
|
|
1
1
|
pypetkitapi/__init__.py,sha256=eVpyGMD3tkYtiHUkdKEeNSZhQlZ4woI2Y5oVoV7CwXM,61
|
2
|
-
pypetkitapi/client.py,sha256=
|
2
|
+
pypetkitapi/client.py,sha256=r_GJHi4gKuAnRAKwfro2G0xyq2aNRmaIfUTP6bWkdpE,13922
|
3
3
|
pypetkitapi/command.py,sha256=ibJ0zenONy-FBvUvyIA_IvUnYEUezLpPxhC_9XZ6SwM,9282
|
4
|
-
pypetkitapi/const.py,sha256=
|
4
|
+
pypetkitapi/const.py,sha256=49NMRaCGok20P-qfzlL6PH7QN9-QDuAsPh2Fejqf2iE,2945
|
5
5
|
pypetkitapi/containers.py,sha256=GQqZKaDgemQM4UDnugWYDP7N01anpJwO4VjQy2Gla3E,3109
|
6
|
-
pypetkitapi/exceptions.py,sha256=
|
6
|
+
pypetkitapi/exceptions.py,sha256=f9QY1EME9ha_vJJx4DuL_OBNpoynYVdtMFtVZbdfook,1129
|
7
7
|
pypetkitapi/feeder_container.py,sha256=5dCgXAHafl2kMk4jM4JGPgE0f48JBs0UkA1TvLPTfXU,10994
|
8
8
|
pypetkitapi/litter_container.py,sha256=tKYd7iaj88TtR0W-wJ6U8PmsUzCcWg4ZisblOTKjKSg,8957
|
9
9
|
pypetkitapi/water_fountain_container.py,sha256=g_J1nYuNTlDg-ltvo2FZRTxKWLgW9E0GVzbFKO6jpA8,5346
|
10
|
-
pypetkitapi-0.1.
|
11
|
-
pypetkitapi-0.1.
|
12
|
-
pypetkitapi-0.1.
|
13
|
-
pypetkitapi-0.1.
|
10
|
+
pypetkitapi-0.1.2.dist-info/LICENSE,sha256=4FWnKolNLc1e3w6cVlT61YxfPh0DQNeQLN1CepKKSBg,1067
|
11
|
+
pypetkitapi-0.1.2.dist-info/METADATA,sha256=uBFcyKIvZ2s9OqUbW3NuGabVbzglHMPVQgSf3KbmFy4,3700
|
12
|
+
pypetkitapi-0.1.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
13
|
+
pypetkitapi-0.1.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|