pynintendoparental 2.2.3__tar.gz → 2.3.2__tar.gz

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.
Files changed (33) hide show
  1. pynintendoparental-2.3.2/PKG-INFO +295 -0
  2. pynintendoparental-2.3.2/README.md +255 -0
  3. pynintendoparental-2.3.2/pynintendoparental/_version.py +1 -0
  4. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/api.py +1 -1
  5. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/const.py +2 -2
  6. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/device.py +18 -7
  7. pynintendoparental-2.3.2/pynintendoparental.egg-info/PKG-INFO +295 -0
  8. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental.egg-info/requires.txt +4 -4
  9. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/setup.py +4 -4
  10. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/tests/test_api.py +1 -1
  11. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/tests/test_applications.py +4 -4
  12. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/tests/test_device.py +403 -17
  13. pynintendoparental-2.2.3/PKG-INFO +0 -60
  14. pynintendoparental-2.2.3/README.md +0 -20
  15. pynintendoparental-2.2.3/pynintendoparental/_version.py +0 -1
  16. pynintendoparental-2.2.3/pynintendoparental.egg-info/PKG-INFO +0 -60
  17. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/LICENSE +0 -0
  18. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/__init__.py +0 -0
  19. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/application.py +0 -0
  20. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/authenticator.py +0 -0
  21. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/enum.py +0 -0
  22. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/exceptions.py +0 -0
  23. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/player.py +0 -0
  24. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/py.typed +0 -0
  25. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental/utils.py +0 -0
  26. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental.egg-info/SOURCES.txt +0 -0
  27. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental.egg-info/dependency_links.txt +0 -0
  28. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pynintendoparental.egg-info/top_level.txt +0 -0
  29. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/pyproject.toml +0 -0
  30. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/setup.cfg +0 -0
  31. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/tests/test_enum.py +0 -0
  32. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/tests/test_init.py +0 -0
  33. {pynintendoparental-2.2.3 → pynintendoparental-2.3.2}/tests/test_player.py +0 -0
