pynintendoparental 1.1.3__py3-none-any.whl → 2.1.0__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.
@@ -6,14 +6,20 @@ import asyncio
6
6
  from datetime import datetime, timedelta, time
7
7
  from typing import Callable
8
8
 
9
+ from pynintendoauth.exceptions import HttpException
10
+
9
11
  from .api import Api
10
12
  from .const import _LOGGER, DAYS_OF_WEEK
11
- from .exceptions import BedtimeOutOfRangeError, DailyPlaytimeOutOfRangeError, HttpException
13
+ from .exceptions import (
14
+ BedtimeOutOfRangeError,
15
+ DailyPlaytimeOutOfRangeError,
16
+ )
12
17
  from .enum import AlarmSettingState, DeviceTimerMode, RestrictionMode
13
18
  from .player import Player
14
19
  from .utils import is_awaitable
15
20
  from .application import Application
16
21
 
22
+
17
23
  class Device:
18
24
  """A device"""
19
25
 
@@ -26,8 +32,9 @@ class Device:
26
32
  self._api: Api = api
27
33
  self.daily_summaries: dict = {}
28
34
  self.parental_control_settings: dict = {}
29
- self.players: list[Player] = []
35
+ self.players: dict[str, Player] = {}
30
36
  self.limit_time: int | float | None = 0
37
+ self.extra_playing_time: int | None = None
31
38
  self.timer_mode: DeviceTimerMode | None = None
32
39
  self.today_playing_time: int | float = 0
33
40
  self.today_time_remaining: int | float = 0
@@ -52,10 +59,7 @@ class Device:
52
59
  @property
53
60
  def model(self) -> str:
54
61
  """Return the model."""
55
- model_map = {
56
- "P00": "Switch",
57
- "P01": "Switch 2"
58
- }
62
+ model_map = {"P00": "Switch", "P01": "Switch 2"}
59
63
  return model_map.get(self.generation, "Unknown")
60
64
 
61
65
  @property
@@ -63,21 +67,25 @@ class Device:
63
67
  """Return the generation."""
64
68
  return self.extra.get("platformGeneration", None)
65
69
 
70
+ @property
71
+ def last_sync(self) -> float | None:
72
+ """Return the last time this device was synced."""
73
+ return self.extra.get("synchronizedParentalControlSetting", {}).get(
74
+ "synchronizedAt", None
75
+ )
76
+
66
77
  async def update(self):
67
78
  """Update data."""
68
79
  _LOGGER.debug(">> Device.update()")
69
80
  now = datetime.now()
70
81
  await asyncio.gather(
71
- self._get_daily_summaries(now),
72
- self._get_parental_control_setting(now),
73
- self.get_monthly_summary(),
74
- self._get_extras()
82
+ self._get_daily_summaries(now),
83
+ self._get_parental_control_setting(now),
84
+ self.get_monthly_summary(),
85
+ self._get_extras(),
75
86
  )
76
- if not self.players:
77
- self.players = Player.from_device_daily_summary(self.daily_summaries)
78
- else:
79
- for player in self.players:
80
- player.update_from_daily_summary(self.daily_summaries)
87
+ for player in self.players.values():
88
+ player.update_from_daily_summary(self.daily_summaries)
81
89
  await self._execute_callbacks()
82
90
 
83
91
  def add_device_callback(self, callback):
@@ -114,9 +122,7 @@ class Device:
114
122
  """Updates the pin for the device."""
115
123
  _LOGGER.debug(">> Device.set_new_pin(pin=REDACTED)")
116
124
  await self._send_api_update(
117
- self._api.async_update_unlock_code,
118
- new_code=pin,
119
- device_id=self.device_id
125
+ self._api.async_update_unlock_code, new_code=pin, device_id=self.device_id
120
126
  )
121
127
 
122
128
  async def add_extra_time(self, minutes: int):
@@ -129,25 +135,30 @@ class Device:
129
135
  async def set_restriction_mode(self, mode: RestrictionMode):
130
136
  """Updates the restriction mode of the device."""
131
137
  _LOGGER.debug(">> Device.set_restriction_mode(mode=%s)", mode)
