pynintendoparental 1.0.2__py3-none-any.whl → 1.0.4__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.
@@ -3,6 +3,8 @@
3
3
 
4
4
  import asyncio
5
5
 
6
+ from pynintendoparental.exceptions import HttpException, NoDevicesFoundException
7
+
6
8
  from .authenticator import Authenticator
7
9
  from .api import Api
8
10
  from .const import _LOGGER
@@ -29,9 +31,14 @@ class NintendoParental:
29
31
  _LOGGER.exception("Error updating device %s: %s",
30
32
  dev.device_id,
31
33
  err)
32
-
33
- response = await self._api.async_get_account_devices()
34
-
34
+ try:
35
+ response = await self._api.async_get_account_devices()
36
+ except HttpException as err:
37
+ if err.status_code == 404:
38
+ _LOGGER.error("No devices found for account %s", self.account_id)
39
+ raise NoDevicesFoundException("No devices found for account") from err
40
+ _LOGGER.error("Error fetching devices: %s", err)
41
+ raise
35
42
  for dev_raw in response["json"]["ownedDevices"]:
36
43
  device: Device = Device.from_device_response(dev_raw, self._api)
37
44
  if self.devices.get(device.device_id, None) is None:
@@ -1 +1 @@
1
- __version__ = "1.0.2"
1
+ __version__ = "1.0.4"
pynintendoparental/api.py CHANGED
@@ -97,7 +97,7 @@ class Api:
97
97
  resp["json"] = {}
98
98
  resp["headers"] = response.headers
99
99
  else:
100
- raise HttpException("HTTP Error", response.status, await response.text())
100
+ raise HttpException(response.status, await response.text())
101
101
 
102
102
  # now return the resp dict
103
103
  return resp
@@ -140,7 +140,8 @@ class Authenticator:
140
140
  )
141
141
 
142
142
  if session_token_response.get("status") != 200:
143
- raise HttpException(f"login error {session_token_response.get('status')}")
143
+ raise HttpException(session_token_response.get("status"),
144
+ session_token_response.get("text"))
144
145
 
145
146
  self._session_token = session_token_response["json"]["session_token"]
146
147
 
@@ -158,13 +159,13 @@ class Authenticator:
158
159
  )
159
160
 
160
161
  if token_response["status"] == 400:
161
- raise InvalidSessionTokenException(token_response["json"]["error"])
162
+ raise InvalidSessionTokenException(400, token_response["json"]["error"])
162
163
 
163
164
  if token_response["status"] == 401:
164
- raise InvalidOAuthConfigurationException(token_response["json"]["error"])
165
+ raise InvalidOAuthConfigurationException(401, token_response["json"]["error"])
165
166
 
166
167
  if token_response.get("status") != 200:
167
- raise HttpException(f"login error {token_response.get('status')}")
168
+ raise HttpException(token_response.get("status"), f"login error {token_response.get('status')}")
168
169
 
169
170
  self._read_tokens(token_response.get("json"))
170
171
  if self.account_id is None:
@@ -177,7 +178,7 @@ class Authenticator:
177
178
  }
178
179
  )
179
180
  if account["status"] != 200:
180
- raise HttpException(f"Unable to get account_id {token_response.get('status')}")
181
+ raise HttpException(account["status"], f"Unable to get account_id {account['status']}")
181
182
  self.account_id = account["json"]["id"]
182
183
  self.account = account["json"]
183
184
 
@@ -373,26 +373,42 @@ class Device:
373
373
  self.alarms_enabled,
374
374
  self.device_id)
375
375
 
376
- async def get_monthly_summary(self, search_date: datetime = None):
376
+ async def get_monthly_summary(self, search_date: datetime = None) -> dict | None:
377
377
  """Gets the monthly summary."""
378
378
  _LOGGER.debug(">> Device.get_monthly_summary(search_date=%s)", search_date)
379
379
  latest = False
380
380
  if search_date is None:
381
- response = await self._api.async_get_device_monthly_summaries(
382
- device_id=self.device_id
383
- )
384
- _LOGGER.debug("Available monthly summaries: %s", response["json"]["available"])
385
- response = response["json"]["available"][0]
386
- search_date = datetime.strptime(f"{response['year']}-{response['month']}-01", "%Y-%m-%d")
387
- _LOGGER.debug("Using search date %s for monthly summary request", search_date)
388
- latest = True
381
+ try:
382
+ response = await self._api.async_get_device_monthly_summaries(
383
+ device_id=self.device_id
384
+ )
385
+ except HttpException as exc:
386
+ _LOGGER.debug("Could not retrieve monthly summaries: %s", exc)
387
+ return
388
+ else:
389
+ available_summaries = response["json"]["available"]
390
+ _LOGGER.debug("Available monthly summaries: %s", available_summaries)
391
+ if not available_summaries:
392
+ _LOGGER.debug("No monthly summaries available for device %s", self.device_id)
393
+ return None
394
+ # Use the most recent available summary
395
+ available_summary = available_summaries[0]
396
+ search_date = datetime.strptime(f"{available_summary['year']}-{available_summary['month']}-01", "%Y-%m-%d")
397
+ _LOGGER.debug("Using search date %s for monthly summary request", search_date)
398
+ latest = True
389
399
 
390
400
  try:
391
401
  response = await self._api.async_get_device_monthly_summary(
392
- device_id = self.device_id,
402
+ device_id=self.device_id,
393
403
  year=search_date.year,
394
404
  month=search_date.month
395
405
  )
406
+ except HttpException as exc:
407
+ _LOGGER.warning("HTTP Exception raised while getting monthly summary for device %s: %s",
408
+ self.device_id,
409
+ exc)
410
+ return None
411
+ else:
396
412
  _LOGGER.debug("Monthly summary query complete for device %s: %s",
397
413
  self.device_id,
398
414
  response["json"]["summary"])
@@ -400,10 +416,7 @@ class Device:
400
416
  self.last_month_summary = summary = response["json"]["summary"]
401
417
  return summary
402
418
  return response["json"]["summary"]
403
- except HttpException as exc:
404
- _LOGGER.warning("HTTP Exception raised while getting monthly summary for device %s: %s",
405
- self.device_id,
406
- exc)
419
+
407
420
 
408
421
  def get_date_summary(self, input_date: datetime = datetime.now()) -> dict:
409
422
  """Returns usage for a given date."""
@@ -2,12 +2,20 @@
2
2
 
3
3
  class HttpException(Exception):
4
4
  """A HTTP error occured"""
5
+ def __init__(self, status_code: int, message: str) -> None:
6
+ """Initialize the exception."""
7
+ super().__init__(message)
8
+ self.status_code = status_code
9
+ self.message = message
5
10
 
6
- def __init__(self, *args: object) -> None:
7
- super().__init__("HTTP Exception", *args)
11
+ def __str__(self) -> str:
12
+ return f"HTTP {self.status_code}: {self.message}"
8
13
 
9
14
  class InvalidSessionTokenException(HttpException):
10
15
  """Provided session token was invalid (invalid_grant)."""
11
16
 
12
17
  class InvalidOAuthConfigurationException(HttpException):
13
18
  """The OAuth scopes are invalid."""
