pynintendoparental 1.0.1__py3-none-any.whl → 1.0.3__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.
@@ -1 +1 @@
1
- __version__ = "1.0.1"
1
+ __version__ = "1.0.3"
pynintendoparental/api.py CHANGED
@@ -192,3 +192,22 @@ class Api:
192
192
  "unlockCode": new_code
193
193
  }
194
194
  )
195
+
196
+ async def async_update_extra_playing_time(
197
+ self,
198
+ device_id: str,
199
+ additional_time: int
200
+ ) -> dict:
201
+ """Update device extra playing time."""
202
+ body = {
203
+ "deviceId": device_id,
204
+ "additionalTime": additional_time,
205
+ "status": "TO_ADDED"
206
+ }
207
+ if additional_time == -1:
208
+ body["status"] = "TO_INFINITY"
209
+ body.pop("additionalTime")
210
+ return await self.send_request(
211
+ endpoint="update_extra_playing_time",
212
+ body=body
213
+ )
@@ -5,8 +5,8 @@ import logging
5
5
 
6
6
  _LOGGER = logging.getLogger(__package__)
7
7
  MOBILE_APP_PKG = "com.nintendo.znma"
8
- MOBILE_APP_VERSION = "2.0.0"
9
- MOBILE_APP_BUILD = "502"
8
+ MOBILE_APP_VERSION = "2.1.1"
9
+ MOBILE_APP_BUILD = "540"
10
10
  OS_NAME = "ANDROID"
11
11
  OS_VERSION = "33"
12
12
  OS_STR = f"{OS_NAME} {OS_VERSION}"
@@ -52,5 +52,9 @@ ENDPOINTS = {
52
52
  "get_device_monthly_summary": {
53
53
  "url": "{BASE_URL}/actions/playSummary/fetchMonthlySummary?deviceId={DEVICE_ID}&year={YEAR}&month={MONTH}&containLatest=false",
54
54
  "method": "GET"
55
+ },
56
+ "update_extra_playing_time": {
57
+ "url": "{BASE_URL}/actions/device/updateExtraPlayingTime",
58
+ "method": "POST"
55
59
  }
56
60
  }
@@ -63,11 +63,7 @@ class Device:
63
63
  else:
64
64
  for player in self.players:
65
65
  player.update_from_daily_summary(self.daily_summaries)
66
- for cb in self._callbacks:
67
- if is_awaitable(cb):
68
- await cb()
69
- else:
70
- cb()
66
+ await self._execute_callbacks()
71
67
 
72
68
  def add_device_callback(self, callback):
73
69
  """Add a callback to the device."""
@@ -83,6 +79,14 @@ class Device:
83
79
  if callback in self._callbacks:
84
80
  self._callbacks.remove(callback)
85
81
 
82
+ async def _execute_callbacks(self):
83
+ """Execute all callbacks."""
84
+ for cb in self._callbacks:
85
+ if is_awaitable(cb):
86
+ await cb()
87
+ else:
88
+ cb()
89
+
86
90
  async def set_new_pin(self, pin: str):
87
91
  """Updates the pin for the device."""
88
92
  _LOGGER.debug(">> Device.set_new_pin(pin=REDACTED)")
@@ -92,6 +96,17 @@ class Device:
92
96
  device_id=self.device_id
93
97
  )
94
98
  self._parse_parental_control_setting(response["json"])
99
+ await self._execute_callbacks()
100
+
101
+ async def add_extra_time(self, minutes: int):
102
+ """Add extra time to the device."""
103
+ _LOGGER.debug(">> Device.add_extra_time(minutes=%s)", minutes)
104
+ await self._api.async_update_extra_playing_time(
105
+ device_id=self.device_id,
106
+ additional_time=minutes
107
+ )
108
+ await self._execute_callbacks()
109
+
95
110
 
96
111
  async def set_restriction_mode(self, mode: RestrictionMode):
97
112
  """Updates the restriction mode of the device."""
@@ -104,6 +119,7 @@ class Device:
104
119
  }
105
120
  )
106
121
  self._parse_parental_control_setting(response["json"])
122
+ await self._execute_callbacks()
107
123
 
108
124
  async def set_bedtime_alarm(self, end_time: time = None, enabled: bool = True):
109
125
  """Update the bedtime alarm for the device."""
@@ -134,6 +150,8 @@ class Device:
134
150
  }
135
151
  )
136
152
  self._parse_parental_control_setting(response["json"])
153
+ self._calculate_times()
154
+ await self._execute_callbacks()
137
155
 
138
156
  async def update_max_daily_playtime(self, minutes: int = 0):
139
157
  """Updates the maximum daily playtime of a device."""
@@ -176,6 +194,8 @@ class Device:
176
194
  }
177
195
  )
178
196
  self._parse_parental_control_setting(response["json"])
197
+ self._calculate_times()
198
+ await self._execute_callbacks()
179
199
 
180
200
  def _update_applications(self):
181
201
  """Updates applications from daily summary."""