132
- self.parental_control_settings["playTimerRegulations"]["restrictionMode"] = str(mode)
138
+ self.parental_control_settings["playTimerRegulations"]["restrictionMode"] = str(
139
+ mode
140
+ )
133
141
  response = await self._api.async_update_play_timer(
134
142
  settings={
135
143
  "deviceId": self.device_id,
136
- "playTimerRegulations": self.parental_control_settings["playTimerRegulations"]
144
+ "playTimerRegulations": self.parental_control_settings[
145
+ "playTimerRegulations"
146
+ ],
137
147
  }
138
148
  )
139
149
  now = datetime.now()
140
- self._parse_parental_control_setting(response["json"], now) # Don't need to recalculate times
150
+ self._parse_parental_control_setting(
151
+ response["json"], now
152
+ ) # Don't need to recalculate times
141
153
  await self._execute_callbacks()
142
154
 
143
155
  async def set_bedtime_alarm(self, value: time):
144
156
  """Update the bedtime alarm for the device."""
145
- _LOGGER.debug(">> Device.set_bedtime_alarm(value=%s)",
146
- value)
157
+ _LOGGER.debug(">> Device.set_bedtime_alarm(value=%s)", value)
147
158
  if not (
148
- (16 <= value.hour <= 22) or
149
- (value.hour == 23 and value.minute == 0) or
150
- (value.hour == 0 and value.minute == 0)
159
+ (16 <= value.hour <= 22)
160
+ or (value.hour == 23 and value.minute == 0)
161
+ or (value.hour == 0 and value.minute == 0)
151
162
  ):
152
163
  raise BedtimeOutOfRangeError(value=value)
153
164
  now = datetime.now()
@@ -157,24 +168,25 @@ class Device:
157
168
  if bedtime["enabled"]:
158
169
  bedtime = {
159
170
  **bedtime,
160
- "endingTime": {
161
- "hour": value.hour,
162
- "minute": value.minute
163
- }
171
+ "endingTime": {"hour": value.hour, "minute": value.minute},
164
172
  }
165
173
  if self.timer_mode == DeviceTimerMode.DAILY:
166
- self.parental_control_settings["playTimerRegulations"]["dailyRegulations"]["bedtime"] = bedtime
174
+ self.parental_control_settings["playTimerRegulations"]["dailyRegulations"][
175
+ "bedtime"
176
+ ] = bedtime
167
177
  else:
168
- self.parental_control_settings["playTimerRegulations"]["eachDayOfTheWeekRegulations"][
169
- DAYS_OF_WEEK[now.weekday()]
170
- ]["bedtime"] = bedtime
178
+ self.parental_control_settings["playTimerRegulations"][
179
+ "eachDayOfTheWeekRegulations"
180
+ ][DAYS_OF_WEEK[now.weekday()]]["bedtime"] = bedtime
171
181
  await self._send_api_update(
172
182
  self._api.async_update_play_timer,
173
183
  settings={
174
184
  "deviceId": self.device_id,
175
- "playTimerRegulations": self.parental_control_settings["playTimerRegulations"]
185
+ "playTimerRegulations": self.parental_control_settings[
186
+ "playTimerRegulations"
187
+ ],
176
188
  },
177
- now=now
189
+ now=now,
178
190
  )
179
191
 
180
192
  async def set_timer_mode(self, mode: DeviceTimerMode):
@@ -186,14 +198,15 @@ class Device:
186
198
  self._api.async_update_play_timer,
187
199
  settings={
188
200
  "deviceId": self.device_id,
189
- "playTimerRegulations": self.parental_control_settings["playTimerRegulations"]
190
- }
201
+ "playTimerRegulations": self.parental_control_settings[
202
+ "playTimerRegulations"
203
+ ],
204
+ },
191
205
  )
192
206
 
193
207
  async def update_max_daily_playtime(self, minutes: int | float = 0):
194
208
  """Updates the maximum daily playtime of a device."""
195
- _LOGGER.debug(">> Device.update_max_daily_playtime(minutes=%s)",
196
- minutes)
209
+ _LOGGER.debug(">> Device.update_max_daily_playtime(minutes=%s)", minutes)
197
210
  if isinstance(minutes, float):
198
211
  minutes = int(minutes)
199
212
  if minutes > 360 or minutes < -1:
@@ -207,39 +220,63 @@ class Device:
207
220
  _LOGGER.debug(
208
221
  "Setting timeToPlayInOneDay.limitTime for device %s to value %s",
209
222
  self.device_id,
210
- minutes)
211
- self.parental_control_settings["playTimerRegulations"]["dailyRegulations"]["timeToPlayInOneDay"]["enabled"] = ttpiod
212
- if "limitTime" in self.parental_control_settings["playTimerRegulations"]["dailyRegulations"]["timeToPlayInOneDay"] and minutes is None:
213
- self.parental_control_settings["playTimerRegulations"]["dailyRegulations"]["timeToPlayInOneDay"].pop("limitTime")
223
+ minutes,
224
+ )
225
+ self.parental_control_settings["playTimerRegulations"]["dailyRegulations"][
226
+ "timeToPlayInOneDay"
227
+ ]["enabled"] = ttpiod
228
+ if (
229
+ "limitTime"
230
+ in self.parental_control_settings["playTimerRegulations"][
231
+ "dailyRegulations"
232
+ ]["timeToPlayInOneDay"]
233
+ and minutes is None
234
+ ):
235
+ self.parental_control_settings["playTimerRegulations"][
236
+ "dailyRegulations"
237
+ ]["timeToPlayInOneDay"].pop("limitTime")
214
238
  else:
215
- self.parental_control_settings["playTimerRegulations"]["dailyRegulations"]["timeToPlayInOneDay"]["limitTime"] = minutes
239
+ self.parental_control_settings["playTimerRegulations"][
240
+ "dailyRegulations"
241
+ ]["timeToPlayInOneDay"]["limitTime"] = minutes
216
242
  else:
217
243
  _LOGGER.debug(
218
244
  "Setting timeToPlayInOneDay.limitTime for device %s to value %s",
219
245
  self.device_id,
220
- minutes
246
+ minutes,
221
247
  )
222
- day_of_week_regs = self.parental_control_settings["playTimerRegulations"]["eachDayOfTheWeekRegulations"]
248
+ day_of_week_regs = self.parental_control_settings["playTimerRegulations"][
249
+ "eachDayOfTheWeekRegulations"
250
+ ]
223
251
  current_day = DAYS_OF_WEEK[now.weekday()]
224
252
  day_of_week_regs[current_day]["timeToPlayInOneDay"]["enabled"] = ttpiod
225
- if "limitTime" in day_of_week_regs[current_day]["timeToPlayInOneDay"] and minutes is None:
253
+ if (
254
+ "limitTime" in day_of_week_regs[current_day]["timeToPlayInOneDay"]
255
+ and minutes is None
256
+ ):
226
257
  day_of_week_regs[current_day]["timeToPlayInOneDay"].pop("limitTime")
227
258
  else:
228
- day_of_week_regs[current_day]["timeToPlayInOneDay"]["limitTime"] = minutes
259
+ day_of_week_regs[current_day]["timeToPlayInOneDay"]["limitTime"] = (
260
+ minutes
261
+ )
229
262
 
230
263
  await self._send_api_update(
231
264
  self._api.async_update_play_timer,
232
265
  settings={
233
266
  "deviceId": self.device_id,
234
- "playTimerRegulations": self.parental_control_settings["playTimerRegulations"]
267
+ "playTimerRegulations": self.parental_control_settings[
268
+ "playTimerRegulations"
269
+ ],
235
270
  },
236
- now=now
271
+ now=now,
237
272
  )
238
273
 
239
274
  def _update_applications(self):
240
275
  """Updates applications from daily summary."""
241
276
  _LOGGER.debug(">> Device._update_applications()")
242
- parsed_apps = Application.from_whitelist(self.parental_control_settings.get("whitelistedApplications", []))
277
+ parsed_apps = Application.from_whitelist(
278
+ self.parental_control_settings.get("whitelistedApplications", [])
279
+ )
243
280
  for app in parsed_apps:
244
281
  try:
245
282
  self.get_application(app.application_id).update(app)
@@ -250,9 +287,13 @@ class Device:
250
287
  def _get_today_regulation(self, now: datetime) -> dict:
251
288
  """Returns the regulation settings for the current day."""
252
289
  if self.timer_mode == DeviceTimerMode.EACH_DAY_OF_THE_WEEK:
253
- day_of_week_regs = self.parental_control_settings["playTimerRegulations"].get("eachDayOfTheWeekRegulations", {})
290
+ day_of_week_regs = self.parental_control_settings[
291
+ "playTimerRegulations"
292
+ ].get("eachDayOfTheWeekRegulations", {})
254
293
  return day_of_week_regs.get(DAYS_OF_WEEK[now.weekday()], {})
255
- return self.parental_control_settings.get("playTimerRegulations", {}).get("dailyRegulations", {})
294
+ return self.parental_control_settings.get("playTimerRegulations", {}).get(
295
+ "dailyRegulations", {}
296
+ )
256
297
 
257
298
  def _parse_parental_control_setting(self, pcs: dict, now: datetime):
258
299
  """Parse a parental control setting request response."""
@@ -260,13 +301,23 @@ class Device:
260
301
  self.parental_control_settings = pcs["parentalControlSetting"]
261
302
 
262
303
  # Clean up bedtimeStartingTime if it's empty
263
- if "bedtimeStartingTime" in self.parental_control_settings["playTimerRegulations"]:
264
- if self.parental_control_settings["playTimerRegulations"].get("bedtimeStartingTime", {}).get("hour", 0) == 0:
265
- self.parental_control_settings["playTimerRegulations"].pop("bedtimeStartingTime")
304
+ if (
305
+ "bedtimeStartingTime"
306
+ in self.parental_control_settings["playTimerRegulations"]
307
+ ):
308
+ if (
309
+ self.parental_control_settings["playTimerRegulations"]
310
+ .get("bedtimeStartingTime", {})
311
+ .get("hour", 0)
312
+ == 0
313
+ ):
314
+ self.parental_control_settings["playTimerRegulations"].pop(
315
+ "bedtimeStartingTime"
316
+ )
266
317
 
267
- self.forced_termination_mode = (
268
- self.parental_control_settings["playTimerRegulations"]["restrictionMode"] == str(RestrictionMode.FORCED_TERMINATION)
269
- )
318
+ self.forced_termination_mode = self.parental_control_settings[
319
+ "playTimerRegulations"
320
+ ]["restrictionMode"] == str(RestrictionMode.FORCED_TERMINATION)
270
321
 
271
322
  # Update limit and bedtime from regulations