19
+
20
+ class NoDevicesFoundException(Exception):
21
+ """No devices were found for the account."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pynintendoparental
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: A Python module to interact with Nintendo Parental Controls
5
5
  Home-page: http://github.com/pantherale0/pynintendoparental
6
6
  Author: pantherale0
@@ -17,7 +17,7 @@ Requires-Dist: black<26,>=23; extra == "dev"
17
17
  Requires-Dist: build<1.3,>=0.10; extra == "dev"
18
18
  Requires-Dist: flake8<8,>=6; extra == "dev"
19
19
  Requires-Dist: isort<7,>=5; extra == "dev"
20
- Requires-Dist: mypy<1.17,>=1.5; extra == "dev"
20
+ Requires-Dist: mypy<1.18,>=1.5; extra == "dev"
21
21
  Requires-Dist: pytest<9,>=7; extra == "dev"
22
22
  Requires-Dist: pytest-cov<7,>=4; extra == "dev"
23
23
  Requires-Dist: twine<7,>=4; extra == "dev"
@@ -0,0 +1,18 @@
1
+ pynintendoparental/__init__.py,sha256=pNcBsHRa4B85USP7uzwPEGF9fu3MA9YgW_hI82F_NXQ,2460
2
+ pynintendoparental/_version.py,sha256=acuR_XSJzp4OrQ5T8-Ac5gYe48mUwObuwjRmisFmZ7k,22
3
+ pynintendoparental/api.py,sha256=hMXq0eNIgFELlNZJtN0rK3plKyu9nirvwiUPNlkjOCY,7013
4
+ pynintendoparental/application.py,sha256=l-oVwM4hrVVUf_2djQ7rJVya7LQP38yhaLPAWt8V8TY,3941
5
+ pynintendoparental/const.py,sha256=Ao38EeEXMOouY4DRgOfeytDY8Kwj3XAaiaCDteSJ7IE,2101
6
+ pynintendoparental/device.py,sha256=ASyWcY8wwWPgq7mlJCnFIP7IeYw0HgSxgIktQUYp8Oo,22201
7
+ pynintendoparental/enum.py,sha256=lzacGti7fcQqAOROjB9782De7bOMYKSEM61SQd6aYG4,401
8
+ pynintendoparental/exceptions.py,sha256=usHY7Ew0vicPVGUNk1NA1sy0vpQxTJrPPT-N5lWNQoY,701
9
+ pynintendoparental/player.py,sha256=WDl0pspHgrV9lGhDp-NKlfP8DV4Yxe02aYaGg9wTTeg,1785
10
+ pynintendoparental/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ pynintendoparental/utils.py,sha256=5-EP_rmPnSSWtbi18Y226GtjLhF3PLONKwmRdiy7m2c,198
12
+ pynintendoparental/authenticator/__init__.py,sha256=MZdA6qqHV0i7rspNL9Z9xl7aRy-EJEm3NIapiIgEJBA,7688
13
+ pynintendoparental/authenticator/const.py,sha256=_nUJVC0U64j_n1LaQd_KDg0EWFcezb87bQyYYXpbPPY,917
14
+ pynintendoparental-1.0.4.dist-info/licenses/LICENSE,sha256=zsxHgHVMnyWq121yND8zBl9Rl9H6EF2K9N51B2ZSm_k,1071
15
+ pynintendoparental-1.0.4.dist-info/METADATA,sha256=tV5qwZ84sU23r0seWjqsNplMfyyUABlI-jymxLuHMRA,1871
16
+ pynintendoparental-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ pynintendoparental-1.0.4.dist-info/top_level.txt,sha256=QQ5bAl-Ljso16P8KLf1NHrFmKk9jLT7bVJG_rVlIXWk,19
18
+ pynintendoparental-1.0.4.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- pynintendoparental/__init__.py,sha256=CDz2tdO25y3vO3lLA6VQZOJl2Np10wgPIl8oWA4aGdE,2039
2
- pynintendoparental/_version.py,sha256=Y3LSfRioSl2xch70pq_ULlvyECXyEtN3krVaWeGyaxk,22
3
- pynintendoparental/api.py,sha256=WzsWhQQcexJ5PIdem0WrC5XZ4FtnIeA29XZmV6tfBp0,7027
4
- pynintendoparental/application.py,sha256=l-oVwM4hrVVUf_2djQ7rJVya7LQP38yhaLPAWt8V8TY,3941
5
- pynintendoparental/const.py,sha256=Ao38EeEXMOouY4DRgOfeytDY8Kwj3XAaiaCDteSJ7IE,2101
6
- pynintendoparental/device.py,sha256=glwB1UdgiOcMKwmC1RjYtP3L-xDBiSXCGqiFxwrjoII,21634
7
- pynintendoparental/enum.py,sha256=lzacGti7fcQqAOROjB9782De7bOMYKSEM61SQd6aYG4,401
8
- pynintendoparental/exceptions.py,sha256=5M3y0OLSRvX9Ly8D4981HqiKTNxxVqeyzPIuAekQ6J8,410
9
- pynintendoparental/player.py,sha256=WDl0pspHgrV9lGhDp-NKlfP8DV4Yxe02aYaGg9wTTeg,1785
10
- pynintendoparental/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- pynintendoparental/utils.py,sha256=5-EP_rmPnSSWtbi18Y226GtjLhF3PLONKwmRdiy7m2c,198
12
- pynintendoparental/authenticator/__init__.py,sha256=dnmp6YjBJ0fQ5YWhWGJsAjj1ROxLihPPGr0PVJtTsgI,7589
13
- pynintendoparental/authenticator/const.py,sha256=_nUJVC0U64j_n1LaQd_KDg0EWFcezb87bQyYYXpbPPY,917
14
- pynintendoparental-1.0.2.dist-info/licenses/LICENSE,sha256=zsxHgHVMnyWq121yND8zBl9Rl9H6EF2K9N51B2ZSm_k,1071
15
- pynintendoparental-1.0.2.dist-info/METADATA,sha256=DvMCwNlUuNKr9KdvzvakZyHmKC118w65vqP3KQMSC18,1871
16
- pynintendoparental-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- pynintendoparental-1.0.2.dist-info/top_level.txt,sha256=QQ5bAl-Ljso16P8KLf1NHrFmKk9jLT7bVJG_rVlIXWk,19
18
- pynintendoparental-1.0.2.dist-info/RECORD,,