@@ -229,31 +249,22 @@ class Device:
229
249
  self._update_day_of_week_regulations()
230
250
  self._update_applications()
231
251
 
232
- async def _get_parental_control_setting(self):
233
- """Retreives parental control settings from the API."""
234
- _LOGGER.debug(">> Device._get_parental_control_setting()")
235
- response = await self._api.async_get_device_parental_control_setting(
236
- device_id=self.device_id
237
- )
238
- self._parse_parental_control_setting(response["json"])
239
-
240
- async def _get_daily_summaries(self):
241
- """Retrieve daily summaries."""
242
- _LOGGER.debug(">> Device._get_daily_summaries()")
243
- response = await self._api.async_get_device_daily_summaries(
244
- device_id = self.device_id
245
- )
246
- self.daily_summaries = response["json"]["dailySummaries"]
247
- _LOGGER.debug("New daily summary %s", self.daily_summaries)
248
- try:
249
- today_playing_time = self.get_date_summary()[0].get("playingTime", 0)
250
- self.today_playing_time = 0 if today_playing_time is None else today_playing_time
251
- today_disabled_time = self.get_date_summary()[0].get("disabledTime", 0)
252
- self.today_disabled_time = 0 if today_disabled_time is None else today_disabled_time
253
- today_exceeded_time = self.get_date_summary()[0].get("exceededTime", 0)
254
- self.today_exceeded_time = 0 if today_exceeded_time is None else today_exceeded_time
255
- _LOGGER.debug("Cached playing, disabled and exceeded time for today for device %s",
252
+ def _calculate_times(self):
253
+ """Calculate times from parental control settings."""
254
+ if not isinstance(self.daily_summaries, list) or not self.daily_summaries:
255
+ return
256
+ if len(self.daily_summaries) == 0:
257
+ return
258
+ _LOGGER.debug(">> Device._calculate_times()")
259
+ today_playing_time = self.daily_summaries[0].get("playingTime", 0)
260
+ self.today_playing_time = 0 if today_playing_time is None else today_playing_time
261
+ today_disabled_time = self.daily_summaries[0].get("disabledTime", 0)
262
+ self.today_disabled_time = 0 if today_disabled_time is None else today_disabled_time
263
+ today_exceeded_time = self.daily_summaries[0].get("exceededTime", 0)
264
+ self.today_exceeded_time = 0 if today_exceeded_time is None else today_exceeded_time
265
+ _LOGGER.debug("Cached playing, disabled and exceeded time for today for device %s",
256
266
  self.device_id)
267
+ try:
257
268
  now = datetime.now()
258
269
  current_minutes_past_midnight = now.hour * 60 + now.minute
259
270
  minutes_in_day = 1440 # 24 * 60
@@ -328,6 +339,25 @@ class Device:
328
339
  _LOGGER.debug("Unable to retrieve applications for device %s: %s", self.name, err)
329
340
  self.application_update_failed = True
330
341
 
342
+ async def _get_parental_control_setting(self):
343
+ """Retreives parental control settings from the API."""
344
+ _LOGGER.debug(">> Device._get_parental_control_setting()")
345
+ response = await self._api.async_get_device_parental_control_setting(
346
+ device_id=self.device_id
347
+ )
348
+ self._parse_parental_control_setting(response["json"])
349
+ self._calculate_times()
350
+
351
+ async def _get_daily_summaries(self):
352
+ """Retrieve daily summaries."""
353
+ _LOGGER.debug(">> Device._get_daily_summaries()")
354
+ response = await self._api.async_get_device_daily_summaries(
355
+ device_id = self.device_id
356
+ )
357
+ self.daily_summaries = response["json"]["dailySummaries"]
358
+ _LOGGER.debug("New daily summary %s", self.daily_summaries)
359
+ self._calculate_times()
360
+
331
361
  async def _get_extras(self):
332
362
  """Retrieve extra properties."""
333
363
  _LOGGER.debug(">> Device._get_extras()")
@@ -343,26 +373,42 @@ class Device:
343
373
  self.alarms_enabled,
344
374
  self.device_id)
345
375
 
346
- async def get_monthly_summary(self, search_date: datetime = None):
376
+ async def get_monthly_summary(self, search_date: datetime = None) -> dict | None:
347
377
  """Gets the monthly summary."""
348
378
  _LOGGER.debug(">> Device.get_monthly_summary(search_date=%s)", search_date)
349
379
  latest = False
350
380
  if search_date is None:
351
- response = await self._api.async_get_device_monthly_summaries(
352
- device_id=self.device_id
353
- )
354
- _LOGGER.debug("Available monthly summaries: %s", response["json"]["available"])
355
- response = response["json"]["available"][0]
356
- search_date = datetime.strptime(f"{response['year']}-{response['month']}-01", "%Y-%m-%d")
357
- _LOGGER.debug("Using search date %s for monthly summary request", search_date)
358
- 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
359
399
 
360
400
  try:
361
401
  response = await self._api.async_get_device_monthly_summary(
362
- device_id = self.device_id,
402
+ device_id=self.device_id,
363
403
  year=search_date.year,
364
404
  month=search_date.month
365
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:
366
412
  _LOGGER.debug("Monthly summary query complete for device %s: %s",
367
413
  self.device_id,
368
414
  response["json"]["summary"])
@@ -370,10 +416,7 @@ class Device:
370
416
  self.last_month_summary = summary = response["json"]["summary"]
371
417
  return summary
372
418
  return response["json"]["summary"]
373
- except HttpException as exc:
374
- _LOGGER.warning("HTTP Exception raised while getting monthly summary for device %s: %s",
375
- self.device_id,
376
- exc)
419
+
377
420
 
378
421
  def get_date_summary(self, input_date: datetime = datetime.now()) -> dict:
379
422
  """Returns usage for a given date."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pynintendoparental
3
- Version: 1.0.1
3
+ Version: 1.0.3
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"
@@ -1,9 +1,9 @@
1
1
  pynintendoparental/__init__.py,sha256=CDz2tdO25y3vO3lLA6VQZOJl2Np10wgPIl8oWA4aGdE,2039
2
- pynintendoparental/_version.py,sha256=d4QHYmS_30j0hPN8NmNPnQ_Z0TphDRbu4MtQj9cT9e8,22
3
- pynintendoparental/api.py,sha256=WW_2VqPH8qs09G8vIMDOcoEgXf-8yyOpmAH8wsYAC1w,6469
2
+ pynintendoparental/_version.py,sha256=2plzdEEb24FLjE2I2XyBBcJEPYWHccNL4SgtLC_6erg,22
3
+ pynintendoparental/api.py,sha256=WzsWhQQcexJ5PIdem0WrC5XZ4FtnIeA29XZmV6tfBp0,7027
4
4
  pynintendoparental/application.py,sha256=l-oVwM4hrVVUf_2djQ7rJVya7LQP38yhaLPAWt8V8TY,3941
5
- pynintendoparental/const.py,sha256=0ROFl98eEOTWqTJBu7ElPgdsILJOyXJi4Xp7Dk8LRUY,1967
6
- pynintendoparental/device.py,sha256=_vb71vG6SKC9OLSvPnnT4Hc5kjBfVO8vGCA4FTg7GMA,20604
5
+ pynintendoparental/const.py,sha256=Ao38EeEXMOouY4DRgOfeytDY8Kwj3XAaiaCDteSJ7IE,2101
6
+ pynintendoparental/device.py,sha256=ASyWcY8wwWPgq7mlJCnFIP7IeYw0HgSxgIktQUYp8Oo,22201
7
7
  pynintendoparental/enum.py,sha256=lzacGti7fcQqAOROjB9782De7bOMYKSEM61SQd6aYG4,401
8
8
  pynintendoparental/exceptions.py,sha256=5M3y0OLSRvX9Ly8D4981HqiKTNxxVqeyzPIuAekQ6J8,410
9
9
  pynintendoparental/player.py,sha256=WDl0pspHgrV9lGhDp-NKlfP8DV4Yxe02aYaGg9wTTeg,1785
@@ -11,8 +11,8 @@ pynintendoparental/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  pynintendoparental/utils.py,sha256=5-EP_rmPnSSWtbi18Y226GtjLhF3PLONKwmRdiy7m2c,198
12
12
  pynintendoparental/authenticator/__init__.py,sha256=dnmp6YjBJ0fQ5YWhWGJsAjj1ROxLihPPGr0PVJtTsgI,7589
13
13
  pynintendoparental/authenticator/const.py,sha256=_nUJVC0U64j_n1LaQd_KDg0EWFcezb87bQyYYXpbPPY,917
14
- pynintendoparental-1.0.1.dist-info/licenses/LICENSE,sha256=zsxHgHVMnyWq121yND8zBl9Rl9H6EF2K9N51B2ZSm_k,1071
15
- pynintendoparental-1.0.1.dist-info/METADATA,sha256=rzybrmp9z0jtmmDLNfp-35fDXD-CRMDDZfG4xDiSb5w,1871
16
- pynintendoparental-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- pynintendoparental-1.0.1.dist-info/top_level.txt,sha256=QQ5bAl-Ljso16P8KLf1NHrFmKk9jLT7bVJG_rVlIXWk,19
18
- pynintendoparental-1.0.1.dist-info/RECORD,,
14
+ pynintendoparental-1.0.3.dist-info/licenses/LICENSE,sha256=zsxHgHVMnyWq121yND8zBl9Rl9H6EF2K9N51B2ZSm_k,1071
15
+ pynintendoparental-1.0.3.dist-info/METADATA,sha256=T6pA0zeYr7UQRNqK9cdMFIaCTrWx-bXZgZxKayg86nE,1871
16
+ pynintendoparental-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ pynintendoparental-1.0.3.dist-info/top_level.txt,sha256=QQ5bAl-Ljso16P8KLf1NHrFmKk9jLT7bVJG_rVlIXWk,19
18
+ pynintendoparental-1.0.3.dist-info/RECORD,,