pypetkitapi 1.7.5__py3-none-any.whl → 1.7.8__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 +27 -13
- pypetkitapi/command.py +21 -1
- pypetkitapi/const.py +6 -1
- pypetkitapi/exceptions.py +5 -1
- pypetkitapi/feeder_container.py +10 -3
- pypetkitapi/medias.py +2 -1
- {pypetkitapi-1.7.5.dist-info → pypetkitapi-1.7.8.dist-info}/METADATA +1 -1
- pypetkitapi-1.7.8.dist-info/RECORD +15 -0
- pypetkitapi-1.7.5.dist-info/RECORD +0 -15
- {pypetkitapi-1.7.5.dist-info → pypetkitapi-1.7.8.dist-info}/LICENSE +0 -0
- {pypetkitapi-1.7.5.dist-info → pypetkitapi-1.7.8.dist-info}/WHEEL +0 -0
pypetkitapi/client.py
CHANGED
@@ -30,9 +30,11 @@ from pypetkitapi.const import (
|
|
30
30
|
from pypetkitapi.containers import AccountData, Device, Pet, RegionInfo, SessionInfo
|
31
31
|
from pypetkitapi.exceptions import (
|
32
32
|
PetkitAuthenticationError,
|
33
|
+
PetkitAuthenticationUnregisteredEmailError,
|
33
34
|
PetkitInvalidHTTPResponseCodeError,
|
34
35
|
PetkitInvalidResponseFormat,
|
35
36
|
PetkitRegionalServerNotFoundError,
|
37
|
+
PetkitSessionExpiredError,
|
36
38
|
PetkitTimeoutError,
|
37
39
|
PypetkitError,
|
38
40
|
)
|
@@ -87,6 +89,7 @@ class PetKitClient:
|
|
87
89
|
for region in response.get("list", []):
|
88
90
|
server = RegionInfo(**region)
|
89
91
|
if server.name.lower() == self.region or server.id.lower() == self.region:
|
92
|
+
self.region = server.id.lower()
|
90
93
|
self.req.base_url = server.gateway
|
91
94
|
_LOGGER.debug("Found matching server: %s", server)
|
92
95
|
return
|
@@ -141,6 +144,8 @@ class PetKitClient:
|
|
141
144
|
response = await self.req.request(
|
142
145
|
method=HTTPMethod.POST,
|
143
146
|
url=PetkitEndpoint.REFRESH_SESSION,
|
147
|
+
data=LOGIN_DATA,
|
148
|
+
headers=await self.get_session_id(),
|
144
149
|
)
|
145
150
|
session_data = response["session"]
|
146
151
|
self._session = SessionInfo(**session_data)
|
@@ -148,6 +153,7 @@ class PetKitClient:
|
|
148
153
|
async def validate_session(self) -> None:
|
149
154
|
"""Check if the session is still valid and refresh or re-login if necessary."""
|
150
155
|
if self._session is None:
|
156
|
+
_LOGGER.debug("No token, logging in")
|
151
157
|
await self.login()
|
152
158
|
return
|
153
159
|
|
@@ -192,6 +198,8 @@ class PetKitClient:
|
|
192
198
|
|
193
199
|
async def get_devices_data(self) -> None:
|
194
200
|
"""Get the devices data from the PetKit servers."""
|
201
|
+
await self.validate_session()
|
202
|
+
|
195
203
|
start_time = datetime.now()
|
196
204
|
if not self.account_data:
|
197
205
|
await self._get_account_data()
|
@@ -199,7 +207,6 @@ class PetKitClient:
|
|
199
207
|
main_tasks = []
|
200
208
|
record_tasks = []
|
201
209
|
device_list: list[Device] = []
|
202
|
-
stats_tasks = []
|
203
210
|
|
204
211
|
for account in self.account_data:
|
205
212
|
_LOGGER.debug("List devices data for account: %s", account)
|
@@ -265,8 +272,6 @@ class PetKitClient:
|
|
265
272
|
],
|
266
273
|
) -> None:
|
267
274
|
"""Fetch the device data from the PetKit servers."""
|
268
|
-
await self.validate_session()
|
269
|
-
|
270
275
|
device_type = device.device_type.lower()
|
271
276
|
|
272
277
|
_LOGGER.debug("Reading device type : %s (id=%s)", device_type, device.device_id)
|
@@ -396,6 +401,8 @@ class PetKitClient:
|
|
396
401
|
setting: dict | None = None,
|
397
402
|
) -> bool:
|
398
403
|
"""Control the device using the PetKit API."""
|
404
|
+
await self.validate_session()
|
405
|
+
|
399
406
|
device = self.petkit_entities.get(device_id)
|
400
407
|
if not device:
|
401
408
|
raise PypetkitError(f"Device with ID {device_id} not found.")
|
@@ -529,17 +536,24 @@ class PrepReq:
|
|
529
536
|
|
530
537
|
# Check for errors in the response
|
531
538
|
if ERR_KEY in response_json:
|
539
|
+
error_code = int(response_json[ERR_KEY].get("code", 0))
|
532
540
|
error_msg = response_json[ERR_KEY].get("msg", "Unknown error")
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
541
|
+
|
542
|
+
match error_code:
|
543
|
+
case 5:
|
544
|
+
raise PetkitSessionExpiredError(f"Session expired: {error_msg}")
|
545
|
+
case 122:
|
546
|
+
raise PetkitAuthenticationError(
|
547
|
+
f"Authentication failed: {error_msg}"
|
548
|
+
)
|
549
|
+
case 125:
|
550
|
+
raise PetkitAuthenticationUnregisteredEmailError(
|
551
|
+
f"Authentication failed: {error_msg}"
|
552
|
+
)
|
553
|
+
case _:
|
554
|
+
raise PypetkitError(
|
555
|
+
f"Request failed code : {error_code} details : {error_msg}"
|
556
|
+
)
|
543
557
|
|
544
558
|
# Check for success in the response
|
545
559
|
if RES_KEY in response_json:
|
pypetkitapi/command.py
CHANGED
@@ -39,7 +39,9 @@ class FeederCommand(StrEnum):
|
|
39
39
|
MANUAL_FEED_DUAL = "manual_feed_dual"
|
40
40
|
CANCEL_MANUAL_FEED = "cancelRealtimeFeed"
|
41
41
|
FOOD_REPLENISHED = "food_replenished"
|
42
|
-
RESET_DESICCANT = "
|
42
|
+
RESET_DESICCANT = "desiccant_reset"
|
43
|
+
REMOVE_DAILY_FEED = "remove_daily_feed"
|
44
|
+
RESTORE_DAILY_FEED = "restore_daily_feed"
|
43
45
|
|
44
46
|
|
45
47
|
class LitterCommand(StrEnum):
|
@@ -160,6 +162,24 @@ ACTIONS_MAP = {
|
|
160
162
|
},
|
161
163
|
supported_device=ALL_DEVICES,
|
162
164
|
),
|
165
|
+
FeederCommand.REMOVE_DAILY_FEED: CmdData(
|
166
|
+
endpoint=PetkitEndpoint.REMOVE_DAILY_FEED,
|
167
|
+
params=lambda device, setting: {
|
168
|
+
"deviceId": device.id,
|
169
|
+
"day": datetime.datetime.now().strftime("%Y%m%d"),
|
170
|
+
**setting, # Need the id of the feed to remove
|
171
|
+
},
|
172
|
+
supported_device=DEVICES_FEEDER,
|
173
|
+
),
|
174
|
+
FeederCommand.RESTORE_DAILY_FEED: CmdData(
|
175
|
+
endpoint=PetkitEndpoint.RESTORE_DAILY_FEED,
|
176
|
+
params=lambda device, setting: {
|
177
|
+
"deviceId": device.id,
|
178
|
+
"day": datetime.datetime.now().strftime("%Y%m%d"),
|
179
|
+
**setting, # Need the id of the feed to restore
|
180
|
+
},
|
181
|
+
supported_device=DEVICES_FEEDER,
|
182
|
+
),
|
163
183
|
FeederCommand.MANUAL_FEED: CmdData(
|
164
184
|
endpoint=lambda device: get_endpoint_manual_feed(device),
|
165
185
|
params=lambda device, setting: {
|
pypetkitapi/const.py
CHANGED
@@ -131,4 +131,9 @@ class PetkitEndpoint(StrEnum):
|
|
131
131
|
MANUAL_FEED_MINI = "feedermini/save_dailyfeed"
|
132
132
|
MANUAL_FEED_FRESH_ELEMENT = "feeder/save_dailyfeed"
|
133
133
|
MANUAL_FEED_DUAL = "saveDailyFeed"
|
134
|
-
DAILY_FEED_AND_EAT = "dailyFeedAndEat"
|
134
|
+
DAILY_FEED_AND_EAT = "dailyFeedAndEat" # D3
|
135
|
+
FEED_STATISTIC = "feedStatistic" # D4
|
136
|
+
DAILY_FEED = "dailyFeeds" # D4S
|
137
|
+
REMOVE_DAILY_FEED = "removeDailyFeed"
|
138
|
+
RESTORE_DAILY_FEED = "restoreDailyFeed"
|
139
|
+
SAVE_FEED = "saveFeed" # For Feeding plan
|
pypetkitapi/exceptions.py
CHANGED
@@ -11,10 +11,14 @@ class PetkitTimeoutError(PypetkitError):
|
|
11
11
|
"""Class for PyPetkit timeout exceptions."""
|
12
12
|
|
13
13
|
|
14
|
-
class
|
14
|
+
class PetkitSessionExpiredError(PypetkitError):
|
15
15
|
"""Class for PyPetkit connection exceptions."""
|
16
16
|
|
17
17
|
|
18
|
+
class PetkitAuthenticationUnregisteredEmailError(PypetkitError):
|
19
|
+
"""Exception raised when the email is not registered with Petkit."""
|
20
|
+
|
21
|
+
|
18
22
|
class PetkitRegionalServerNotFoundError(PypetkitError):
|
19
23
|
"""Exception raised when the specified region server is not found."""
|
20
24
|
|
pypetkitapi/feeder_container.py
CHANGED
@@ -5,7 +5,7 @@ from typing import Any, ClassVar
|
|
5
5
|
|
6
6
|
from pydantic import BaseModel, Field
|
7
7
|
|
8
|
-
from pypetkitapi.const import D3, DEVICE_DATA, DEVICE_RECORDS, PetkitEndpoint
|
8
|
+
from pypetkitapi.const import D3, D4, D4S, DEVICE_DATA, DEVICE_RECORDS, PetkitEndpoint
|
9
9
|
from pypetkitapi.containers import CloudProduct, Device, FirmwareDetail, Wifi
|
10
10
|
|
11
11
|
|
@@ -271,6 +271,10 @@ class FeederRecord(BaseModel):
|
|
271
271
|
"""Get the endpoint URL for the given device type."""
|
272
272
|
if device_type == D3:
|
273
273
|
return PetkitEndpoint.DAILY_FEED_AND_EAT
|
274
|
+
if device_type == D4:
|
275
|
+
return PetkitEndpoint.FEED_STATISTIC
|
276
|
+
if device_type == D4S:
|
277
|
+
return PetkitEndpoint.DAILY_FEED
|
274
278
|
return PetkitEndpoint.GET_DEVICE_RECORD
|
275
279
|
|
276
280
|
@classmethod
|
@@ -283,7 +287,10 @@ class FeederRecord(BaseModel):
|
|
283
287
|
"""Generate query parameters including request_date."""
|
284
288
|
if request_date is None:
|
285
289
|
request_date = datetime.now().strftime("%Y%m%d")
|
286
|
-
|
290
|
+
|
291
|
+
if device.device_type.lower() == D4:
|
292
|
+
return {"date": request_date, "type": 0, "deviceId": device.device_id}
|
293
|
+
return {"days": request_date, "deviceId": device.device_id}
|
287
294
|
|
288
295
|
|
289
296
|
class Feeder(BaseModel):
|
@@ -296,7 +303,7 @@ class Feeder(BaseModel):
|
|
296
303
|
cloud_product: CloudProduct | None = Field(None, alias="cloudProduct")
|
297
304
|
created_at: str | None = Field(None, alias="createdAt")
|
298
305
|
firmware: float
|
299
|
-
firmware_details: list[FirmwareDetail] = Field(alias="firmwareDetails")
|
306
|
+
firmware_details: list[FirmwareDetail] | None = Field(None, alias="firmwareDetails")
|
300
307
|
hardware: int
|
301
308
|
id: int
|
302
309
|
locale: str | None = None
|
pypetkitapi/medias.py
CHANGED
@@ -194,5 +194,6 @@ class MediaDownloadDecode:
|
|
194
194
|
except Exception as e: # noqa: BLE001
|
195
195
|
logging.error("Error decrypting image from file %s: %s", file_path, e)
|
196
196
|
return None
|
197
|
-
Path(file_path).
|
197
|
+
if Path(file_path).exists():
|
198
|
+
Path(file_path).unlink()
|
198
199
|
return decrypted_data
|
@@ -0,0 +1,15 @@
|
|
1
|
+
pypetkitapi/__init__.py,sha256=eVpyGMD3tkYtiHUkdKEeNSZhQlZ4woI2Y5oVoV7CwXM,61
|
2
|
+
pypetkitapi/client.py,sha256=VsISt7-njR0eFlloJenBjMXqeHjnrXXJr-7wamNA3fk,20473
|
3
|
+
pypetkitapi/command.py,sha256=q_9B-_z74BicMKNTR46PpELhmqW1cUV8q-95D6riJy4,7959
|
4
|
+
pypetkitapi/const.py,sha256=4FiPflYNMtQpycPDhtk9V2MZG9AjPC_FNNO36C_8NVI,3698
|
5
|
+
pypetkitapi/containers.py,sha256=nhp50QwyoQRveTnEgWL7JFEY3Tl5m5wp9EqZvklW87Y,4338
|
6
|
+
pypetkitapi/exceptions.py,sha256=fuTLT6Iw2_kA7eOyNJPf59vQkgfByhAnTThY4lC0Rt0,1283
|
7
|
+
pypetkitapi/feeder_container.py,sha256=RvhOHG-CKAHunDx28iQRpam3r3rvvBcdjw1dw8fTlS0,14370
|
8
|
+
pypetkitapi/litter_container.py,sha256=wsM-v7ibx17lsD-o17RGz2wvcHCUu1iZ2RqAh3CN1Qc,18254
|
9
|
+
pypetkitapi/medias.py,sha256=IuWkC7usw0Hbx173X8TGv24jOp4nqv6bIUosZBpXMGg,6945
|
10
|
+
pypetkitapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
pypetkitapi/water_fountain_container.py,sha256=3F4GP5pXJqq6kxLMSK__GgFMuZ-rz1VDIIhaVd19Kl8,6781
|
12
|
+
pypetkitapi-1.7.8.dist-info/LICENSE,sha256=4FWnKolNLc1e3w6cVlT61YxfPh0DQNeQLN1CepKKSBg,1067
|
13
|
+
pypetkitapi-1.7.8.dist-info/METADATA,sha256=ECwzMWXEWub8VuUZLucFRzM-mXHoBlkiu62xeRfeT00,4852
|
14
|
+
pypetkitapi-1.7.8.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
15
|
+
pypetkitapi-1.7.8.dist-info/RECORD,,
|
@@ -1,15 +0,0 @@
|
|
1
|
-
pypetkitapi/__init__.py,sha256=eVpyGMD3tkYtiHUkdKEeNSZhQlZ4woI2Y5oVoV7CwXM,61
|
2
|
-
pypetkitapi/client.py,sha256=aBfwh6O-egpaX7vhW1I2l3flnXL_XrwGraH1uOzmH-w,19888
|
3
|
-
pypetkitapi/command.py,sha256=gw3_J_oZHuuGLk66P8uRSqSrySjYa8ArpKaPHi2ybCw,7155
|
4
|
-
pypetkitapi/const.py,sha256=ZpFWBgzb3nvy0Z4oyM550cCunlIceWgXqqtwtJc3mFo,3479
|
5
|
-
pypetkitapi/containers.py,sha256=nhp50QwyoQRveTnEgWL7JFEY3Tl5m5wp9EqZvklW87Y,4338
|
6
|
-
pypetkitapi/exceptions.py,sha256=NWmpsI2ewC4HaIeu_uFwCeuPIHIJxZBzjoCP7aNwvhs,1139
|
7
|
-
pypetkitapi/feeder_container.py,sha256=y1A5WhObXCdbcWwtKgSPzTWokYchoUDlholAD-AMgGQ,14069
|
8
|
-
pypetkitapi/litter_container.py,sha256=wsM-v7ibx17lsD-o17RGz2wvcHCUu1iZ2RqAh3CN1Qc,18254
|
9
|
-
pypetkitapi/medias.py,sha256=8hrMdzFR9d0L0PQOVYdy-MReSF9GMp8Ft0IGGHtL1Ag,6904
|
10
|
-
pypetkitapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
pypetkitapi/water_fountain_container.py,sha256=3F4GP5pXJqq6kxLMSK__GgFMuZ-rz1VDIIhaVd19Kl8,6781
|
12
|
-
pypetkitapi-1.7.5.dist-info/LICENSE,sha256=4FWnKolNLc1e3w6cVlT61YxfPh0DQNeQLN1CepKKSBg,1067
|
13
|
-
pypetkitapi-1.7.5.dist-info/METADATA,sha256=bvYlZOdBLw9_nu53rnNrPSXvgYdSvQMjqAVeSFMm7VE,4852
|
14
|
-
pypetkitapi-1.7.5.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
15
|
-
pypetkitapi-1.7.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|