272
323
  self.timer_mode = DeviceTimerMode(
@@ -275,12 +326,20 @@ class Device:
275
326
  today_reg = self._get_today_regulation(now)
276
327
  limit_time = today_reg.get("timeToPlayInOneDay", {}).get("limitTime")
277
328
  self.limit_time = limit_time if limit_time is not None else -1
329
+ extra_playing_time_data = (
330
+ pcs.get("ownedDevice", {}).get("device", {}).get("extraPlayingTime")
331
+ )
332
+ self.extra_playing_time = None
333
+ if extra_playing_time_data:
334
+ self.extra_playing_time = extra_playing_time_data.get("inOneDay", {}).get(
335
+ "duration"
336
+ )
278
337
 
279
338
  bedtime_setting = today_reg.get("bedtime", {})
280
339
  if bedtime_setting.get("enabled"):
281
340
  self.bedtime_alarm = time(
282
341
  hour=bedtime_setting["endingTime"]["hour"],
283
- minute=bedtime_setting["endingTime"]["minute"]
342
+ minute=bedtime_setting["endingTime"]["minute"],
284
343
  )
285
344
  else:
286
345
  self.bedtime_alarm = time(hour=0, minute=0)
@@ -303,8 +362,10 @@ class Device:
303
362
  self.today_playing_time = self.daily_summaries[0].get("playingTime") or 0
304
363
  self.today_disabled_time = self.daily_summaries[0].get("disabledTime") or 0
305
364
  self.today_exceeded_time = self.daily_summaries[0].get("exceededTime") or 0
306
- _LOGGER.debug("Cached playing, disabled and exceeded time for today for device %s",
307
- self.device_id)
365
+ _LOGGER.debug(
366
+ "Cached playing, disabled and exceeded time for today for device %s",
367
+ self.device_id,
368
+ )
308
369
  self._calculate_today_remaining_time(now)
309
370
 
310
371
  month_playing_time: int = 0
@@ -319,56 +380,79 @@ class Device:
319
380
  for app in parsed_apps:
320
381
  try:
321
382
  int_app = self.get_application(app.application_id)
322
- _LOGGER.debug("Updating cached app state %s for device %s",
323
- int_app.application_id,
324
- self.device_id)
383
+ _LOGGER.debug(
384
+ "Updating cached app state %s for device %s",
385
+ int_app.application_id,
386
+ self.device_id,
387
+ )
325
388
  int_app.update(app)
326
389
  except ValueError:
327
- _LOGGER.debug("Creating new cached application entry %s for device %s",
328
- app.application_id,
329
- self.device_id)
390
+ _LOGGER.debug(
391
+ "Creating new cached application entry %s for device %s",
392
+ app.application_id,
393
+ self.device_id,
394
+ )
330
395
  self.applications.append(app)
331
396
 
332
397
  # update application playtime
333
398
  try:
334
399
  for player in self.get_date_summary()[0].get("devicePlayers", []):
335
400
  for app in player.get("playedApps", []):
336
- self.get_application(app["applicationId"]).update_today_time_played(app)
401
+ self.get_application(app["applicationId"]).update_today_time_played(
402
+ app
403
+ )
337
404
  self.application_update_failed = False
338
405
  except ValueError as err:
339
- _LOGGER.debug("Unable to retrieve applications for device %s: %s", self.name, err)
406
+ _LOGGER.debug(
407
+ "Unable to retrieve applications for device %s: %s", self.name, err
408
+ )
340
409
  self.application_update_failed = True
341
410
 
342
411
  def _calculate_today_remaining_time(self, now: datetime):
343
412
  """Calculates the remaining playing time for today."""
344
- self.stats_update_failed = True # Assume failure until success
413
+ self.stats_update_failed = True # Assume failure until success
345
414
  try:
346
- minutes_in_day = 1440 # 24 * 60
415
+ minutes_in_day = 1440 # 24 * 60
347
416
  current_minutes_past_midnight = now.hour * 60 + now.minute
348
417
 
349
418
  if self.limit_time in (-1, None):
350
419
  # No play limit, so remaining time is until end of day.
351
- time_remaining_by_play_limit = minutes_in_day - current_minutes_past_midnight
420
+ time_remaining_by_play_limit = (
421
+ minutes_in_day - current_minutes_past_midnight
422
+ )
352
423
  else:
353
424
  time_remaining_by_play_limit = self.limit_time - self.today_playing_time
354
425
 
355
426
  # 2. Calculate remaining time until bedtime
356
- if self.bedtime_alarm and self.bedtime_alarm != time(hour=0, minute=0) and self.alarms_enabled:
427
+ if (
428
+ self.bedtime_alarm
429
+ and self.bedtime_alarm != time(hour=0, minute=0)
430
+ and self.alarms_enabled
431
+ ):
357
432
  bedtime_dt = datetime.combine(now.date(), self.bedtime_alarm)
358
- if bedtime_dt > now: # Bedtime is in the future today
433
+ if bedtime_dt > now: # Bedtime is in the future today
359
434
  time_remaining_by_bedtime = (bedtime_dt - now).total_seconds() / 60
360
- else: # Bedtime has passed
435
+ else: # Bedtime has passed
361
436
  time_remaining_by_bedtime = 0.0
362
437
  else:
363
- time_remaining_by_bedtime = minutes_in_day - current_minutes_past_midnight
438
+ time_remaining_by_bedtime = (
439
+ minutes_in_day - current_minutes_past_midnight
440
+ )
364
441
 
365
442
  # Effective remaining time is the minimum of the two constraints
366
- effective_remaining_time = min(time_remaining_by_play_limit, time_remaining_by_bedtime)
443
+ effective_remaining_time = min(
444
+ time_remaining_by_play_limit, time_remaining_by_bedtime
445
+ )
367
446
  self.today_time_remaining = int(max(0.0, effective_remaining_time))
368
- _LOGGER.debug("Calculated today's remaining time: %s minutes", self.today_time_remaining)
447
+ _LOGGER.debug(
448
+ "Calculated today's remaining time: %s minutes",
449
+ self.today_time_remaining,
450
+ )
369
451
  self.stats_update_failed = False
370
452
  except (ValueError, TypeError, AttributeError) as err:
371
- _LOGGER.warning("Unable to calculate remaining time for device %s: %s", self.name, err)
453
+ _LOGGER.warning(
454
+ "Unable to calculate remaining time for device %s: %s", self.name, err
455
+ )
372
456
 
373
457
  async def _get_parental_control_setting(self, now: datetime):
374
458
  """Retreives parental control settings from the API."""
@@ -383,7 +467,7 @@ class Device:
383
467
  """Retrieve daily summaries."""
384
468
  _LOGGER.debug(">> Device._get_daily_summaries()")
385
469
  response = await self._api.async_get_device_daily_summaries(
386
- device_id = self.device_id
470
+ device_id=self.device_id
387
471
  )
388
472
  self.daily_summaries = response["json"]["dailySummaries"]
389
473
  _LOGGER.debug("New daily summary %s", self.daily_summaries)
@@ -395,14 +479,16 @@ class Device:
395
479
  if self.alarms_enabled is not None:
396
480
  # first refresh can come from self.extra without http request
397
481
  response = await self._api.async_get_account_device(
398
- device_id = self.device_id
482
+ device_id=self.device_id
399
483
  )
400
484
  self.extra = response["json"]["ownedDevice"]["device"]
401
485
  status = self.extra["alarmSetting"]["visibility"]
402
486
  self.alarms_enabled = status == str(AlarmSettingState.VISIBLE)
403
- _LOGGER.debug("Cached alarms enabled to state %s for device %s",
404
- self.alarms_enabled,
405
- self.device_id)
487
+ _LOGGER.debug(
488
+ "Cached alarms enabled to state %s for device %s",
489
+ self.alarms_enabled,
490
+ self.device_id,
491
+ )
406
492
 
407
493
  async def get_monthly_summary(self, search_date: datetime = None) -> dict | None:
408
494
  """Gets the monthly summary."""
@@ -420,69 +506,93 @@ class Device:
420
506
  available_summaries = response["json"]["available"]
421
507
  _LOGGER.debug("Available monthly summaries: %s", available_summaries)
422
508
  if not available_summaries:
423
- _LOGGER.debug("No monthly summaries available for device %s", self.device_id)
509
+ _LOGGER.debug(
510
+ "No monthly summaries available for device %s", self.device_id
511
+ )
424
512
  return None
425
513
  # Use the most recent available summary
426
514
  available_summary = available_summaries[0]
427
- search_date = datetime.strptime(f"{available_summary['year']}-{available_summary['month']}-01", "%Y-%m-%d")
428
- _LOGGER.debug("Using search date %s for monthly summary request", search_date)
515
+ search_date = datetime.strptime(
516
+ f"{available_summary['year']}-{available_summary['month']}-01",
517
+ "%Y-%m-%d",
518
+ )
519
+ _LOGGER.debug(
520
+ "Using search date %s for monthly summary request", search_date
521
+ )
429
522
  latest = True
430
523
 
431
524
  try:
432
525
  response = await self._api.async_get_device_monthly_summary(
433
- device_id=self.device_id,
434
- year=search_date.year,
435
- month=search_date.month
526
+ device_id=self.device_id, year=search_date.year, month=search_date.month
436
527
  )
437
528
  except HttpException as exc:
438
- _LOGGER.warning("HTTP Exception raised while getting monthly summary for device %s: %s",
439
- self.device_id,
440
- exc)
529
+ _LOGGER.warning(
530
+ "HTTP Exception raised while getting monthly summary for device %s: %s",
531
+ self.device_id,
532
+ exc,
533
+ )
441
534
  return None
442
535
  else:
443
- _LOGGER.debug("Monthly summary query complete for device %s: %s",
444
- self.device_id,
445
- response["json"]["summary"])
536
+ _LOGGER.debug(
537
+ "Monthly summary query complete for device %s: %s",
538
+ self.device_id,
539
+ response["json"]["summary"],
540
+ )
446
541
  if latest:
447
542
  self.last_month_summary = summary = response["json"]["summary"]
543
+ # Generate player objects
544
+ for player in response.get("json", {}).get("summary", {}).get("players", []):
545
+ profile = player.get("profile")
546
+ if not profile or not profile.get("playerId"):
547
+ continue
548
+ player_id = profile["playerId"]
549
+ if player_id not in self.players:
550
+ self.players[player_id] = Player.from_profile(profile)
551
+ self.players[player_id].month_summary = player.get("summary", {})
448
552
  return summary
449
553
  return response["json"]["summary"]
450
554
 
451
-
452
555
  def get_date_summary(self, input_date: datetime = datetime.now()) -> dict:
453
556
  """Returns usage for a given date."""
454
557
  if not self.daily_summaries:
455
558
  raise ValueError("No daily summaries available to search.")
456
559
  summary = [
457
- x for x in self.daily_summaries
458
- if x["date"] == input_date.strftime('%Y-%m-%d')
560
+ x
561
+ for x in self.daily_summaries
562
+ if x["date"] == input_date.strftime("%Y-%m-%d")
459
563
  ]
460
564
  if len(summary) == 0:
461
565
  input_date -= timedelta(days=1)
462
566
  summary = [
463
- x for x in self.daily_summaries
464
- if x["date"] == input_date.strftime('%Y-%m-%d')
465
- ]
567
+ x
568
+ for x in self.daily_summaries
569
+ if x["date"] == input_date.strftime("%Y-%m-%d")
570
+ ]
466
571
  if len(summary) == 0:
467
- raise ValueError(f"A summary for the given date {input_date} does not exist")
572
+ raise ValueError(
573
+ f"A summary for the given date {input_date} does not exist"
574
+ )
468
575
  return summary
469
576
 
470
577
  def get_application(self, application_id: str) -> Application:
471
578
  """Returns a single application."""
472
- app = next((app for app in self.applications if app.application_id == application_id), None)
579
+ app = next(
580
+ (app for app in self.applications if app.application_id == application_id),
581
+ None,
582
+ )
473
583
  if app:
474
584
  return app
475
585
  raise ValueError(f"Application with id {application_id} not found.")
476
586
 
477
587
  def get_player(self, player_id: str) -> Player:
478
588
  """Returns a player."""
479
- player = next((p for p in self.players if p.player_id == player_id), None)
589
+ player = self.players.get(player_id)
480
590
  if player:
481
591
  return player
482
592
  raise ValueError(f"Player with id {player_id} not found.")
483
593
 
484
594
  @classmethod
485
- async def from_devices_response(cls, raw: dict, api) -> list['Device']:
595
+ async def from_devices_response(cls, raw: dict, api) -> list["Device"]:
486
596
  """Parses a device request response body."""
487
597
  _LOGGER.debug("Parsing device list response")
488
598
  if "ownedDevices" not in raw.keys():
