pypetkitapi 0.1.2__py3-none-any.whl → 0.2.1__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 CHANGED
@@ -1,5 +1,6 @@
1
1
  """Pypetkit Client: A Python library for interfacing with PetKit"""
2
2
 
3
+ import asyncio
3
4
  from datetime import datetime, timedelta
4
5
  from enum import StrEnum
5
6
  import hashlib
@@ -17,6 +18,7 @@ from pypetkitapi.const import (
17
18
  ERR_KEY,
18
19
  LOGIN_DATA,
19
20
  RES_KEY,
21
+ SUCCESS_KEY,
20
22
  Header,
21
23
  PetkitEndpoint,
22
24
  PetkitURL,
@@ -44,7 +46,7 @@ class PetKitClient:
44
46
  _session: SessionInfo | None = None
45
47
  _servers_list: list[RegionInfo] = []
46
48
  account_data: list[AccountData] = []
47
- device_list: list[Feeder | Litter | WaterFountain] = []
49
+ device_list: dict[int, Feeder | Litter | WaterFountain] = {}
48
50
 
49
51
  def __init__(
50
52
  self,
@@ -136,6 +138,8 @@ class PetKitClient:
136
138
  # Retrieve the list of servers
137
139
  await self._get_base_url()
138
140
 
141
+ _LOGGER.debug("Logging in to PetKit server")
142
+
139
143
  # Prepare the data to send
140
144
  data = LOGIN_DATA.copy()
141
145
  data["encrypt"] = "1"
@@ -210,6 +214,7 @@ class PetKitClient:
210
214
 
211
215
  async def get_devices_data(self) -> None:
212
216
  """Get the devices data from the PetKit servers."""
217
+ start_time = datetime.now()
213
218
  if not self.account_data:
214
219
  await self._get_account_data()
215
220
 
@@ -219,19 +224,25 @@ class PetKitClient:
219
224
  if account.device_list:
220
225
  device_list.extend(account.device_list)
221
226
 
222
- _LOGGER.info("%s devices found for this account", len(device_list))
227
+ _LOGGER.debug("Fetch %s devices for this account", len(device_list))
228
+
229
+ tasks = []
223
230
  for device in device_list:
224
231
  _LOGGER.debug("Fetching devices data: %s", device)
225
232
  device_type = device.device_type.lower()
226
- # TODO: Fetch device records
227
233
  if device_type in DEVICES_FEEDER:
228
- await self._fetch_device_data(device, Feeder)
234
+ tasks.append(self._fetch_device_data(device, Feeder))
229
235
  elif device_type in DEVICES_LITTER_BOX:
230
- await self._fetch_device_data(device, Litter)
236
+ tasks.append(self._fetch_device_data(device, Litter))
231
237
  elif device_type in DEVICES_WATER_FOUNTAIN:
232
- await self._fetch_device_data(device, WaterFountain)
238
+ tasks.append(self._fetch_device_data(device, WaterFountain))
233
239
  else:
234
240
  _LOGGER.warning("Unknown device type: %s", device_type)
241
+ await asyncio.gather(*tasks)
242
+
243
+ end_time = datetime.now()
244
+ total_time = end_time - start_time
245
+ _LOGGER.debug("Petkit fetch took : %s", total_time)
235
246
 
236
247
  async def _fetch_device_data(
237
248
  self,
@@ -253,22 +264,28 @@ class PetKitClient:
253
264
  )
254
265
  device_data = data_class(**response)
255
266
  device_data.device_type = device.device_type # Add the device_type attribute
256
- _LOGGER.info("Adding device type: %s", device.device_type)
257
- self.device_list.append(device_data)
267
+ _LOGGER.debug(
268
+ "Reading device type : %s (id=%s)", device.device_type, device.device_id
269
+ )
270
+ self.device_list[device.device_id] = device_data
258
271
 
259
272
  async def send_api_request(
260
273
  self,
261
- device: Feeder | Litter | WaterFountain,
274
+ device_id: int,
262
275
  action: StrEnum,
263
276
  setting: dict | None = None,
264
277
  ) -> None:
265
278
  """Control the device using the PetKit API."""
279
+ device = self.device_list.get(device_id)
280
+ if not device:
281
+ raise PypetkitError(f"Device with ID {device_id} not found.")
266
282
 
267
283
  _LOGGER.debug(
268
- "Control API: %s %s %s",
284
+ "Control API device=%s id=%s action=%s param=%s",
285
+ device.device_type,
286
+ device_id,
269
287
  action,
270
288
  setting,
271
- device,
272
289
  )
273
290
 
274
291
  if device.device_type:
@@ -302,12 +319,16 @@ class PetKitClient:
302
319
  params = action_info.params(device)
303
320
 
304
321
  prep_req = PrepReq(base_url=self._base_url)
305
- await prep_req.request(
322
+ res = await prep_req.request(
306
323
  method=HTTPMethod.POST,
307
324
  url=url,
308
325
  data=params,
309
326
  headers=headers,
310
327
  )
328
+ if res == SUCCESS_KEY:
329
+ _LOGGER.info("Command executed successfully")
330
+ else:
331
+ _LOGGER.error("Command execution failed")
311
332
 
312
333
 
313
334
  class PrepReq:
@@ -141,6 +141,7 @@ class StateFeeder(BaseModel):
141
141
  door: int | None = None
142
142
  feed_state: FeedState | None = Field(None, alias="feedState")
143
143
  feeding: int | None = None
144
+ error_msg: str | None = Field(None, alias="errorMsg")
144
145
  ota: int | None = None
145
146
  overall: int | None = None
146
147
  pim: int | None = None
@@ -163,10 +164,10 @@ class Feeder(BaseModel):
163
164
  bt_mac: str | None = Field(None, alias="btMac")
164
165
  cloud_product: CloudProduct | None = Field(None, alias="cloudProduct")
165
166
  created_at: str | None = Field(None, alias="createdAt")
166
- firmware: str | None = None
167
- firmware_details: list[FirmwareDetail] | None = Field(None, alias="firmwareDetails")
168
- hardware: int | None = None
169
- id: int | None = None
167
+ firmware: float
168
+ firmware_details: list[FirmwareDetail] = Field(alias="firmwareDetails")
169
+ hardware: int
170
+ id: int
170
171
  locale: str | None = None
171
172
  mac: str | None = None
172
173
  model_code: int | None = Field(None, alias="modelCode")
@@ -177,7 +178,7 @@ class Feeder(BaseModel):
177
178
  settings: SettingsFeeder | None = None
178
179
  share_open: int | None = Field(None, alias="shareOpen")
179
180
  signup_at: str | None = Field(None, alias="signupAt")
180
- sn: str | None = None
181
+ sn: str
181
182
  state: StateFeeder | None = None
182
183
  timezone: float | None = None
183
184
  p2p_type: int | None = Field(None, alias="p2pType")
@@ -91,6 +91,7 @@ class StateLitter(BaseModel):
91
91
  box_full: bool | None = Field(None, alias="boxFull")
92
92
  box_state: int | None = Field(None, alias="boxState")
93
93
  deodorant_left_days: int | None = Field(None, alias="deodorantLeftDays")
94
+ error_msg: str | None = Field(None, alias="errorMsg")
94
95
  frequent_restroom: int | None = Field(None, alias="frequentRestroom")
95
96
  liquid_empty: bool | None = Field(None, alias="liquidEmpty")
96
97
  liquid_lack: bool | None = Field(None, alias="liquidLack")
@@ -140,10 +141,10 @@ class Litter(BaseModel):
140
141
  auto_upgrade: int | None = Field(None, alias="autoUpgrade")
141
142
  bt_mac: str | None = Field(None, alias="btMac")
142
143
  created_at: str | None = Field(None, alias="createdAt")
143
- firmware: str | None = None
144
- firmware_details: list[FirmwareDetail] | None = Field(None, alias="firmwareDetails")
145
- hardware: int | None = None
146
- id: int | None = None
144
+ firmware: float
145
+ firmware_details: list[FirmwareDetail] = Field(alias="firmwareDetails")
146
+ hardware: int
147
+ id: int
147
148
  is_pet_out_tips: int | None = Field(None, alias="isPetOutTips")
148
149
  locale: str | None = None
149
150
  mac: str | None = None
@@ -156,7 +157,7 @@ class Litter(BaseModel):
156
157
  settings: SettingsLitter | None = None
157
158
  share_open: int | None = Field(None, alias="shareOpen")
158
159
  signup_at: str | None = Field(None, alias="signupAt")
159
- sn: str | None = None
160
+ sn: str
160
161
  state: StateLitter | None = None
161
162
  timezone: float | None = None
162
163
  cloud_product: CloudProduct | None = Field(None, alias="cloudProduct") # For T5/T6
@@ -100,9 +100,9 @@ class WaterFountain(BaseModel):
100
100
  filter_expected_days: int | None = Field(None, alias="filterExpectedDays")
101
101
  filter_percent: int | None = Field(None, alias="filterPercent")
102
102
  filter_warning: int | None = Field(None, alias="filterWarning")
103
- firmware: int | None = None
104
- hardware: int | None = None
105
- id: int | None = None
103
+ firmware: float
104
+ hardware: int
105
+ id: int
106
106
  is_night_no_disturbing: int | None = Field(None, alias="isNightNoDisturbing")
107
107
  lack_warning: int | None = Field(None, alias="lackWarning")
108
108
  locale: str | None = None
@@ -110,14 +110,14 @@ class WaterFountain(BaseModel):
110
110
  mac: str | None = None
111
111
  mode: int | None = None
112
112
  module_status: int | None = Field(None, alias="moduleStatus")
113
- name: str | None = None
113
+ name: str
114
114
  record_automatic_add_water: int | None = Field(
115
115
  None, alias="recordAutomaticAddWater"
116
116
  )
117
117
  schedule: Schedule | None = None
118
118
  secret: str | None = None
119
119
  settings: SettingsFountain | None = None
120
- sn: str | None = None
120
+ sn: str
121
121
  status: Status | None = None
122
122
  sync_time: str | None = Field(None, alias="syncTime")
123
123
  timezone: float | None = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pypetkitapi
3
- Version: 0.1.2
3
+ Version: 0.2.1
4
4
  Summary: Python client for PetKit API
5
5
  Home-page: https://github.com/Jezza34000/pypetkit
6
6
  License: MIT
@@ -0,0 +1,13 @@
1
+ pypetkitapi/__init__.py,sha256=eVpyGMD3tkYtiHUkdKEeNSZhQlZ4woI2Y5oVoV7CwXM,61
2
+ pypetkitapi/client.py,sha256=xfdhNAW3jsq9xQWgnEZTc2fAH7z4GHA3jtZlwDlmgCc,14635
3
+ pypetkitapi/command.py,sha256=ibJ0zenONy-FBvUvyIA_IvUnYEUezLpPxhC_9XZ6SwM,9282
4
+ pypetkitapi/const.py,sha256=49NMRaCGok20P-qfzlL6PH7QN9-QDuAsPh2Fejqf2iE,2945
5
+ pypetkitapi/containers.py,sha256=GQqZKaDgemQM4UDnugWYDP7N01anpJwO4VjQy2Gla3E,3109
6
+ pypetkitapi/exceptions.py,sha256=f9QY1EME9ha_vJJx4DuL_OBNpoynYVdtMFtVZbdfook,1129
7
+ pypetkitapi/feeder_container.py,sha256=IGXeAEbLkZhOTCzF2MhwqadIAKXg6jHbfU037dt6byY,10985
8
+ pypetkitapi/litter_container.py,sha256=aLAvcB8K_nx7iBRkAarZs-48HAj2NkG7XjJFonWMuME,8948
9
+ pypetkitapi/water_fountain_container.py,sha256=LcCTDjk7eSbnF7e38xev3D5mCv5wwJ6go8WGGBv-CaU,5278
10
+ pypetkitapi-0.2.1.dist-info/LICENSE,sha256=4FWnKolNLc1e3w6cVlT61YxfPh0DQNeQLN1CepKKSBg,1067
11
+ pypetkitapi-0.2.1.dist-info/METADATA,sha256=mb2SmQx9J0Vx-N5ukYHAlUodTLrU0M1TYc0psdIb57E,3700
12
+ pypetkitapi-0.2.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
13
+ pypetkitapi-0.2.1.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- pypetkitapi/__init__.py,sha256=eVpyGMD3tkYtiHUkdKEeNSZhQlZ4woI2Y5oVoV7CwXM,61
2
- pypetkitapi/client.py,sha256=r_GJHi4gKuAnRAKwfro2G0xyq2aNRmaIfUTP6bWkdpE,13922
3
- pypetkitapi/command.py,sha256=ibJ0zenONy-FBvUvyIA_IvUnYEUezLpPxhC_9XZ6SwM,9282
4
- pypetkitapi/const.py,sha256=49NMRaCGok20P-qfzlL6PH7QN9-QDuAsPh2Fejqf2iE,2945
5
- pypetkitapi/containers.py,sha256=GQqZKaDgemQM4UDnugWYDP7N01anpJwO4VjQy2Gla3E,3109
6
- pypetkitapi/exceptions.py,sha256=f9QY1EME9ha_vJJx4DuL_OBNpoynYVdtMFtVZbdfook,1129
7
- pypetkitapi/feeder_container.py,sha256=5dCgXAHafl2kMk4jM4JGPgE0f48JBs0UkA1TvLPTfXU,10994
8
- pypetkitapi/litter_container.py,sha256=tKYd7iaj88TtR0W-wJ6U8PmsUzCcWg4ZisblOTKjKSg,8957
9
- pypetkitapi/water_fountain_container.py,sha256=g_J1nYuNTlDg-ltvo2FZRTxKWLgW9E0GVzbFKO6jpA8,5346
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,,