@@ -0,0 +1,295 @@
1
+ Metadata-Version: 2.4
2
+ Name: pynintendoparental
3
+ Version: 2.3.2
4
+ Summary: A Python module to interact with Nintendo Parental Controls
5
+ Home-page: http://github.com/pantherale0/pynintendoparental
6
+ Author: pantherale0
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8, <4
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: pynintendoauth==1.0.2
15
+ Provides-Extra: dev
16
+ Requires-Dist: aiofiles<26,>=23; extra == "dev"
17
+ Requires-Dist: bandit<1.10,>=1.7; extra == "dev"
18
+ Requires-Dist: black<26,>=23; extra == "dev"
19
+ Requires-Dist: build<1.4,>=0.10; extra == "dev"
20
+ Requires-Dist: Faker<41,>=38; extra == "dev"
21
+ Requires-Dist: flake8<8,>=6; extra == "dev"
22
+ Requires-Dist: isort<8,>=5; extra == "dev"
23
+ Requires-Dist: mypy<1.20,>=1.5; extra == "dev"
24
+ Requires-Dist: pytest<10,>=7; extra == "dev"
25
+ Requires-Dist: pytest-cov<8,>=4; extra == "dev"
26
+ Requires-Dist: pytest-asyncio<2.0,>=0.21; extra == "dev"
27
+ Requires-Dist: syrupy<6,>=5; extra == "dev"
28
+ Requires-Dist: twine<7,>=4; extra == "dev"
29
+ Dynamic: author
30
+ Dynamic: classifier
31
+ Dynamic: description
32
+ Dynamic: description-content-type
33
+ Dynamic: home-page
34
+ Dynamic: license
35
+ Dynamic: license-file
36
+ Dynamic: provides-extra
37
+ Dynamic: requires-dist
38
+ Dynamic: requires-python
39
+ Dynamic: summary
40
+
41
+ <div align="center">
42
+
43
+ # Python Nintendo Parental Controls
44
+
45
+ A simple, Python API to connect to Nintendo Switch Parental Controls.
46
+
47
+ [![Test Status](https://github.com/pantherale0/pynintendoparental/actions/workflows/test.yml/badge.svg)](https://github.com/pantherale0/pynintendoparental/actions/workflows/test.yml)
48
+ [![Coverage Status](https://codecov.io/gh/pantherale0/pynintendoparental/branch/main/graph/badge.svg?token=SO6VDF7951)](https://codecov.io/gh/pantherale0/pynintendoparental)
49
+ [![PyPi](https://img.shields.io/pypi/v/pynintendoparental)](https://pypi.org/project/pynintendoparental)
50
+ [![Licence](https://img.shields.io/github/license/pantherale0/pynintendoparental)](LICENSE)
51
+
52
+ <a href="https://www.buymeacoffee.com/pantherale0" target="_blank" title="buymeacoffee">
53
+ <img src="https://iili.io/JoQ1MeS.md.png" alt="buymeacoffee-yellow-badge" style="width: 104px;">
54
+ </a>
55
+
56
+ </div>
57
+
58
+ ## Install
59
+
60
+ ```bash
61
+ # Install tool
62
+ pip3 install pynintendoparental
63
+
64
+ # Install locally
65
+ just install
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ ### Authentication
71
+
72
+ This library requires authentication with Nintendo's servers. The `pynintendoparental.Authenticator` class handles this. There are two ways to authenticate:
73
+
74
+ **Method 1: Using a Session Token**
75
+
76
+ If you already have a `session_token`, you can pass it to the `Authenticator`:
77
+
78
+ ```python
79
+ import aiohttp
80
+ from pynintendoparental.authenticator import Authenticator
81
+
82
+ async with aiohttp.ClientSession() as session:
83
+ session_token = "YOUR_SESSION_TOKEN"
84
+ auth = Authenticator(session_token, session)
85
+ await auth.async_complete_login(use_session_token=True)
86
+ ```
87
+
88
+ **Method 2: Interactive Login**
89
+
90
+ If you don't have a `session_token`, you can perform an interactive login to obtain one. The library will provide a login URL. You need to open this URL in your browser, log in to your Nintendo Account, and then copy the URL of the "Select this person" button and paste it back into your application.
91
+
92
+ Here is an example of how to do this:
93
+
94
+ ```python
95
+ import asyncio
96
+ import aiohttp
97
+ from pynintendoparental.authenticator import Authenticator
98
+
99
+ async def interactive_login():
100
+ """Performs an interactive login to get a session token."""
101
+ async with aiohttp.ClientSession() as session:
102
+ auth = Authenticator(client_session=session)
103
+ print("Please open the following URL in your browser:")
104
+ print(auth.login_url)
105
+
106
+ response_url = input("Please paste the URL you were redirected to: ")
107
+ await auth.async_complete_login(response_url)
108
+
109
+ print(f"Login successful! Your session token is: {auth.session_token}")
110
+ print("You can save this token to avoid logging in next time.")
111
+ return auth
112
+
113
+ async def main():
114
+ auth = await interactive_login()
115
+ # You can now use the 'auth' object with NintendoParental
116
+ # nintendo = await NintendoParental.create(auth)
117
+
118
+ if __name__ == "__main__":
119
+ asyncio.run(main())
120
+ ```
121
+
122
+ ### Basic Usage
123
+
124
+ Here's a simple example of how to use `pynintendoparental`:
125
+
126
+ ```python
127
+ import asyncio
128
+ import aiohttp
129
+
130
+ from pynintendoparental import NintendoParental
131
+ from pynintendoparental.authenticator import Authenticator
132
+
133
+ async def main():
134
+ """The main function."""
135
+ session_token = "YOUR_SESSION_TOKEN" # Replace with your session token
136
+
137
+ async with aiohttp.ClientSession() as session:
138
+ auth = Authenticator(session_token, session)
139
+ await auth.async_complete_login(use_session_token=True)
140
+ nintendo = await NintendoParental.create(auth)
141
+
142
+ # Iterate through all devices
143
+ for device in nintendo.devices.values():
144
+ print(f"Device: {device.name}")
145
+ print(f" ID: {device.device_id}")
146
+ print(f" Model: {device.model}")
147
+ print(f" Playtime limit: {device.limit_time} minutes")
148
+ print(f" Today's playing time: {device.today_playing_time} minutes")
149
+ print(f" Time remaining: {device.today_time_remaining} minutes")
150
+
151
+ # Update the daily playtime limit
152
+ # await device.update_max_daily_playtime(180) # Set to 3 hours
153
+
154
+ if __name__ == "__main__":
155
+ asyncio.run(main())
156
+ ```
157
+
158
+ ### The `NintendoParental` object
159
+
160
+ The main entry point is the `NintendoParental` object. You create it using the asynchronous class method `create`:
161
+
162
+ ```python
163
+ nintendo = await NintendoParental.create(auth, timezone="Europe/London", lang="en-GB")
164
+ ```
165
+
166
+ - `auth`: An `Authenticator` instance.
167
+ - `timezone`: (Optional) The timezone to use for API requests. Defaults to "Europe/London".
168
+ - `lang`: (Optional) The language to use for API requests. Defaults to "en-GB".
169
+
170
+ After creation, the `NintendoParental` object will have a `devices` property, which is a dictionary of `Device` objects, keyed by their device ID.
171
+
172
+ ### The `Device` object
173
+
174
+ The `Device` object contains information about a specific Nintendo Switch console and allows you to control it.
175
+
176
+ #### Properties
177
+
178
+ - `name` (str): The name of the device.
179
+ - `device_id` (str): The unique ID of the device.
180
+ - `model` (str): The model of the device (e.g., "Switch").
181
+ - `limit_time` (int): The daily playtime limit in minutes. `-1` if no limit is set.
182
+ - `today_playing_time` (int): The total playing time for the current day in minutes.
183
+ - `today_time_remaining` (int): The remaining playtime for the current day in minutes.
184
+ - `players` (dict): A dictionary of `Player` objects associated with the device, keyed by player ID.
185
+ - `applications` (dict): A dictionary of `Application` objects that have been played on the device.
186
+ - `timer_mode` (DeviceTimerMode): The current timer mode (`DAILY` or `EACH_DAY_OF_THE_WEEK`).
187
+ - `bedtime_alarm` (datetime.time): The time when the bedtime alarm will sound.
188
+ - `bedtime_end` (datetime.time): The time when the bedtime restrictions end. Set to `datetime.time(0, 0)` if disabled.
189
+ - `forced_termination_mode` (bool): `True` if the software will be suspended when the playtime limit is reached.
190
+ - `alarms_enabled` (bool): `True` if alarms are enabled.
191
+ - `last_sync` (float): The timestamp of the last sync with the device.
192
+
193
+ #### Methods
194
+
195
+ All methods are asynchronous.
196
+
197
+ - `update()`: Refreshes the device data from the Nintendo API.
198
+ - `add_device_callback(callback)`: Adds a callback that will be called when the device state changes.
199
+ - `remove_device_callback(callback)`: Removes a previously added callback.
200
+ - `set_new_pin(pin: str)`: Sets a new PIN for the parental controls.
201
+ - `add_extra_time(minutes: int)`: Adds extra playing time for the current day.
202
+ - `update_max_daily_playtime(minutes: int)`: Sets the daily playtime limit. Use `-1` to remove the limit.
203
+ - `set_restriction_mode(mode: RestrictionMode)`: Sets the restriction mode.
204
+ - `RestrictionMode.FORCED_TERMINATION`: The software will be suspended when the playtime limit is reached.
205
+ - `RestrictionMode.ALARM`: An alarm will be shown, but the software will not be suspended.
206
+ - `set_bedtime_alarm(value: datetime.time)`: Sets the bedtime alarm.
207
+ - `set_bedtime_end_time(value: datetime.time)`: Sets the time when bedtime restrictions end. Pass `datetime.time(0, 0)` to disable.
208
+ - `set_timer_mode(mode: DeviceTimerMode)`: Sets the timer mode.
209
+ - `DeviceTimerMode.DAILY`: A single playtime limit for all days.
210
+ - `DeviceTimerMode.EACH_DAY_OF_THE_WEEK`: Different playtime limits for each day of the week.
211
+ - `set_daily_restrictions(...)`: Sets the restrictions for a specific day of the week (when `timer_mode` is `EACH_DAY_OF_THE_WEEK`).
212
+ - `set_functional_restriction_level(level: FunctionalRestrictionLevel)`: Sets the content restriction level based on age ratings.
213
+ - `FunctionalRestrictionLevel.CHILD`, `TEEN`, `YOUNG_ADULT`, `CUSTOM`.
214
+ - `get_monthly_summary(search_date: datetime = None)`: Gets the monthly summary for a given month.
215
+ - `get_date_summary(input_date: datetime = datetime.now())`: Gets the usage summary for a given date.
216
+ - `get_application(application_id: str)`: Gets an `Application` object by ID.
217
+ - `get_player(player_id: str)`: Gets a `Player` object by ID.
218
+
219
+ ### The `Player` object
220
+
221
+ The `Player` object holds information about a single user on the Nintendo Switch. `Player` objects are accessed via the `players` dictionary on a `Device` object.
222
+
223
+ #### Properties
224
+
225
+ - `player_id` (str): The player's unique ID.
226
+ - `nickname` (str): The player's nickname.
227
+ - `player_image` (str): URL to the player's Mii image.
228
+ - `playing_time` (int): The player's total playing time for the current day in minutes.
229
+ - `apps` (list): A list of applications that the player has played today. Each entry is a dictionary containing details about the played application, including the `applicationId`.
230
+
231
+ ### The `Application` object
232
+
233
+ The `Application` object contains information about a specific game or application on the Nintendo Switch. `Application` objects are accessed via the `applications` dictionary on a `Device` object.
234
+
235
+ #### Properties
236
+
237
+ - `application_id` (str): The application's unique ID.
238
+ - `name` (str): The name of the application.
239
+ - `image_url` (str): URL to the application's icon.
240
+ - `safe_launch_setting` (SafeLaunchSetting): The application's status on the console's Allow List. This allows an application to bypass general age/content restrictions. (`NONE`, `ALLOW`).
241
+ - `today_time_played` (int): The total time the application has been played today by all players in minutes.
242
+
243
+ #### Methods
244
+
245
+ All methods are asynchronous.
246
+
247
+ - `set_safe_launch_setting(safe_launch_setting: SafeLaunchSetting)`: Updates the application's status on the console's Allow List. This allows an application to bypass general age/content restrictions.
248
+ - `add_application_callback(callback)`: Adds a callback that will be called when the application's state changes.
249
+ - `remove_application_callback(callback)`: Removes a previously added callback.
250
+
251
+ ### Full Example
252
+
253
+ This example demonstrates how to log in, list devices, and view player and application information.
254
+
255
+ ```python
256
+ import asyncio
257
+ import aiohttp
258
+
259
+ from pynintendoparental import NintendoParental
260
+ from pynintendoparental.authenticator import Authenticator
261
+ from pynintendoparental.enum import RestrictionMode
262
+
263
+ async def main():
264
+ """The main function."""
265
+ session_token = "YOUR_SESSION_TOKEN"
266
+
267
+ async with aiohttp.ClientSession() as session:
268
+ auth = Authenticator(session_token, session)
269
+ await auth.async_complete_login(use_session_token=True)
270
+ nintendo = await NintendoParental.create(auth)
271
+
272
+ # Get the first device
273
+ device = list(nintendo.devices.values())[0]
274
+ print(f"Device: {device.name}")
275
+
276
+ # Change the restriction mode to suspend the software when playtime is up
277
+ await device.set_restriction_mode(RestrictionMode.FORCED_TERMINATION)
278
+ print("Restriction mode set to forced termination.")
279
+
280
+ # Print today's summary for each player
281
+ for player in device.players.values():
282
+ print(f"Player: {player.nickname}")
283
+ print(f" Playing time: {player.playing_time} minutes")
284
+
285
+ # Print applications played by the player
286
+ for app in player.apps:
287
+ app_id = app['applicationId']
288
+ # Get the application object from the device
289
+ application = device.get_application(app_id)
290
+ print(f" - Played {application.name} for {app['playingTime']} minutes")
291
+
292
+ if __name__ == "__main__":
293
+ asyncio.run(main())
294
+
295
+ ```
@@ -0,0 +1,255 @@
1
+ <div align="center">
2
+
3
+ # Python Nintendo Parental Controls
4
+
5
+ A simple, Python API to connect to Nintendo Switch Parental Controls.
6
+
7
+ [![Test Status](https://github.com/pantherale0/pynintendoparental/actions/workflows/test.yml/badge.svg)](https://github.com/pantherale0/pynintendoparental/actions/workflows/test.yml)
8
+ [![Coverage Status](https://codecov.io/gh/pantherale0/pynintendoparental/branch/main/graph/badge.svg?token=SO6VDF7951)](https://codecov.io/gh/pantherale0/pynintendoparental)
9
+ [![PyPi](https://img.shields.io/pypi/v/pynintendoparental)](https://pypi.org/project/pynintendoparental)
10
+ [![Licence](https://img.shields.io/github/license/pantherale0/pynintendoparental)](LICENSE)
11
+
12
+ <a href="https://www.buymeacoffee.com/pantherale0" target="_blank" title="buymeacoffee">
13
+ <img src="https://iili.io/JoQ1MeS.md.png" alt="buymeacoffee-yellow-badge" style="width: 104px;">
14
+ </a>
15
+
16
+ </div>
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ # Install tool
22
+ pip3 install pynintendoparental
23
+
24
+ # Install locally
25
+ just install
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### Authentication
31
+
32
+ This library requires authentication with Nintendo's servers. The `pynintendoparental.Authenticator` class handles this. There are two ways to authenticate:
33
+
34
+ **Method 1: Using a Session Token**
35
+
36
+ If you already have a `session_token`, you can pass it to the `Authenticator`:
37
+
38
+ ```python
39
+ import aiohttp
40
+ from pynintendoparental.authenticator import Authenticator
41
+
42
+ async with aiohttp.ClientSession() as session:
43
+ session_token = "YOUR_SESSION_TOKEN"
44
+ auth = Authenticator(session_token, session)
45
+ await auth.async_complete_login(use_session_token=True)
46
+ ```
47
+
48
+ **Method 2: Interactive Login**
49
+
50
+ If you don't have a `session_token`, you can perform an interactive login to obtain one. The library will provide a login URL. You need to open this URL in your browser, log in to your Nintendo Account, and then copy the URL of the "Select this person" button and paste it back into your application.
51
+
52
+ Here is an example of how to do this:
53
+
54
+ ```python
55
+ import asyncio
56
+ import aiohttp
57
+ from pynintendoparental.authenticator import Authenticator
58
+
59
+ async def interactive_login():
60
+ """Performs an interactive login to get a session token."""
61
+ async with aiohttp.ClientSession() as session:
62
+ auth = Authenticator(client_session=session)
63
+ print("Please open the following URL in your browser:")
64
+ print(auth.login_url)
65
+
66
+ response_url = input("Please paste the URL you were redirected to: ")
67
+ await auth.async_complete_login(response_url)
68
+
69
+ print(f"Login successful! Your session token is: {auth.session_token}")
70
+ print("You can save this token to avoid logging in next time.")
71
+ return auth
72
+
73
+ async def main():
74
+ auth = await interactive_login()
75
+ # You can now use the 'auth' object with NintendoParental
76
+ # nintendo = await NintendoParental.create(auth)
77
+
78
+ if __name__ == "__main__":
79
+ asyncio.run(main())
80
+ ```
81
+
82
+ ### Basic Usage
83
+
84
+ Here's a simple example of how to use `pynintendoparental`:
85
+
86
+ ```python
87
+ import asyncio
88
+ import aiohttp
89
+
90
+ from pynintendoparental import NintendoParental
91
+ from pynintendoparental.authenticator import Authenticator
92
+
93
+ async def main():
94
+ """The main function."""
95
+ session_token = "YOUR_SESSION_TOKEN" # Replace with your session token
96
+
97
+ async with aiohttp.ClientSession() as session:
98
+ auth = Authenticator(session_token, session)
99
+ await auth.async_complete_login(use_session_token=True)
100
+ nintendo = await NintendoParental.create(auth)
101
+
102
+ # Iterate through all devices
103
+ for device in nintendo.devices.values():
104
+ print(f"Device: {device.name}")
105
+ print(f" ID: {device.device_id}")
106
+ print(f" Model: {device.model}")
107
+ print(f" Playtime limit: {device.limit_time} minutes")
108
+ print(f" Today's playing time: {device.today_playing_time} minutes")
109
+ print(f" Time remaining: {device.today_time_remaining} minutes")
110
+
111
+ # Update the daily playtime limit
112
+ # await device.update_max_daily_playtime(180) # Set to 3 hours
113
+
114
+ if __name__ == "__main__":
115
+ asyncio.run(main())
116
+ ```
117
+
118
+ ### The `NintendoParental` object
119
+
120
+ The main entry point is the `NintendoParental` object. You create it using the asynchronous class method `create`:
121
+
122
+ ```python
123
+ nintendo = await NintendoParental.create(auth, timezone="Europe/London", lang="en-GB")
124
+ ```
125
+
126
+ - `auth`: An `Authenticator` instance.
127
+ - `timezone`: (Optional) The timezone to use for API requests. Defaults to "Europe/London".
128
+ - `lang`: (Optional) The language to use for API requests. Defaults to "en-GB".
129
+
130
+ After creation, the `NintendoParental` object will have a `devices` property, which is a dictionary of `Device` objects, keyed by their device ID.
131
+
132
+ ### The `Device` object
133
+
134
+ The `Device` object contains information about a specific Nintendo Switch console and allows you to control it.
135
+
136
+ #### Properties
137
+
138
+ - `name` (str): The name of the device.
139
+ - `device_id` (str): The unique ID of the device.
140
+ - `model` (str): The model of the device (e.g., "Switch").
141
+ - `limit_time` (int): The daily playtime limit in minutes. `-1` if no limit is set.
142
+ - `today_playing_time` (int): The total playing time for the current day in minutes.
143
+ - `today_time_remaining` (int): The remaining playtime for the current day in minutes.
144
+ - `players` (dict): A dictionary of `Player` objects associated with the device, keyed by player ID.
145
+ - `applications` (dict): A dictionary of `Application` objects that have been played on the device.
146
+ - `timer_mode` (DeviceTimerMode): The current timer mode (`DAILY` or `EACH_DAY_OF_THE_WEEK`).
147
+ - `bedtime_alarm` (datetime.time): The time when the bedtime alarm will sound.
148
+ - `bedtime_end` (datetime.time): The time when the bedtime restrictions end. Set to `datetime.time(0, 0)` if disabled.
149
+ - `forced_termination_mode` (bool): `True` if the software will be suspended when the playtime limit is reached.
150
+ - `alarms_enabled` (bool): `True` if alarms are enabled.
151
+ - `last_sync` (float): The timestamp of the last sync with the device.
152
+
153
+ #### Methods
154
+
155
+ All methods are asynchronous.
156
+
157
+ - `update()`: Refreshes the device data from the Nintendo API.
158
+ - `add_device_callback(callback)`: Adds a callback that will be called when the device state changes.
159
+ - `remove_device_callback(callback)`: Removes a previously added callback.
160
+ - `set_new_pin(pin: str)`: Sets a new PIN for the parental controls.
161
+ - `add_extra_time(minutes: int)`: Adds extra playing time for the current day.
162
+ - `update_max_daily_playtime(minutes: int)`: Sets the daily playtime limit. Use `-1` to remove the limit.
163
+ - `set_restriction_mode(mode: RestrictionMode)`: Sets the restriction mode.
164
+ - `RestrictionMode.FORCED_TERMINATION`: The software will be suspended when the playtime limit is reached.
165
+ - `RestrictionMode.ALARM`: An alarm will be shown, but the software will not be suspended.
166
+ - `set_bedtime_alarm(value: datetime.time)`: Sets the bedtime alarm.
167
+ - `set_bedtime_end_time(value: datetime.time)`: Sets the time when bedtime restrictions end. Pass `datetime.time(0, 0)` to disable.
168
+ - `set_timer_mode(mode: DeviceTimerMode)`: Sets the timer mode.
169
+ - `DeviceTimerMode.DAILY`: A single playtime limit for all days.
170
+ - `DeviceTimerMode.EACH_DAY_OF_THE_WEEK`: Different playtime limits for each day of the week.
171
+ - `set_daily_restrictions(...)`: Sets the restrictions for a specific day of the week (when `timer_mode` is `EACH_DAY_OF_THE_WEEK`).
172
+ - `set_functional_restriction_level(level: FunctionalRestrictionLevel)`: Sets the content restriction level based on age ratings.
173
+ - `FunctionalRestrictionLevel.CHILD`, `TEEN`, `YOUNG_ADULT`, `CUSTOM`.
174
+ - `get_monthly_summary(search_date: datetime = None)`: Gets the monthly summary for a given month.
175
+ - `get_date_summary(input_date: datetime = datetime.now())`: Gets the usage summary for a given date.
176
+ - `get_application(application_id: str)`: Gets an `Application` object by ID.
177
+ - `get_player(player_id: str)`: Gets a `Player` object by ID.
178
+
179
+ ### The `Player` object
180
+
181
+ The `Player` object holds information about a single user on the Nintendo Switch. `Player` objects are accessed via the `players` dictionary on a `Device` object.
182
+
183
+ #### Properties
184
+
185
+ - `player_id` (str): The player's unique ID.
186
+ - `nickname` (str): The player's nickname.
187
+ - `player_image` (str): URL to the player's Mii image.
188
+ - `playing_time` (int): The player's total playing time for the current day in minutes.
189
+ - `apps` (list): A list of applications that the player has played today. Each entry is a dictionary containing details about the played application, including the `applicationId`.
190
+
191
+ ### The `Application` object
192
+
193
+ The `Application` object contains information about a specific game or application on the Nintendo Switch. `Application` objects are accessed via the `applications` dictionary on a `Device` object.
194
+
195
+ #### Properties
196
+
197
+ - `application_id` (str): The application's unique ID.
198
+ - `name` (str): The name of the application.
199
+ - `image_url` (str): URL to the application's icon.
200
+ - `safe_launch_setting` (SafeLaunchSetting): The application's status on the console's Allow List. This allows an application to bypass general age/content restrictions. (`NONE`, `ALLOW`).
201
+ - `today_time_played` (int): The total time the application has been played today by all players in minutes.
202
+
203
+ #### Methods
204
+
205
+ All methods are asynchronous.
206
+
207
+ - `set_safe_launch_setting(safe_launch_setting: SafeLaunchSetting)`: Updates the application's status on the console's Allow List. This allows an application to bypass general age/content restrictions.
208
+ - `add_application_callback(callback)`: Adds a callback that will be called when the application's state changes.
209
+ - `remove_application_callback(callback)`: Removes a previously added callback.
210
+
211
+ ### Full Example
212
+
213
+ This example demonstrates how to log in, list devices, and view player and application information.
214
+
215
+ ```python
216
+ import asyncio
217
+ import aiohttp
218
+
219
+ from pynintendoparental import NintendoParental
220
+ from pynintendoparental.authenticator import Authenticator
221
+ from pynintendoparental.enum import RestrictionMode
222
+
223
+ async def main():
224
+ """The main function."""
225
+ session_token = "YOUR_SESSION_TOKEN"
226
+
227
+ async with aiohttp.ClientSession() as session:
228
+ auth = Authenticator(session_token, session)
229
+ await auth.async_complete_login(use_session_token=True)
230
+ nintendo = await NintendoParental.create(auth)
231
+
232
+ # Get the first device
233
+ device = list(nintendo.devices.values())[0]
234
+ print(f"Device: {device.name}")
235
+
236
+ # Change the restriction mode to suspend the software when playtime is up
237
+ await device.set_restriction_mode(RestrictionMode.FORCED_TERMINATION)
238
+ print("Restriction mode set to forced termination.")
239
+
240
+ # Print today's summary for each player
241
+ for player in device.players.values():
242
+ print(f"Player: {player.nickname}")
243
+ print(f" Playing time: {player.playing_time} minutes")
244
+
245
+ # Print applications played by the player
246
+ for app in player.apps:
247
+ app_id = app['applicationId']
248
+ # Get the application object from the device
249
+ application = device.get_application(app_id)
250
+ print(f" - Played {application.name} for {app['playingTime']} minutes")
251
+
252
+ if __name__ == "__main__":
253
+ asyncio.run(main())
254
+
255
+ ```
@@ -0,0 +1 @@
1
+ __version__ = "2.3.2"
@@ -190,7 +190,7 @@ class Api:
190
190
  """Update device unlock code."""
191
191
  return await self.send_request(
192
192
  endpoint="update_unlock_code",
193
- body={"deviceId": device_id, "unlockCode": new_code},
193
+ body={"deviceId": device_id, "unlockCode": str(new_code)},
194
194
  )
195
195
 
196
196
  async def async_update_extra_playing_time(
@@ -6,8 +6,8 @@ import logging
6
6
  _LOGGER = logging.getLogger(__package__)
7
7
  CLIENT_ID = "54789befb391a838"
8
8
  MOBILE_APP_PKG = "com.nintendo.znma"
9
- MOBILE_APP_VERSION = "2.3.1"
10
- MOBILE_APP_BUILD = "620"
9
+ MOBILE_APP_VERSION = "2.3.2"
10
+ MOBILE_APP_BUILD = "640"
11
11
  OS_NAME = "ANDROID"
12
12
  OS_VERSION = "34"
13
13
  OS_STR = f"{OS_NAME} {OS_VERSION}"
@@ -208,7 +208,7 @@ class Device:
208
208
  async def set_bedtime_end_time(self, value: time):
209
209
  """Update the bedtime end time for the device."""
210
210
  _LOGGER.debug(">> Device.set_bedtime_end_time(value=%s)", value)
211
- if not time(5, 0) <= value <= time(9, 0):
211
+ if not time(5, 0) <= value <= time(9, 0) and value != time(0, 0):
212
212
  raise BedtimeOutOfRangeError(value=value)
213
213
  now = datetime.now()
214
214
  if self.timer_mode == DeviceTimerMode.DAILY:
@@ -219,10 +219,17 @@ class Device:
219
219
  regulation = self.parental_control_settings["playTimerRegulations"][
220
220
  "eachDayOfTheWeekRegulations"
221
221
  ][DAYS_OF_WEEK[now.weekday()]]
222
- regulation["bedtime"]["startingTime"] = {
223
- "hour": value.hour,
224
- "minute": value.minute,
222
+ new_bedtime_settings = {
223
+ **regulation["bedtime"],
224
+ "enabled": regulation["bedtime"]["endingTime"] or value != time(0, 0),
225
+ "startingTime": {
226
+ "hour": value.hour,
227
+ "minute": value.minute,
228
+ }
229
+ if value != time(0, 0)
230
+ else None,
225
231
  }
232
+ regulation["bedtime"] = new_bedtime_settings
226
233
  await self._send_api_update(
227
234
  self._api.async_update_play_timer,
228
235
  self.device_id,
@@ -328,7 +335,7 @@ class Device:
328
335
  _LOGGER.debug(">> Device.update_max_daily_playtime(minutes=%s)", minutes)
329
336
  if isinstance(minutes, float):
330
337
  minutes = int(minutes)
331
- if minutes > 360 or minutes < -1:
338
+ if not (-1 <= minutes <= 360):
332
339
  raise DailyPlaytimeOutOfRangeError(minutes)
333
340
  now = datetime.now()
334
341
  ttpiod = True
@@ -416,8 +423,12 @@ class Device:
416
423
  """Parse a parental control setting request response."""
417
424
  _LOGGER.debug(">> Device._parse_parental_control_setting()")
418
425
  self.parental_control_settings = pcs["parentalControlSetting"]
419
- self.parental_control_settings["playTimerRegulations"].pop("bedtimeStartingTime", None)
420
- self.parental_control_settings["playTimerRegulations"].pop("bedtimeEndingTime", None)
426
+ self.parental_control_settings["playTimerRegulations"].pop(
427
+ "bedtimeStartingTime", None
428
+ )
429
+ self.parental_control_settings["playTimerRegulations"].pop(
430
+ "bedtimeEndingTime", None
431
+ )
421
432
  self.forced_termination_mode = self.parental_control_settings[
422
433
  "playTimerRegulations"
423
434
  ]["restrictionMode"] == str(RestrictionMode.FORCED_TERMINATION)