@@ -500,7 +610,7 @@ class Device:
500
610
  return devices
501
611
 
502
612
  @classmethod
503
- def from_device_response(cls, raw: dict, api) -> 'Device':
613
+ def from_device_response(cls, raw: dict, api) -> "Device":
504
614
  """Parses a single device request response body."""
505
615
  _LOGGER.debug("Parsing device response")
506
616
  if "deviceId" not in raw.keys():
@@ -2,8 +2,10 @@
2
2
 
3
3
  from enum import Enum, StrEnum
4
4
 
5
+
5
6
  class AlarmSettingState(Enum):
6
7
  """Alarm setting states."""
8
+
7
9
  SUCCESS = 0
8
10
  TO_VISIBLE = 1
9
11
  TO_INVISIBLE = 2
@@ -13,16 +15,20 @@ class AlarmSettingState(Enum):
13
15
  def __str__(self) -> str:
14
16
  return self.name
15
17
 
18
+
16
19
  class RestrictionMode(Enum):
17
20
  """Restriction modes."""
21
+
18
22
  FORCED_TERMINATION = 0
19
23
  ALARM = 1
20
24
 
21
25
  def __str__(self) -> str:
22
26
  return self.name
23
27
 
28
+
24
29
  class DeviceTimerMode(StrEnum):
25
30
  """Device timer modes."""
31
+
26
32
  DAILY = "DAILY"
27
33
  EACH_DAY_OF_THE_WEEK = "EACH_DAY_OF_THE_WEEK"
28
34